Deep clone, czyli kopiowanie obiektów
Kopiowanie obiektów może się wydawać dla większości wręcz trywialne. Jednak, dla początkujących w programowaniu obiektowym może to nie być takie oczywiste.
O co chodzi?
Żeby lepiej zrozumieć problem przeanalizujmy przykład:
<?php class Zawod{ private $nazwa; function __construct($nazwa) { $this->nazwa=$nazwa; } public function setNazwa($nazwa) { $this->nazwa=$nazwa; } public function getNazwa() { return $this->nazwa; } } class Osoba { private $imie; private $zawod; function __construct($imie, $nazwa) { $this->imie = $imie; $this->zawod=new Zawod($nazwa); } public function setImie($imie) { $this->imie = $imie; } public function getImie() { return $this->imie; } public function setZawod($zawod) { $this->zawod->setNazwa($zawod); } public function getZawod() { return $this->zawod->getNazwa(); } } $osoba=new Osoba('Ania', 'sekretarka'); $osoba2 = $osoba; echo $osoba->getImie()." - ".$osoba->getZawod()."\n"; // wyswietli "Ania - sekretarka" echo $osoba2->getImie()." - ".$osoba2->getZawod()."\n"; // wyswieli "Ania - sekretarka" $osoba->setImie('Tomek'); $osoba->setZawod('kierowca'); echo $osoba->getImie()." - ".$osoba->getZawod()."\n"; // wyswietli "Tomek - kierowca" echo $osoba2->getImie()." - ".$osoba2->getZawod()."\n"; // wyswieli "Tomek - kierowca" ?>
Do 45 linii wszystko wydaje się oczywiste. Jednak, w ostatniej linii dostajemy „niespodziewany” wynik – zmiana imienia i zawodu w obiekcie $osoba powoduje także zmianę w $osoba2. Dlaczego tak się dzieje? W linii 43 tak naprawdę nie robimy kopii obiektu jak to się dzieje w typach prostych (liczby, stringi itp.), tylko tworzymy nową referencję (odnośnik na odpowiedni adres pamięci, więcej informacji na Wikipedii) do obiektu stworzonego za pomocą zmiennej $osoba. Jeżeli chcemy skopiować obiekt musimy użyć clone oraz stworzyć magiczną metodę __clone() w klasie Osoba, którą wykorzystamy do sklonowania właściwości obiektu.
<?php class Zawod{ private $nazwa; function __construct($nazwa) { $this->nazwa=$nazwa; } public function setNazwa($nazwa) { $this->nazwa=$nazwa; } public function getNazwa() { return $this->nazwa; } } class Osoba { private $imie; private $zawod; function __construct($imie, $nazwa) { $this->imie = $imie; $this->zawod=new Zawod($nazwa); } public function setImie($imie) { $this->imie = $imie; } public function getImie() { return $this->imie; } public function setZawod($zawod) { $this->zawod->setNazwa($zawod); } public function getZawod() { return $this->zawod->getNazwa(); } public function __clone() { $this->zawod = clone $this->zawod; } } $osoba=new Osoba('Ania', 'sekretarka'); $osoba2 = clone $osoba; echo $osoba->getImie()." - ".$osoba->getZawod()."\n"; // wyswietli "Ania - sekretarka" echo $osoba2->getImie()." - ".$osoba2->getZawod()."\n"; // wyswieli "Ania - sekretarka" $osoba->setImie('Tomek'); $osoba->setZawod('kierowca'); $osoba2->setZawod('kierowniczka'); echo $osoba->getImie()." - ".$osoba->getZawod()."\n"; // wyswietli "Tomek - kierowca" echo $osoba2->getImie()." - ".$osoba2->getZawod()."\n"; // wyswieli "Ania - kierowniczka" ?>