Gettery i settery czyli Property hooks w PHP 8.4

Dowiedz się jak działają property hooks czyli gettery i settery w PHP 8.4 wraz z praktycznymi przykładami

Wstęp

Osobiście z jedną rzeczy jakich mi brakowało w PHP był brak tzw. „Property hooks”, na szczęście to się zmieniło. Dzięki nowemu rozwiązaniu będzie przyjdzie łatwiej programować, a sam PHP staje się nieco bardziej nowoczesny. Oczywiście wiele drogi przed nim, ale kroki są czynione i jest coraz lepiej

Czym są Property hooks

Są to m.in. gettery i settery, chociaż niewykluczone że powstanie ich więcej. Takie hooki występują zawsze przy własności i są to po prostu metody. Hook Get – zawsze gdy pobierasz (zaczytujesz) wartość własności, a hook Set – zawsze gdy ustawiasz wartość własności i w tym artykule skupimy się tylko na tych dwóch.

Przykłady

Podstawy

Przed czasami PHP 8.4 gettery i settery w PHP trzeba było tworzyć jako osobne metody, które nie były hookami i pamiętać o tym, że istnieją i pilnować, aby były zawsze wykorzystywane. Tworzone były przez to prywatne własności, aby wymusić używanie tych metod.

class Rectangle
{
  private int $x;
  private int $y;

  public function setX(int $x): self
  {
    $this->x = $x;
    return $this;
  }

  public function getX(): int
  {
    return $this->x;
  }

  public function setY(int $y): self
  {
    $this->y = $y;
    return $this;
  }

  public function getY(): int
  {
    return $this->y;
  }
}

Oczywiście dla powyższego przykładu można pokusić się o wywalenie setterów i użyć konstruktora do przypisania wartości, ale załóżmy że chcemy mieć możliwość modyfikowania X i Y w locie.

Też do powyższego przykładu własności mogły by być publiczne ponieważ gettery i settery nie wykonują żadnej logiki, ale załóżmy, że będą, a nawet samemu można pokusić się o modyfikację tych metod.

Więc: mamy trochę długą klasę, która przechowuje wymiary prostokąta: klasa umożliwia ustawienie, zmianę i pobranie długości boków.

Napiszmy teraz to samo za pomocą Property Hooks

class Rectangle
{
  public int $x
  {
  	get => $this->x;
  	set(int $x) => $this->x = $x;
  }
  public int $y
  {
  	get => $this->y;
  	set(int $y) => $this->y = $y;
  }
}

Też zrobił nam się porządek i też pójdzie nam szybko znaleźć logikę danych hooków.

Oczywiście powyższe wykorzystanie wygląda na mega błahe, ale też daję prosty przykład, aby łatwiej załapać o co chodzi.

Prawie bym zapomniał: powyższe hooki wykorzystuje się po prostu używając własności:

$r = new Rectangle;
$r->x = 2;
$r->y = 3;

var_dump($r);

Wypluło mi taki wynik:

object(Rectangle)#1 (2) {
["x"]=>
int(2)
["y"]=>
int(3)
}

No to co? Gotów na bardziej poważne przykłady?

Logika w getterach i setterach

Jak wcześniej użyliśmy prostych metod strzałkowych do ustawienia hooków, tak teraz użyjemy nieco więcej logiki.

Oczywiście należy traktować hooki get i set jako metody, więc w zasadzie da się w nich kodzić jak w każdej innej metodzie. Pomęczmy jeszcze nasz prostokąt i dodajmy co nie co smaczków.

Z tego co udało mi się zapamiętać ze szkoły to prostokąty jako długość boku przyjmują liczbę dodatnią niezerową. Settery x i y mogą to sprawdzać i rzucać wyjątek w razie niepoprawnej wartości:

class Rectangle
{
  public int $x
  {
  	get => $this->x;
  	set(int $x) {
        if($x <= 0) {
            throw new Exception("Side length x must be greater than 0");
        }
        $this->x = $x;
    }
  }
  public int $y
  {
  	get => $this->y;
  	set(int $y) {
        if($y <= 0) {
            throw new Exception("Side length y must be greater than 0");
        }
        $this->y = $y;
    }
  }
}

$r = new Rectangle;
$r->x = -2;
$r->y = 3;

var_dump($r);

Proszę zauważyć że jako X ustawiłem celowo -2, aby sprawdzić działanie kodu. Oczywiście zadziałał poprawnie i zwrócił oto wyjątek:

Side length x must be greater than 0 in /app/tmp.php:10

Tak oto w locie mamy walidację własności ogarniętą w dosyć banalny sposób.

Jeszcze pasowałoby ogarnąć inne własności prostokąta. Bo tak że on ma tylko X i Y mało daje. Może by tak dodać jego pole?

Jak wiadomo z podstawówki, pole prostokąta liczy się X * Y. Znając te zaawansowane techniki liczenia spróbujmy je wdrożyć za pomocą gettera. Niech pole liczy się w locie przy pobieraniu własności. Co ciekawsze, ta własność nie będzie posiadała settera co uczyni ją Virtual Property (wirtualną własnością).

Tak będzie wyglądał kod z dodanym polem prostokąta:

class Rectangle
{
  public int $x
  {
  	get => $this->x;
  	set(int $x) {
        if($x <= 0) {
            throw new Exception("Side length x must be greater than 0");
        }
        $this->x = $x;
    }
  }
  public int $y
  {
  	get => $this->y;
  	set(int $y) {
        if($y <= 0) {
            throw new Exception("Side length y must be greater than 0");
        }
        $this->y = $y;
    }
  }

  public int $area
  {
    get => $this->x * $this->y;
  }
}

$r = new Rectangle;
$r->x = 8;
$r->y = 3;

var_dump($r->area);

Oczywiście powyższy kod wykazał wynik = 24.

Własność area jest wyliczana za każdym razem, gdy pobieramy jej wartość, zatem próba ręcznego nadpisania np.

$r->area = 33;

nie powiedzie się. Błąd może nam na twarz nie wyskoczy, ale wynik zostanie na nowo przeliczony – to jest dobre zabezpieczenie przed niepożądanym nadpisaniem wartości własności.

Co dalej?

Dokumentacja na temat tego jak działają Property Hooks czyli póki co gettery i settery jest bardziej rozbudowana niż o to co dzisiaj przedstawiłem. Łaknących większej wiedzy zapraszam na https://www.php.net/manual/en/language.oop5.property-hooks.php gdzie m.in. opisane jest jak zadziałają hooki w przypadku dziedziczenia i jakie modyfikatory można używać przy nich.

Na koniec

Dziękuję za przeczytanie artykułu i mam nadzieję że spodobał się. W przyszłości planuję nadal opisywać nowości ze świata PHP, więc zachęcam do śledzenia bloga. Jeśli macie pomysły lub uwagi co do treści, prosiłbym o kontakt przez Discord – link do serwera znajduje się w nawigacji

Subscribe
Powiadom o
guest
0 komentarzy
najstarszy
najnowszy oceniany
Inline Feedbacks
View all comments
Włączyć powiadomienia? Bardzo pragnę Dam sobie radę