【发布时间】:2020-12-28 16:36:21
【问题描述】:
我正在使用 PHP 7.4 和属性类型提示。
假设我有一个 A 类,有几个私有属性。当我使用 \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 不应为空。可能我可以将属性 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。 -
但是从域的角度来看,它会使类状态无效,因为 0 不是正确的 ID。看起来很hacky。
-
我认为有一个 int 能够为 NULL 更加hacky并且违反类型安全性,即使在我看来这是可能的。
-
是的,离理想还很远。我不喜欢默认设置为可空或 0。我希望有一个干净的解决方案。
-
当您访问
getId(): int时,您期望一个int,但是当它为NULL 时,这当然是不对的。因此,您需要返回一个 int,并像return (int)$this->id;一样自行检查。然后 NULL 将被强制转换为 0。或者将签名更改为getId(): ?int。
标签: php doctrine-orm soap-client php-7.4