Por que de repente estou recebendo o erro "Propriedade digitada não deve ser acessada antes da inicialização" ao introduzir dicas de tipo de propriedade?

10

Atualizei minhas definições de classe para usar as dicas de tipo de propriedade recém-introduzidas, assim:

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

Mas, ao tentar salvar minha entidade no Doctrine, estou recebendo um erro dizendo:

A propriedade digitada não deve ser acessada antes da inicialização

Isso não acontece apenas com $idou $createdAt, mas também acontece com $valueou $updatedAt, que são propriedades anuláveis.

yivi
fonte

Respostas:

20

Como o PHP 7.4 introduz dicas de tipo para propriedades, é particularmente importante fornecer valores válidos para todas as propriedades, para que todas as propriedades tenham valores que correspondam aos tipos declarados.

Uma variável que nunca foi atribuída não tem um nullvalor, mas está em um undefinedestado, que nunca corresponderá a nenhum tipo declarado . undefined !== null.

Para o código acima, se você fez:

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

Você obteria:

Erro fatal: Erro não capturado: Propriedade digitada Foo :: $ val não deve ser acessada antes da inicialização

Uma vez que $valnão éstring nem nullao acessá-lo.

A maneira de contornar isso é atribuir valores a todas as suas propriedades que correspondem aos tipos declarados. Você pode fazer isso como valores padrão da propriedade ou durante a construção, dependendo da sua preferência e do tipo da propriedade.

Por exemplo, para o acima, você pode fazer:

class Foo {

    private int $id;
    private ?string $val = null; // <-- declaring default null value for the property
    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();
    }

Agora todas as propriedades teriam um valor válido e a instância estaria em um estado válido.

Isso pode ocorrer particularmente quando você conta com valores provenientes do banco de dados para valores de entidade. Por exemplo, IDs gerados automaticamente ou criação e / ou valores atualizados; que geralmente são deixados como uma preocupação do DB.

Para IDs gerados automaticamente, o caminho recomendado a seguir é alterar a declaração de tipo para ?int $id = null. Para todo o resto, basta escolher um valor apropriado para o tipo de propriedade.

yivi
fonte
-5

Meu erro:

"Typed property Proxies\\__CG__\\App\\Entity\\Organization::$ must not be accessed before initialization (in __sleep)"

Minha solução - adicione o próximo método à classe:

public function __sleep()
{
    return [];
}
Lebnik
fonte
11
Por favor, leia a pergunta com precisão e responda com base na pergunta, não totalmente com base no seu problema / solução.
MAChitgarha