HEAPS

PHP 8.4 property hooks: mniej getterów, czytelniejszy model domeny

Property hooks i asymmetric visibility w PHP 8.4. Jak pisać krótsze value objecty, kiedy zostawić klasyczne metody i czego unikać przy migracji ze starszego PHP.

Gettery nie znikają. Część z nich po prostu przestaje mieć sens.

PHP 8.4 wprowadziło property hooks i asymmetric visibility. Skraca klasy, w których od lat powtarzałeś to samo: prywatne pole, publiczny getter, setter z trim i walidacją.

Jeśli nadal jesteś na 8.3, te dwie rzeczy często wystarczają jako powód do upgrade’u.

Property hooks w skrócie

Hook podpinasz pod odczyt albo zapis właściwości. IDE i static analysis widzą to jako właściwość, nie jako parę magicznych metod schowanych w __get.

final class Email
{
    public string $value {
        set(string $value) {
            $normalized = strtolower(trim($value));
            if (!filter_var($normalized, FILTER_VALIDATE_EMAIL)) {
                throw new InvalidArgumentException('Invalid email');
            }
            $this->value = $normalized;
        }
    }

    public function __construct(string $value)
    {
        $this->value = $value;
    }
}

Normalizacja zostaje przy polu. Caller pisze $email->value, a reguły domeny nie uciekają do serwisu aplikacyjnego.

Asymmetric visibility bez boileplate’u

Chcesz, żeby właściwość była publicznie czytelna, ale zapisywalna tylko w klasie? W 8.4 nie musisz pisać gettera tylko po to.

final class Order
{
    public function __construct(
        public private(set) string $id,
        public private(set) string $status,
    ) {}

    public function markPaid(): void
    {
        $this->status = 'paid';
    }
}

Z zewnątrz czytasz $order->status. Zmiana statusu idzie przez metodę, która zna reguły przejść. Kod wygląda jak dane, a zachowanie zostaje domknięte.

Gdzie hooks pomagają, a gdzie przeszkadzają

Dobrze wchodzą w value objecty, DTO z lekką normalizacją i modele, w których getter nic nie robi poza zwrotem pola.

Gorzej, gdy:

  • logika w set woła bazę albo HTTP (trudno testować, trudno czytać),
  • właściwość jest tablicą i spodziewasz się pełnej semantyki referencji,
  • zespół jeszcze nie ma PHP 8.4 na wszystkich środowiskach, a polyfille i tak nie oddadzą składni języka.

Hook to nie miejsce na orchestration. To miejsce na niezmienniki i prostą transformację wartości.

Migracja bez big bang

Nie przepisuj całego modelu w weekend. Zacznij od nowych klas i od miejsc, gdzie getter/setter tylko szumią. W Symfony i Doctrine sprawdź, czy hydracja i proxy nie zakładają starego kształtu API. W serializacji (symfony/serializer, JMS) upewnij się, że public properties z hookami zachowują się tak, jak oczekujesz w testach.

W kolejnym value objectcie napisz mniej kodu i zostaw regułę bliżej danych. Reszta zwykle przychodzi sama.