¿Por qué de repente obtengo un error "No se debe acceder a la propiedad escrita antes de la inicialización" al introducir sugerencias de tipo de propiedades?

10

He actualizado mis definiciones de clase para hacer uso de las sugerencias de tipo de propiedad recién introducidas, como esta:

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

Pero cuando intento guardar mi entidad en Doctrine, recibo un error que dice:

No se debe acceder a la propiedad escrita antes de la inicialización

Esto no solo ocurre con $ido $createdAt, sino que también sucede con $valueo $updatedAt, que son propiedades que pueden ser anuladas.

yivi
fuente

Respuestas:

21

Dado que PHP 7.4 introduce sugerencias de tipo para propiedades, es particularmente importante proporcionar valores válidos para todas las propiedades, de modo que todas las propiedades tengan valores que coincidan con sus tipos declarados.

Una variable que nunca se ha asignado no tiene un nullvalor, pero está en un undefinedestado que nunca coincidirá con ningún tipo declarado . undefined !== null.

Para el código anterior, si hiciste:

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

Obtendrías:

Error fatal: Error no detectado: No se debe acceder a la propiedad escrita a máquina Foo :: $ val antes de la inicialización

Como $valno es stringni nullal acceder a él.

La forma de evitar esto es asignar valores a todas sus propiedades que coincidan con los tipos declarados. Puede hacerlo como valores predeterminados para la propiedad o durante la construcción, según su preferencia y el tipo de propiedad.

Por ejemplo, para lo anterior podría hacer:

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

Ahora todas las propiedades tendrían un valor válido y la instancia estaría en un estado válido.

Esto puede afectar particularmente cuando confía en valores que provienen de la base de datos para valores de entidad. Por ejemplo, ID generados automáticamente, o creación y / o valores actualizados; que a menudo se dejan como una preocupación de DB.

Para las ID generadas automáticamente, la forma recomendada es cambiar la declaración de tipo a ?int $id = null. Para todo lo demás, simplemente elija un valor apropiado para el tipo de propiedad.

yivi
fuente
-5

Mi error:

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

Mi solución: agregue el siguiente método a la clase:

public function __sleep()
{
    return [];
}
Lebnik
fuente
1
Lea la pregunta con precisión, luego responda según la pregunta, no totalmente según su problema / solución.
MAChitgarha