Dlaczego nagle pojawia się błąd „Nie można uzyskać dostępu do właściwości typu przed zainicjowaniem” podczas wprowadzania wskazówek dotyczących typu właściwości?

10

Zaktualizowałem definicje klas, aby korzystać z nowo wprowadzonych wskazówek typu właściwości, takich jak:

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

Ale podczas próby zapisania mojego bytu w Doctrine pojawia się błąd:

Nie można uzyskać dostępu do wpisanej właściwości przed inicjalizacją

Dzieje się tak nie tylko z $idlub $createdAt, ale także z $valuelub $updatedAt, które są właściwościami zerowalnymi.

yivi
źródło

Odpowiedzi:

20

Ponieważ PHP 7.4 wprowadza podpowiedzi do właściwości, szczególnie ważne jest podanie prawidłowych wartości dla wszystkich właściwości, aby wszystkie właściwości miały wartości pasujące do zadeklarowanych typów.

Zmienna, która nigdy nie została przypisana, nie ma nullwartości, ale znajduje się w undefinedstanie, który nigdy nie będzie pasował do żadnego zadeklarowanego typu . undefined !== null.

Dla powyższego kodu, jeśli zrobiłeś:

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

Dostałbyś:

Błąd krytyczny: Nieprzechwycony błąd: Wpisana właściwość Foo :: $ val nie może być dostępna przed inicjalizacją

Ponieważ $valnie ma stringani nulldostępu do niego.

Aby obejść ten problem, należy przypisać wartości do wszystkich właściwości, które pasują do zadeklarowanych typów. Możesz to zrobić jako wartości domyślne dla nieruchomości lub podczas budowy, w zależności od preferencji i typu nieruchomości.

Na przykład w powyższym przypadku możesz:

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

Teraz wszystkie właściwości będą miały poprawną wartość, a instancja będzie w prawidłowym stanie.

Może to uderzyć szczególnie często, gdy polegasz na wartościach, które pochodzą z DB dla wartości encji. Np. Automatycznie wygenerowane identyfikatory lub wartości utworzone i / lub zaktualizowane; które często są pozostawione jako problem DB.

W przypadku automatycznie generowanych identyfikatorów zalecanym krokiem naprzód jest zmiana deklaracji typu na ?int $id = null. Dla całej reszty wystarczy wybrać odpowiednią wartość dla typu nieruchomości.

yivi
źródło
-5

Mój błąd:

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

Moje rozwiązanie - dodaj następną metodę do klasy:

public function __sleep()
{
    return [];
}
Lebnik
źródło
1
Przeczytaj dokładnie pytanie, a następnie odpowiedz na podstawie pytania, a nie całkowicie na podstawie twojego problemu / rozwiązania.
MAChitgarha