PHP/Metody magiczne: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Zyx (dyskusja | edycje)
dopisanie dalszej części
Zyx (dyskusja | edycje)
Dokończenie opisu klonowania.
Linia 159:
Jednak nasz obiekt może mieć otwarte różne zasoby (np. pliki) lub przechowywać referencje do innych obiektów. Standardowe klonowanie po prostu skopiuje same referencje, dlatego musimy mieć możliwość "poprawienia" sklonowanej kopii tak, by była ona w pełni samodzielna wtedy, gdy tego potrzebujemy. Tu do akcji wkracza magiczna metoda <code>__clone()</code>. Gdy znajduje się ona w klasie, PHP wywołuje ją tuż po sklonowaniu obiektu, dzięki czemu ma ona możliwość wprowadzenia poprawek.
 
Aby zademonstrować działanie wspomnianej funkcji, napiszemy implementację struktury danych zwanej ''kolejką''. Struktury danych są jednym z filarów algorytmiki. Służą do przechowywania dowolnie dużej liczby informacji ograniczonej wyłącznie rozmiarami pamięci, oferując dostęp do nich w pewien ściśle określony sposób. Działanie kolejki jest podobne do kolejek w sklepie - klienci przychodzą z jednej strony, a z drugiej są obsługiwani. W informatyce klientów zastępują dane dodawane z jednej strony, a zdejmowane z drugiej. Będziemy potrzebować dwóch klas: pierwszej do reprezentowania pojedynczego elementu kolejki oraz drugiej do zarządzania całą strukturą. Nas szczególnie będzie interesować proces klonowania. Klonując kolejkę, musimy także sklonować wszystkie obiekty reprezentujące jej elementy, dlatego musimy skorzystać z magicznej metody <code>__clone()</code>.
{{RDoZrobienia}}
 
Zaczynamy od napisania klasy pojedynczego elementu kolejki. Będzie on przechowywać informację oraz referencję do następnego elementu w kolejce. Następny element może mieć namiary na kolejny i tak dalej, aż do końca - tworzy się w ten sposób łańcuch elementów prowadzących od pierwszego do ostatniego czekającego elementu.
 
<source lang="php" line><?php
 
class QueueElement
{
private $_data;
private $_next = null;
 
public function __construct($data)
{
$this->_data = $data;
} // end __construct();
 
public function getData()
{
return $this->_data;
} // end getData();
 
public function setNext(QueueElement $next)
{
$this->_next = $next;
} // end setNext();
 
public function getNext()
{
return $this->_next;
} // end getNext();
} // end QueueElement;
</source>
 
Nasza klasa składa się praktycznie wyłącznie z getterów i setterów. Nie ma tutaj jeszcze operacji klonowania, ponieważ na tym poziomie nie mamy jeszcze nic do zrobienia. Dopiero główna klasa kolejki zawierać będzie całe sterowanie. Znajdować się tu będą namiary na pierwszy i ostatni element kolejki tak, aby można było się do nich szybko dostać podczas odczytu lub zapisu.
 
<source lang="php" line><?php
 
class Queue
{
private $_first = null;
private $_last = null;
 
public function enqueue($data)
{
// Nic nie ma w kolejce.
if($this->_first === null)
{
$this->_first = $this->_last = new QueueElement($data);
}
else
// Coś jest w kolejce, doklejamy się do ostatniego.
{
$element = new QueueElement($data);
$this->_last->setNext($element);
$this->_last = $element;
}
} // end enqueue();
 
public function dequeue()
{
if($this->_first === null)
{
throw new UnderflowException('Nie można pobrać elementu z pustej kolejki.');
}
$dequeued = $this->_first;
$this->_first = $this->_first->getNext();
if($this->_first === null)
{
$this->_last = null;
}
 
return $dequeued->getData();
} // end dequeue();
 
public function __clone()
{
// Zalozmy, ze sklonowana kolejka jest pusta
$top = $this->_first;
$this->_first = $this->_last = null;
 
// Przejedzmy sie po dotychczasowej kolejce i sklonujmy
// wszystkie jej elementy
while($top != null)
{
$this->enqueue(clone $top);
$top = $top->getNext();
}
} // end __clone();
} // end Queue;
</source>
 
Przykładowe działanie:
 
<source lang="php" line><?php
 
$queue = new Queue;
$queue->enqueue(5);
$queue->enqueue(4);
$queue->enqueue(3);
 
$cloned = clone $queue;
$cloned->enqueue(2);
 
try
{
while(true)
{
echo $queue->dequeue().'<br/>';
}
}
catch(UnderflowException $exception)
{
// pusto
}
</source>
 
W przykładzie dodajemy do kolejki trzy elementy, a następnie ją klonujemy i dorzucamy do kopii jeszcze jeden element. Na końcu wyświetlamy elementy oryginału. Gdyby podczas klonowania pozostawić niezmienione elementy, wyświetliłyby nam się cztery liczby. Elementy kolejki są obiektami, zatem w skrypcie dostępne są jedynie referencje do nich. Operacja <code>enqueue()</code> sprawiłaby, że czwarta liczba zostałaby doczepiona do wspólnego elementu, a co gorsza - oryginał wcale by o tym nie wiedział! Mogłoby to doprowadzić do poważnych błędów w działaniu obu list. Zakomentuj operację <code>__clone()</code> i prześledź wszystko samodzielnie.
 
Przy implementowaniu własnej wersji <code>__clone()</code> musimy pamiętać, że operuje ona na '''już sklonowanym obiekcie''', a nie na oryginale. Innymi słowy, PHP najpierw wykonuje dokładną kopię całego obiektu, a dopiero później wykonuje na niej <code>__clone()</code>, by pozwolić jej pozmieniać te rzeczy, które powinny być zmodyfikowane.
 
{{Porada|Nie musisz implementować struktur danych samodzielnie. Oprócz tablic, począwszy od wersji PHP 5.3.0 w bibliotece standardowej SPL zawarte są implementacje najważniejszych struktur: kolejek, stosów, list dwukierunkowych, stert oraz kolejek priorytetowych.}}
 
=== Serializacja obiektów ===