Menu
Coddy logo textTech

Późne wiązanie statyczne

Część sekcji Programowanie obiektowe ścieżki PHP w Coddy. Lekcja 36 z 91.

Gdy używasz self:: w klasie nadrzędnej, zawsze odnosi się ono do klasy, w której zostało zapisane, a nie do klasy, która jest faktycznie używana. Może to powodować nieoczekiwane zachowanie w dziedziczeniu. Późne wiązanie statyczne rozwiązuje ten problem za pomocą słowa kluczowego static::.

Rozważmy ten przykład, w którym self:: nie działa zgodnie z oczekiwaniami:

<?php
class ParentClass {
    public static function create() {
        return new self();
    }
    
    public static function getClassName() {
        return self::class;
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();

Wynik:

ParentClass
ParentClass

Mimo że wywołaliśmy ChildClass::create(), otrzymaliśmy obiekt ParentClass, ponieważ self:: jest rozstrzygane podczas kompilacji. Teraz użyjmy zamiast tego static:::

<?php
class ParentClass {
    public static function create() {
        return new static();
    }
    
    public static function getClassName() {
        return static::class;
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();

Wynik:

ChildClass
ChildClass

W przypadku static:: PHP czeka do czasu wykonania, aby określić, której klasy użyć. Nazywa się to „późnym” wiązaniem, ponieważ decyzja jest opóźniona. Jest to niezbędne w przypadku metod fabrykujących i każdej metody statycznej, która powinna uwzględniać dziedziczenie.

Kluczowa kwestia: Używaj static:: zamiast self::, gdy chcesz, aby klasy potomne poprawnie dziedziczyły i dostosowywały zachowanie statyczne.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system rejestru modeli, który pokazuje możliwości późnego wiązania statycznego. Utworzysz bazową klasę Model z metodą fabrykującą, którą mogą dziedziczyć klasy potomne, a dzięki static:: każda klasa potomna będzie poprawnie tworzyć swoje własne instancje, a nie instancje klasy nadrzędnej.

Rozmieścisz kod w czterech plikach:

  • Model.php: Utwórz bazową klasę Model z chronioną właściwością statyczną $tableName ustawioną na "models". Dodaj metodę statyczną create(), która używa new static(), aby zwrócić nową instancję dowolnej klasy, która ją wywołała. Dodaj kolejną metodę statyczną getTable(), która zwraca static::$tableName. Na koniec dodaj metodę describe(), która zwraca "[ClassName] from table [tableName]", używając static::class i static::$tableName.
  • User.php: Utwórz klasę User, która rozszerza Model. Dołącz plik klasy nadrzędnej. Nadpisz statyczną właściwość $tableName, ustawiając ją na "users". Klasa dziedziczy wszystkie metody z Model bez żadnych zmian.
  • Product.php: Utwórz klasę Product, która rozszerza Model. Dołącz plik klasy nadrzędnej. Nadpisz statyczną właściwość $tableName, ustawiając ją na "products". Klasa również dziedziczy wszystkie metody z klasy nadrzędnej.
  • main.php: Dołącz pliki klas User i Product. Użyj odziedziczonej metody fabrykującej create(), aby utworzyć instancje zarówno User, jak i Product. Dla każdej instancji wywołaj describe() i wyświetl wynik w osobnym wierszu. Następnie wyświetl nazwę tabeli dla każdej klasy, używając User::getTable() i Product::getTable(), każdą w osobnym wierszu.

Cała magia polega na tym, że metody create() i getTable() są zdefiniowane wyłącznie w nadrzędnej klasie Model, a mimo to poprawnie działają z klasami potomnymi, ponieważ static:: opóźnia wiązanie do czasu wykonania programu. Gdy wywołasz User::create(), PHP utworzy instancję klasy User, a nie Model, ponieważ static:: odnosi się do klasy, która faktycznie została wywołana.

Wynik powinien zawierać cztery wiersze: opis instancji User, opis instancji Product, nazwę tabeli User oraz nazwę tabeli Product.

Spróbuj swoich sił

<?php

require_once 'User.php';
require_once 'Product.php';

// TODO: Użyj odziedziczonej metody fabrykującej create(), aby utworzyć instancję User

// TODO: Użyj odziedziczonej metody fabrykującej create(), aby utworzyć instancję Product

// TODO: Wywołaj describe() na instancji User i wypisz wynik

// TODO: Wywołaj describe() na instancji Product i wypisz wynik

// TODO: Wypisz wynik User::getTable()

// TODO: Wypisz wynik Product::getTable()

?>
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator PHP online