为什么我在引入属性类型提示时,突然得到一个"类型属性在初始化前不得访问"的错误?

回答 2 浏览 12.4万 2019-12-10

我已经更新了我的类定义,以利用新引入的属性类型提示,就像这样。

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; }
}

但是,当我试图在Doctrine上保存我的实体时,我得到一个错误,说。

在初始化之前,不得访问该类型的属性

这不仅发生在$id$createdAt上,也发生在$value$updatedAt上,它们都是可忽略不计的属性。

yivi 提问于2019-12-10
2 个回答
#1楼 已采纳
得票数 191

由于 PHP 7.4 引入了属性的类型提示,为所有的属性提供有效的值尤为重要,这样所有的属性的值都与它们所声明的类型相匹配。

一个从未被分配的属性没有null值,但它处于undefined状态,它永远不会与任何声明的类型相匹配undefined !== null.

对于上面的代码,如果你做到了,那么。

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

你会得到。

致命错误。未发现的错误。类型化的属性Foo::$val在初始化前不能被访问。

因为$val在访问时既不是string也不是null

解决这个问题的方法是为你的所有属性分配符合声明类型的值。你可以作为属性的默认值或在构造过程中这样做,这取决于你的偏好和属性的类型。

例如,对于上述情况,人们可以做的是。

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();
    }

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

当你依赖来自DB的实体值时,这种情况会特别多。例如,自动生成的ID,或创建和/或更新的值;这些值通常被留作DB关注。

对于自动生成的ID,推荐的前进方式是将类型声明改为。

private ?int $id = null

其余的,只需为属性的类型选择一个适当的值即可。

yivi 提问于2019-12-10
yivi 修改于2021-04-08
换句话说,从PHP7.4开始,类型提示的类成员就有了空的安全James Bond 2020-10-23
如果需要的话,你也可以使用isset()来检查属性是否被安全初始化,以避免初始化,例如用null来初始化。Max 2022-06-22
#2楼
得票数 32

对于可归零类型的属性,你需要使用语法

private ?string $val = null;

否则它就会抛出一个致命的错误。

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

Hhyperion 提问于2020-05-22
a) 这在另一个答案中已经涉及。 b) 这不是一个错误。那是(好的)设计。如果一个属性除了定义的类型外还可能持有null,你要明确说明这一点。我希望这样的报告会被立即拒绝。yivi 2020-05-22