"Typed property must not be accessed before initialization" PHP 7.4+ 中的错误
"Typed property must not be accessed before initialization" error in PHP 7.4+
我正在使用 PHP 7.4 和 属性 类型提示。
假设我有一个 class A,有几个私有属性。当我使用 \SoapClient、Doctrine ORM 或任何实例化 class 绕过构造函数和直接使用反射 getting/setting 属性的工具时,我遇到错误 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 ...
我可以通过将属性声明为可为 null 并默认设置 null 值来缓解此问题。
<?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
但是,我不喜欢这种解决方法。我的 class 的要点是要符合领域并将领域约束封装在 class 设计中。在这种情况下,属性 name
不应为空。可能我可以将 属性 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
我的问题是:有没有办法保持适当的class设计并避免面临Typed property must not be accessed before initialization
错误?如果否,解决此问题的首选方法是什么? (例如,将所有属性定义为可为 null 的空值或将字符串属性定义为空字符串等)
让我们简化事情并阐明基于反射的 ORM(例如 Doctrine)是如何工作的。特别是,Doctrine 仅在从数据库加载对象时使用反射。所以 id 将始终被设置。考虑:
class Entity
{
public int $id;
}
$entity = (new \ReflectionClass(Entity::class))->newInstanceWithoutConstructor();
$idProperty = new \ReflectionProperty($entity, 'id');
$idProperty->setValue($entity, 1001);
echo 'ID ' . $entity->id . "\n";
上面的例子没有错误信息。除非您还计划设置所有属性,否则您不会使用反射来创建没有构造函数的 class。
此时,唯一的问题是您希望 'properly designed' class 做什么:
$entity = new Entity();
echo 'ID ' . $entity->id . "\n";
如果认为在对象被持久化之前 id 为 null 是可以的,那么你可以走 ?int 路线。如果没有,那么可以考虑使用 guid 而不是序列。
我觉得这里有个误会。未初始化的类型属性没有状态,这意味着它们没有初始 NULL
。如果您希望 属性 成为 NULL
,您必须明确指定它。
private ?string $name = NULL;
因此,您试图在不设置这些属性的情况下避免此异常是不正确的,没有任何意义!。类型化属性的目的是避免隐式初始化,并始终提供清晰且有意义的显式值。并且请不要仅仅将所有属性定义为可为空以使此异常消失!!因为这将破坏类型属性和 PHP 7.4 .
的全部意义
我正在使用 PHP 7.4 和 属性 类型提示。
假设我有一个 class A,有几个私有属性。当我使用 \SoapClient、Doctrine ORM 或任何实例化 class 绕过构造函数和直接使用反射 getting/setting 属性的工具时,我遇到错误 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 ...
我可以通过将属性声明为可为 null 并默认设置 null 值来缓解此问题。
<?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
但是,我不喜欢这种解决方法。我的 class 的要点是要符合领域并将领域约束封装在 class 设计中。在这种情况下,属性 name
不应为空。可能我可以将 属性 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
我的问题是:有没有办法保持适当的class设计并避免面临Typed property must not be accessed before initialization
错误?如果否,解决此问题的首选方法是什么? (例如,将所有属性定义为可为 null 的空值或将字符串属性定义为空字符串等)
让我们简化事情并阐明基于反射的 ORM(例如 Doctrine)是如何工作的。特别是,Doctrine 仅在从数据库加载对象时使用反射。所以 id 将始终被设置。考虑:
class Entity
{
public int $id;
}
$entity = (new \ReflectionClass(Entity::class))->newInstanceWithoutConstructor();
$idProperty = new \ReflectionProperty($entity, 'id');
$idProperty->setValue($entity, 1001);
echo 'ID ' . $entity->id . "\n";
上面的例子没有错误信息。除非您还计划设置所有属性,否则您不会使用反射来创建没有构造函数的 class。
此时,唯一的问题是您希望 'properly designed' class 做什么:
$entity = new Entity();
echo 'ID ' . $entity->id . "\n";
如果认为在对象被持久化之前 id 为 null 是可以的,那么你可以走 ?int 路线。如果没有,那么可以考虑使用 guid 而不是序列。
我觉得这里有个误会。未初始化的类型属性没有状态,这意味着它们没有初始 NULL
。如果您希望 属性 成为 NULL
,您必须明确指定它。
private ?string $name = NULL;
因此,您试图在不设置这些属性的情况下避免此异常是不正确的,没有任何意义!。类型化属性的目的是避免隐式初始化,并始终提供清晰且有意义的显式值。并且请不要仅仅将所有属性定义为可为空以使此异常消失!!因为这将破坏类型属性和 PHP 7.4 .
的全部意义