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
?>
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:
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>");
}
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.