Metody magiczne w PHP

Stronę tą wyświetlono już: 227 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:

Listing 1
  1. <?php
  2. class Settings{
  3. private $settings;
  4. // mogiczna metoda __construct - wywoływana przy tworzeniu obiektu
  5. public function __construct(){
  6. echo("<p>Konstruktor wywołany</p>");
  7. }
  8. // magiczna metoda get - pobierająca wartości z tablicy asocjacyjnej $this->settings
  9. public function __get($name){
  10. if(isset($this->settings[$name])){
  11. return $this->settings[$name];
  12. }
  13. return Null;
  14. }
  15. // magiczna metoda set - ustawiająca wartości w tablicy asocjacyjnej $this->settings
  16. public function __set($name, $value){
  17. $this->settings[$name] = $value;
  18. }
  19. // magiczna metoda isset - sprawdzająca, czy w tablicy asocjacyjnej $this->settings jest już taki element zainicjowany
  20. public function __isset($name){
  21. if(array_key_exists($name, $this->settings)){
  22. if(isset($this->settings[$name])){
  23. return true;
  24. }
  25. }
  26. return false;
  27. }
  28. // magiczna metoda unset - zwalniająca pamięć przydzieloną w tablicy asocjacyjnej $this->settings pod indeksem $name
  29. public function __unset($name){
  30. if(array_key_exists($name, $this->settings)){
  31. echo("<p>usuwam</p>");
  32. unset($this->settings[$name]);
  33. }
  34. }
  35. public function writeAll(){
  36. var_dump($this->settings);
  37. }
  38. // magiczna metoda __dstruct - destruktor (wywoływany, gdy obiekt jest usuwany)
  39. public function __destruct(){
  40. echo("<p>Destruktor wywołany, obiekt jest usuwany</p>");
  41. }
  42. }
  43. $settings = new Settings;
  44. $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
  45. echo("<p>" . $settings->description . "</p>"); // tutaj pobieram wartość z komówki tablicy asocjacyjnej obietku $settings klasy Settings za pomocą metody magicznej __get
  46. if(isset($settings->date)){
  47. echo("Data ustawiona: " . $settings->date);
  48. }else{
  49. echo("Data nie została ustawiona");
  50. }
  51. $settings->writeAll(); // wypisuję dane z tablicy obiektu $settings klasy Settings
  52. unset($settings->description); // usuwam komórkę z tablicy obiektu $settings klasy Settings
  53. $settings->writeAll();
  54. unset($settings); // niszczenie obiektu
  55. ?>

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:

Listing 2
  1. public function __toString(){
  2. $settings_str = "<pre>";
  3. foreach($this->settings as $key => $value){
  4. $settings_str .= $key . ": " . $value . "n";
  5. }
  6. $settings_str = trim($settings_str, "n");
  7. $settings_str .= "</pre>";
  8. return $settings_str;
  9. }

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

Listing 3
  1. <?php
  2. echo($settings); // $settings - to obiekt klasy Settings
  3. ?>

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:

Listing 4
  1. public function __invoke($name){
  2. if(array_key_exists($name, $this->settings)){
  3. var_dump($this->settings[$name]);
  4. }
  5. }

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

Listing 5
  1. <?php
  2. $settings("description");
  3. ?>

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:

Listing 6
  1. public function __call($method, $args){
  2. echo("<p>Metoda $this->" . $method . "(" . implode(', ', $args) . ")</p>");
  3. }
  4. public function __callStatic($method, $args){
  5. echo("<p>Metoda $this->" . $method . "(" . implode(', ', $args) . ")</p>");
  6. }

Wywołanie metody __call:

Listing 7
  1. <?php
  2. $settings->metoda("argument1");
  3. $settings->metoda("argument1", "argument2");
  4. ?>

Wynik działania powyższego kodu:

Metoda $this->metoda(argument1)

Metoda $this->metoda(argument1, argument2)

Wywołanie metody __callStatic:

Listing 8
  1. <?php
  2. Settings::metodaStatyczna("argument1");
  3. Settings::metodaStatyczna("argument1", "argument2");
  4. ?>

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:

Listing 9
  1. <?php
  2. class Color{
  3. public $red,
  4. $green,
  5. $blue;
  6. public function __construct($red, $green, $blue){
  7. $this->red = $red;
  8. $this->green = $green;
  9. $this->blue = $blue;
  10. }
  11. public function __toString(){
  12. return "RGB(" . $this->red ."; " . $this->green . "; " . $this->blue . ")";
  13. }
  14. }
  15. class Car{
  16. public $color,
  17. $mark,
  18. $engine_description;
  19. public function __construct($color, $mark, $engine_description){
  20. $this->color = $color;
  21. $this->mark = $mark;
  22. $this->engine_description = $engine_description;
  23. }
  24. public function __toString(){
  25. return "<p>Marka samochodu: " . $this->mark . "; kolor nadwozia: " . $this->color . "; opis silnika: " . $this->engine_description . "</p>";
  26. }
  27. public function __clone(){
  28. $this->color = clone $this->color;
  29. }
  30. }
  31. $mercedes = new Car(new Color(0,0,0), "Mercedes", "2.0 l 8-cylindrowy rzędowy");
  32. $mercedes2 = clone $mercedes;
  33. $mercedes2->color->red = 100;
  34. echo("<p>Pierwszy samochód:</p>" . $mercedes);
  35. echo("<p>Drugi samochód samochód:</p>" . $mercedes2);
  36. ?>

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.

Komentarze