Я использую PHP 7.4 и подсказки типа свойства.
Скажем, у меня есть класс А с парой частных владений. Когда я использую \SoapClient, Doctrine ORM или любой другой инструмент, который создает экземпляр класса в обход конструктора и получает/устанавливает свойства напрямую с помощью отражения, я сталкиваюсь с ошибкой PHP Fatal error: Uncaught Error: Typed property A::$id must not be accessed before initialization in
.
<?php
declare(strict_types=1);
class A
{
private int $id;
private string $name;
public function __construct(int $id, string $name)
{
$this->id = $id;
$this->name = $name;
}
public function getId(): int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
}
$a = (new \ReflectionClass(A::class))->newInstanceWithoutConstructor();
var_dump($a->getId()); // Fatal error: Uncaught Error: Typed property A::$id must not be accessed before initialization in ...
Я могу смягчить эту проблему, объявив свойства как обнуляемые и установив нулевое значение по умолчанию.
<?php
declare(strict_types=1);
class A
{
private ?int $id = null;
private ?string $name = null;
public function __construct(?int $id, ?string $name)
{
$this->id = $id;
$this->name = $name;
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
}
$a = (new \ReflectionClass(A::class))->newInstanceWithoutConstructor();
var_dump($a->getId()); // NULL
var_dump($a->getName()); // NULL
Однако мне не нравится этот обходной путь. Смысл моего класса в том, чтобы быть совместимым с предметной областью и инкапсулировать ограничения предметной области в структуру класса. В этом случае свойство name
не должно принимать значение NULL. Потенциально я могу объявить свойство name
пустой строкой, но это тоже не похоже на чистое решение.
<?php
declare(strict_types=1);
class A
{
private ?int $id = null;
private string $name = '';
public function __construct(?int $id, string $name)
{
$this->id = $id;
$this->name = $name;
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
}
$a = (new \ReflectionClass(A::class))->newInstanceWithoutConstructor();
var_dump($a->getId()); // NULL
var_dump($a->getName()); // ''
$idProperty = new \ReflectionProperty($a, 'id');
$idProperty->setAccessible(true);
if (null === $idProperty->getValue($a)) {
$idProperty->setValue($a, 1001);
}
$nameProperty = new \ReflectionProperty($a, 'name');
$nameProperty->setAccessible(true);
if ('' === $nameProperty->getValue($a)) {
$nameProperty->setValue($a, 'Name');
}
var_dump($a->getId()); // 1001
var_dump($a->getName()); // Name
Мой вопрос: есть ли способ сохранить правильный дизайн класса и избежать ошибки Typed property must not be accessed before initialization
? Если нет, то какой предпочтительный подход к решению этой проблемы? (например, определить все свойства как пустые значения NULL или строковые свойства как пустую строку и т. д.)
private int $id = 0;
. Если вы этого не сделаете, начальное значение равно NULL. - person Markus Zeller   schedule 28.12.2020getId(): int
, вы ожидаете int, но когда это NULL, конечно, это неправильно. Итак, вам нужно вернуть int и проверить себя, какreturn (int)$this->id;
. Затем NULL будет преобразован в 0. Или измените подпись наgetId(): ?int
. - person Markus Zeller   schedule 28.12.2020