Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Dziedziczenie magicznych metod
Forum PHP.pl > Forum > PHP > Object-oriented programming
Asmox
Chciałem zrobić uniwersalną implementację dla metody magicznej __get($var). W tym celu, w klasie abstractObject napisałem coś takiego:
  1.  
  2. public function __get($zmienna) {
  3. if (!property_exists($this, $zmienna)) {
  4. $exc = new ExceptionConceptor('Proba odwolania do nieistniejacego pola');
  5. $exc->addConcept('pole', $zmienna);
  6. throw $exc;
  7. }
  8. if (!isset($this->$zmienna)) {
  9. if (method_exists($this, 'pobierzPole'.ucfirst($zmienna)))
  10. call_user_func(array($this, 'pobierzPole'.ucfirst($zmienna)));
  11. }
  12. //return $this->$zmienna;
  13. }

Natomiast w klasie dziedziczącej coś takiego:
  1. public function __get($zmienna) {
  2. parent::__get($zmienna);
  3. return $this->$zmienna;
  4. }

Powiem szczerze, że jestem zszokowany koniecznością napisania czegoś takiego. Myślałem, że dziedzicząc __geta, klasa dziedzicząca pobierze normalnie swoje pole. Natomiast gdy mój kod wyglądał w ten sposób, że w klasie dziedziczącej nie było nic dotyczącego tej metody, a w abstractObject nie było komentarza przy returnie, wywalało mi komunikat:
Cytat
Notice: Undefined property: tag::$(tu nazwa zmiennej do której się odwoływałem) in...


Natomiast jeśli chodzi o dziedziczenie magicznej metody __set(), jest jeszcze gorzej. Kod wygląda tak: (klasa abstractObject)
  1. public function __set($zmienna, $wartosc) {
  2. if ($this->blokada) {
  3. if (isset ($this->$zmienna)) {
  4. $exc = new abstractObjectException('Proba dostepu do zablokowanego pola');
  5. throw $exc;
  6. }
  7. else
  8. $this->$zmienna = $wartosc;
  9. }
  10. $this->$zmienna = $wartosc;
  11. echo $this->$zmienna;
  12. }


Jedno pytanie: dlaczego tak się dzieje? :-(
mortus
Dzieje się tak, ponieważ każda klasa ma domyślne metody __set() i __get(). Zatem przeciążamy te metody w klasie rodzicielskiej, tworzymy klasę rozszerzającą klasę rodzicielską i domyślne metody __set() i __get() klasy potomnej przeciążają te nasze - już wcześniej przeciążone. Takie błędne koło.

Po prostu jeśli metody w klasie rodzicielskiej i potomnej nazywają się tak samo, i mają taki sam zasięg, to te z klasy potomnej przeciążają te z klasy rodzicielskiej.
A metody __set() i __get() istnieją zawszę i korzystamy z nich za "sprawą" operatora wyłuskania ->.
Asmox
Czyli co mam zrobić? Pisać takie samy metody w każdej klasie?
mortus
Musisz pamiętać, że metody __set() i __get() służą do ustawiania i pobierania wartości zmiennych prywatnych i chronionych. Jeśli w klasie nie masz np. prywatnej zmiennej $a, a użyjesz kodu
  1. $object->a = 'jakaś wartość tekstowa';

to tak naprawdę nie zadziałają Twoje metody __set() i __get(), a zadziałają te metody domyślne. W obiekcie danej klasy od tej chwili będziesz miał zmienną $a, ale będzie to zmienna publiczna, którą możemy ustawić/pobrać nie przeciążając metod __set()/__get(). Dlatego używanie przeciążonych metod __set() i __get() w ten sposób, w jaki Ty to robisz mija się z celem, chyba, że każdą zmienną jaką później będziesz dodawał masz zadeklarowaną jako prywatną lub chronioną. Lepszy podejściem byłoby coś takiego:
  1. class A {
  2. private $properties = array();
  3. public function __set($key, $value) {
  4. if(!isset($this->properties[$key])) {
  5. $this->properties[$key] = $value;
  6. }
  7. }
  8. public function __get($key) {
  9. if(isset($this->properties[$key])) {
  10. return $this->properties[$key];
  11. }
  12. }
  13. }

Jeżeli zatem koniecznością u Ciebie jest przeciążanie metod __set() i __get(), to nie masz innego wyjścia, jak przeciążać te metody dla każdej klasy, czy to potomnej, czy rodzicielskiej.
Natomiast zastanów się, czy rzeczywiście jest Ci to potrzebne. Zamiast się zastanawiać możesz również sprawdzić, jaki będzie zasięg (widoczność) tych Twoich zmiennych, robiąc var_dump jakiegoś przykładowego obiektu.

Kiedyś już coś na ten temat pisałem. Poczytaj.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.