为什么我在引入属性类型提示时突然收到“初始化前不得访问类型化属性”错误?

2022-08-30 07:32:33

我已经更新了我的类定义,以利用新引入的属性类型提示,如下所示:

class Foo {

    private int $id;
    private ?string $val;
    private DateTimeInterface $createdAt;
    private ?DateTimeInterface $updatedAt;

    public function __construct(int $id) {
        $this->id = $id;
    }


    public function getId(): int { return $this->id; }
    public function getVal(): ?string { return $this->val; }
    public function getCreatedAt(): ?DateTimeInterface { return $this->createdAt; }
    public function getUpdatedAt(): ?DateTimeInterface { return $this->updatedAt; }

    public function setVal(?string $val) { $this->val = $val; }
    public function setCreatedAt(DateTimeInterface $date) { $this->createdAt = $date; }
    public function setUpdatedAt(DateTimeInterface $date) { $this->updatedAt = $date; }
}

但是,当试图在教义上保存我的实体时,我得到了一个错误,说:

初始化前不得访问类型化属性

这不仅发生在 或 上,也发生在 or 上,它们是可为空的属性。$id$createdAt$value$updatedAt


答案 1

由于 PHP 7.4 引入了属性的类型提示,因此为所有属性提供有效值尤为重要,以便所有属性都具有与其声明的类型匹配的值。

从未分配过的属性没有值,但它位于状态上,该状态永远不会与任何声明的类型匹配。.nullundefinedundefined !== null

对于上面的代码,如果您这样做了:

$f = new Foo(1);
$f->getVal();

您将获得:

致命错误:未捕获错误:键入属性 Foo::$val初始化前不得访问

因为 既不是 也不是访问它时。$valstringnull

解决此问题的方法是将值分配给与声明类型匹配的所有属性。您可以将其作为属性的默认值或在构造期间执行此操作,具体取决于您的首选项和属性的类型。

例如,对于上述内容,可以执行以下操作:

class Foo {

    private int $id;
    private ?string $val = null; // <-- declaring default null value for the property
    private Collection $collection;
    private DateTimeInterface $createdAt;
    private ?DateTimeInterface $updatedAt;

    public function __construct(int $id) {
        // and on the constructor we set the default values for all the other 
        // properties, so now the instance is on a valid state
        $this->id = $id;
        $this->createdAt = new DateTimeImmutable();
        $this->updatedAt = new DateTimeImmutable();

        $this->collection = new ArrayCollection();
    }

现在,所有属性都将具有有效值,并且实例将处于有效状态。

当您依赖来自 DB 的值作为实体值时,这种情况可能会特别频繁。例如,自动生成的 ID,或创建和/或更新的值;这通常被保留为数据库关注点。

对于自动生成的 ID,建议的方法是将类型声明更改为:

private ?int $id = null

对于所有其他内容,只需为属性类型选择适当的值即可。


答案 2

对于可为 null 的类型化属性,您需要使用语法

private ?string $val = null;

否则,它会引发致命错误。

由于这个概念会导致不必要的致命错误,因此我创建了一个 https://bugs.php.net/bug.php?id=79620 的错误报告 - 没有成功,但至少我尝试过......


推荐