PHP 7.4+ 中的“初始化之前不得访问类型化属性”错误

Mik*_*lov 7 php soap-client doctrine-orm php-7.4

我正在使用 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 ...
Run Code Online (Sandbox Code Playgroud)

我可以通过将属性声明为可为空并默认设置空值来缓解此问题。

<?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
Run Code Online (Sandbox Code Playgroud)

但是,我不喜欢这种解决方法。我的类的重点是符合域要求并将域约束封装在类设计中。在这种情况下,该属性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
Run Code Online (Sandbox Code Playgroud)

我的问题是:有没有办法保持正确的类设计并避免面临错误Typed property must not be accessed before initialization?如果不是,解决这个问题的首选方法是什么?(例如,将所有属性定义为可为 null 的空值或将字符串属性定义为空字符串等)

Fat*_*ror 9

我认为这里有一个误会。未初始化的类型化属性没有状态,这意味着它们没有初始值NULL。如果您想要一个属性,则NULL必须明确指定它。

private ?string $name = NULL;
Run Code Online (Sandbox Code Playgroud)

因此,您尝试在不设置这些属性的情况下避免此异常是不正确的,也是没有意义的!类型化属性的目的是避免隐式初始化并始终提供清晰且有意义的显式值。并且请不要只是将所有属性定义为可为空来使此异常消失!因为这将破坏类型化属性和 PHP 7.4 的全部意义。