Metody magiczne w PHP

Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 3460 razy

Metody magiczne w PHP są specjalnymi metodami wywoływanymi automatycznie przez interpreter w określonych sytuacjach. Nazwy wszystkich metod magicznych zaczynają się od podwójnego znaku dolnej spacji "__", z czego wynika, że znane powinny być ci dwie pierwsze metody magiczne: __construct (konstruktor) i __destruct. Konstruktor jest oczywiście wywoływany przez interpreter przy tworzeniu nowego obiektu klasy, zaś destruktor, gdy klasa jest niszczona.

Metody magiczne __get, __set, __isset i __unset.

Pierwsze dwie metody __get i __set słusznie mogą się kojarzyć z ustawianiem pól klasy, natomiast __isset służy do sprawdzania, czy dana zmienna została ustawiona a __unset do zwalniania pamięci przypisanej do danej zmiennej. Co ciekawe dane pole klasy tak naprawdę może być zawarte w jednej zmiennej tablicowej typu array. Oto przykład:

<?php class Settings{ private $settings; // mogiczna metoda __construct - wywoływana przy tworzeniu obiektu public function __construct(){ echo("<p>Konstruktor wywołany</p>"); } // magiczna metoda get - pobierająca wartości z tablicy asocjacyjnej $this->settings public function __get($name){ if(isset($this->settings[$name])){ return $this->settings[$name]; } return Null; } // magiczna metoda set - ustawiająca wartości w tablicy asocjacyjnej $this->settings public function __set($name, $value){ $this->settings[$name] = $value; } // magiczna metoda isset - sprawdzająca, czy w tablicy asocjacyjnej $this->settings jest już taki element zainicjowany public function __isset($name){ if(array_key_exists($name, $this->settings)){ if(isset($this->settings[$name])){ return true; } } return false; } // magiczna metoda unset - zwalniająca pamięć przydzieloną w tablicy asocjacyjnej $this->settings pod indeksem $name public function __unset($name){ if(array_key_exists($name, $this->settings)){ echo("<p>usuwam</p>"); unset($this->settings[$name]); } } public function writeAll(){ var_dump($this->settings); } // magiczna metoda __dstruct - destruktor (wywoływany, gdy obiekt jest usuwany) public function __destruct(){ echo("<p>Destruktor wywołany, obiekt jest usuwany</p>"); } } $settings = new Settings; $settings->description = "Opis"; // tutaj wywoływana jest metoda magiczna __set ustawiająca/dodająca nową komórkę do wewnętrznej tablicy ascocjacyjnej obiekut $settings klasy Settings echo("<p>" . $settings->description . "</p>"); // tutaj pobieram wartość z komówki tablicy asocjacyjnej obietku $settings klasy Settings za pomocą metody magicznej __get if(isset($settings->date)){ echo("Data ustawiona: " . $settings->date); }else{ echo("Data nie została ustawiona"); } $settings->writeAll(); // wypisuję dane z tablicy obiektu $settings klasy Settings unset($settings->description); // usuwam komórkę z tablicy obiektu $settings klasy Settings $settings->writeAll(); unset($settings); // niszczenie obiektu ?>

Wynik powyższego kodu:

Konstruktor wywołany

Opis

Data nie została ustawiona
array (size=1)
  'description' => string 'Opis' (length=4)

usuwam

array (size=0)
  empty

Destruktor wywołany, obiekt jest usuwany

Metoda magiczna __toString

Ta metoda jest wywoływana automatycznie np. gdy dwa obiekty są łączone w tekst za pomocą operatora . (kropki). Oto przykład implementacji tej metody dla wcześniej utworzonej klasy Settings:

public function __toString(){ $settings_str = "<pre>"; foreach($this->settings as $key => $value){ $settings_str .= $key . ": " . $value . "n"; } $settings_str = trim($settings_str, "n"); $settings_str .= "</pre>"; return $settings_str; }

Wywołanie powyższej metody magicznej na obiekcie klasy Settings wygląda następująco:

<?php echo($settings); // $settings - to obiekt klasy Settings ?>

Wynik działania:

description: Opis

Metoda magiczna __invoke

Ta metoda umożliwia wywołanie obiektu klasy jak funkcji. Oto przykład obsłużenia tej metody dla wcześniej utworzonej klasy Settings:

public function __invoke($name){ if(array_key_exists($name, $this->settings)){ var_dump($this->settings[$name]); } }

Wywołanie powyższej metody na obiekcie klasy Settings będzie wyglądało tak:

<?php $settings("description"); ?>

Można sprawdzić, czy dany obiekt funkcji obsługuje tego typu wywołania za pomocą funkcji is_callable.

Wynikiem czego będzie:

string 'Opis' (length=4)

Metoda magiczna __call i __callStatic

Obie te metody obsługują wywołanie nieistniejącej metody klasy. Jedyną różnicą jest to, że pierwsza dotyczy metod obiektu klasy, natomiast druga metod statycznych klasy. oto przykład obsługi tych magicznych metod dla wyżej utworzonej klasy Settings:

public function __call($method, $args){ echo("<p>Metoda $this->" . $method . "(" . implode(', ', $args) . ")</p>"); } public function __callStatic($method, $args){ echo("<p>Metoda $this->" . $method . "(" . implode(', ', $args) . ")</p>"); }

Wywołanie metody __call:

<?php $settings->metoda("argument1"); $settings->metoda("argument1", "argument2"); ?>

Wynik działania powyższego kodu:

Metoda $this->metoda(argument1)

Metoda $this->metoda(argument1, argument2)

Wywołanie metody __callStatic:

<?php Settings::metodaStatyczna("argument1"); Settings::metodaStatyczna("argument1", "argument2"); ?>

Wynik działania powyższego kodu:

Metoda $this->metodaStatyczna(argument1)

Metoda $this->metodaStatyczna(argument1, argument2)

Metoda magiczna __clone

Ponieważ w PHP wszystkie obiekty klas są typami referencyjnymi, zachodzi konieczność sklonowania zawartości klasy. Do tego celu służy słowo kluczowe clone, którego użycie spowoduje wywołanie metody magicznej __clone, ale tylko wtedy, gdy metoda ta zostanie obsłużona. Obsługi tej metody magicznej wymagają jedynie te klasy, które będą przechowywały wewnątrz obiekty innych klas, ponieważ domyślnie (gdy metoda __clone nie jest obsłużona) kopiowane są wartości pól, które w przypadku obiektów klas są wskaźnikami. Oto przykład implementacji:

<?php class Color{ public $red, $green, $blue; public function __construct($red, $green, $blue){ $this->red = $red; $this->green = $green; $this->blue = $blue; } public function __toString(){ return "RGB(" . $this->red ."; " . $this->green . "; " . $this->blue . ")"; } } class Car{ public $color, $mark, $engine_description; public function __construct($color, $mark, $engine_description){ $this->color = $color; $this->mark = $mark; $this->engine_description = $engine_description; } public function __toString(){ return "<p>Marka samochodu: " . $this->mark . "; kolor nadwozia: " . $this->color . "; opis silnika: " . $this->engine_description . "</p>"; } public function __clone(){ $this->color = clone $this->color; } } $mercedes = new Car(new Color(0,0,0), "Mercedes", "2.0 l 8-cylindrowy rzędowy"); $mercedes2 = clone $mercedes; $mercedes2->color->red = 100; echo("<p>Pierwszy samochód:</p>" . $mercedes); echo("<p>Drugi samochód samochód:</p>" . $mercedes2); ?>

Wynik działania powyższego kodu:

Pierwszy samochód:

Marka samochodu: Mercedes; kolor nadwozia: RGB(0; 0; 0); opis silnika: 2.0 l 8-cylindrowy rzędowy

Drugi samochód samochód:

Marka samochodu: Mercedes; kolor nadwozia: RGB(100; 0; 0); opis silnika: 2.0 l 8-cylindrowy rzędowy

Gdyby z klasy Car wyrzucić metodę magiczną __clone, efekt działania kodu byłby taki:

Pierwszy samochód:

Marka samochodu: Mercedes; kolor nadwozia: RGB(100; 0; 0); opis silnika: 2.0 l 8-cylindrowy rzędowy

Drugi samochód samochód:

Marka samochodu: Mercedes; kolor nadwozia: RGB(100; 0; 0); opis silnika: 2.0 l 8-cylindrowy rzędowy

Zmieniłem składową koloru obiektu $mercedes2 a skutek tego jest widoczny również na obiekcie, z którego był on kopiowany. Spowodowane jest to oczywiście tym, że do nowej klasy przypisany został adres obiektu klasy typu Color a nie jego wartości.