Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Prośba o sprawdzenie klasy
Forum PHP.pl > Forum > PHP > Object-oriented programming
Jarod
W książce natrafiłem na przykład, który wydaje mi się błędny. Wiem, że książki helionu mają literówki i różnego rodzaju błędy (na stronie helionu w erracie nie ma poprawki do tego), dlatego proszę o sprawdzenie poniższego kodu.


  1. <?php
  2. interface Validator{
  3. abstract function validate();
  4. }
  5. ?>


  1. <?php
  2. require_once('interface.Validator.php');
  3.  
  4. abstract class PropertyObject implements Validator {
  5.  
  6. protected $propertyTable = array();  //Przechowuje pary nazwa-wartość przypisujące
  7. //własności do pól bazy danych
  8. protected $changedProperties = array(); //Lista własności które zostały zmodyfikowane
  9. protected $data; //Dane z bazy
  10. protected $errors = array();
  11.  
  12. public function __construct($arData) {
  13. $this->data = $arData;
  14. }
  15.  
  16. function __get($propertyName) {
  17. if (!array_key_exists($propertyName, $this->propertyTable))
  18. throw new Exception('Błędna własność '.$propertyName.'!');
  19.  
  20. if (method_exists($this, 'get' , $propertyName)) {
  21. return call_user_func(array($this, 'get' , $propertyName));
  22. }
  23. else { 
  24. return $this->data[$this->propertyTable[$propertyName]];
  25. }
  26. }
  27.  
  28.  
  29. function __set($propertyName, $value) {
  30. if (!array_key_exists($propertyName, $this->propertyTable))
  31. throw new Exception('Błędna własność '. $propertyName.' !');
  32.  
  33. if (method_exists($this, 'set' , $propertyName)) {
  34. return call_user_func(array($this, 'set' , $propertyName), $value);
  35. }
  36. else { 
  37. if ($this->propertyTable[$propertyName] != $value &&
  38.  !in_array($propertyName, $this->changedProperties)) {
  39.  $this->changedProperties[] = $propertyName;
  40. }
  41.  
  42. $this_>data[$this->propertyTable[$propertyName]] = $value;
  43. }
  44. }
  45.  
  46. function validate() {
  47.  
  48. }
  49. }
  50. ?>


I teraz ten kod gdzie podejrzewam, że jest błąd..

  1. <?php
  2. reguire_once ('class.PropertyObject.php');
  3.  
  4. class Address extends PropertyObject {
  5.  
  6. function __construct($addressid) {
  7. $arData = DataManager::getAddressData($addressid);
  8.  
  9. parent::__construct($arData);
  10.  
  11. $this->propertyTable['addressid'] = 'adres_id';
  12. $this->propertyTable['id'] = 'adres_id';
  13. $this->propertyTable['entityid'] = 'jednostka_id';
  14. $this->propertyTable['address1'] = 'sadres1';
  15. $this->propertyTable['address2'] = 'sadres2';
  16. $this->propertyTable['city'] = 'smiasto';
  17. $this->propertyTable['zipcode'] = 'skod';
  18. $this->propertyTable['type'] = 'styp';
  19. }
  20.  
  21.  
  22.  function validate () {
  23.  
  24.  if (strlen($this->zipcode) != 6) {
  25.  $this->errors['zipcode'] = 'Należy podać poprawny kod pocztowy.';
  26.  } 
  27.  
  28.  if ($this->address1) {
  29.  $this->errors['address1'] = 'Adres to pole wymagane';
  30.  } 
  31.  
  32.  if ($this->city) {
  33.  $this->errors['city'] = 'Miasto to pole wymagane';
  34.  } 
  35.  
  36.  if (sizeof($this->errors)) {
  37.  return false;
  38.  } else {
  39.  return true;
  40.  }
  41.  
  42.  }
  43. }
  44. ?>


Wydaje mi się, że błąd jest w metodzie validate(). Autor odwołuje suię w ten sposób
  1. <?php
  2. if ($this->address1) {
  3.  $this->errors['address1'] = 'Adres to pole wymagane';
  4.  }
  5. ?>


a powinno być chyba
  1. <?php
  2. if ($this->propertyTable['address1']) {
  3.  $this->errors['address1'] = 'Adres to pole wymagane';
  4.  }
  5. ?>


jeśli nie to dlaczego właśnie tak?
envp
Zauważ, że masz funkcje __get() oraz __set() - pierwsza wykorzystywana jest wtedy kiedy robisz

  1. <?php
  2. echo $obiekt->cos;
  3. ?>


druga kiedy:
  1. <?php
  2. $obiekt->cos = 'lalalal';
  3. ?>


Nawet, kiedy pole cos nie istnieje. Opisalem to tutaj:
envp's devblog
Ludvik
Nie podoba mi się ten kod. Po pierwsze właściwość nie jest walidatorem, co najwyżej może być walidowana (czyli raczej Validable). Poza tym nie rozumiem, po co deklarować metodę validate, w klasie PropertyObject... Jest to typowy przykład zastosowania metody abstrakcyjnej, a nie bezsensownego wklepywania pustej...

Tak poza tym, to dlaczego my mamy to sprawdzać? Więcej się nauczysz, jak sam będziesz chciał to sprawdzić... Odpal skrypt, zdebuguj go xdebugiem czy czymś innym...
Jarod
Cytat(Ludvik @ 16.08.2006, 13:29 ) *
Nie podoba mi się ten kod. Po pierwsze właściwość nie jest walidatorem, co najwyżej może być walidowana (czyli raczej Validable). Poza tym nie rozumiem, po co deklarować metodę validate, w klasie PropertyObject... Jest to typowy przykład zastosowania metody abstrakcyjnej, a nie bezsensownego wklepywania pustej...


Coraz częściej dochodze do wniosku, że ta książka jest bardzo zamotana. Chyba tylko po to, żeby więcej stron było.

Cytat(envp @ 16.08.2006, 10:26 ) *
Zauważ, że masz funkcje __get() oraz __set() - pierwsza wykorzystywana jest wtedy kiedy robisz


Ok. Powoli łapie. Ale nie mogę zrozumieć tego kawałka kodu (z metody __get() ):
  1. <?php
  2. if (method_exists($this, 'get' , $propertyName)) {
  3. return call_user_func(array($this, 'get' , $propertyName));
  4. }
  5. else { 
  6. return $this->data[$this->propertyTable[$propertyName]];
  7. ?>


Po co sprawdza się czy metoda istnieje. Nie łapie też argumentów tej metody oraz call_user_func. Czytałem o tych funkjach ale nie mogę załapać. Możecie mi to wytłumaczyć?
Ludvik
Przepisałeś błędnie niektóre wywołania metod. Zamiast kropki (operator łączenia łańcuchów znakowych) wstawiłeś przecinek.
  1. <?php
  2. method_exists($this, 'get' . $propertyName)
  3. ?>

call_user_func" title="Zobacz w manualu php" target="_manual... Manual na pewno Ci wyjaśni znaczenie parametrów.
Całe to zamieszanie jest po to, aby korzystać z metod getNazwaWłaściwości zamiast bezpośredniego odwoływania się do tablicy z danymi. Jeżeli nie istnieje metoda pobierająca dane, wtedy zwracana jest wartość bezpośrednio z tablicy...
Jarod
Cytat(Ludvik @ 17.08.2006, 08:58 ) *
Przepisałeś błędnie niektóre wywołania metod. Zamiast kropki (operator łączenia łańcuchów znakowych) wstawiłeś przecinek.
  1. <?php
  2. method_exists($this, 'get' . $propertyName)
  3. ?>

Tak mam w książce. Ale naet jak powinna być kropka to nie ma sensu zapis $this.get.propertyName.
Nie powinno być:
  1. <?php
  2. method_exists($this->'get'->$propertyName)
  3. ?>

?
Ogólnie w manulu piszą, że funkcja method_exists pobiera dwa parametry: instancje klasy, metode.

Nie łapie tego. Zresztą przecież jak mamy metode magiczną __get() to nie odwołujemy się do niej przez $this->cos tylko $this->cos();


Cytat(Ludvik @ 17.08.2006, 08:58 ) *
call_user_func" title="Zobacz w manualu php" target="_manual... Manual na pewno Ci wyjaśni znaczenie parametrów.

Sprawdzałem. Podobnie jak w z method_exists nie rozumiem po co w środek wstawiane jest to get i array..

Cytat(Ludvik @ 17.08.2006, 08:58 ) *
Całe to zamieszanie jest po to, aby korzystać z metod getNazwaWłaściwości zamiast bezpośredniego odwoływania się do tablicy z danymi. Jeżeli nie istnieje metoda pobierająca dane, wtedy zwracana jest wartość bezpośrednio z tablicy...

Jaki jest sens? Przecież pisząć aplikacje pisze metodę, która pobiera dane, bo wiem, że tego będę potrzebował. I nie ma sensu mówić, że nad proejktem może pracować wiele osób. Bo nawet w takim przypadku jeden programista pisze i kończy daną klase i powinien takie metody dostępowe utworzyć..
Ludvik
Powinno być
  1. <?php
  2. method_exists($this, 'get' . $propertyName)
  3. ?>

Pierwszy argument to instancja klasy. Drugi argument ('get' . $propertyName) to nazwa metody. Nie widzę nic dziwnego w tym wywołaniu...

Dalej...
  1. <?php
  2. call_user_func(array($this, 'get' . $propertyName));
  3. ?>

Tak samo... Pierwszy argument ma być typu callback. Jest to nazwa funkcji albo w przypadku wywoływania metod tablica. Pierwszy elementem tablicy jest referencja do obiektu (albo nazwa klasy, jeżeli wywołujemy statyczną metodę). Drugi element to nazwa metody.

Oni to zrobili po to, aby przeciążyć "magiczne" metody, ale nie tracić zachowania metod programisty getXXX... Jeżeli istnieje metoda getWlasciwosc, a ty poprosisz o dane tak:
  1. <?php
  2. echo $obj->wlasciwosc;
  3. ?>

To i tak efektem będzie wywołanie metody getWlasciwosc...
Jarod
Cytat(Ludvik @ 17.08.2006, 09:27 ) *
Powinno być
  1. <?php
  2. method_exists($this, 'get' . $propertyName)
  3. ?>

Pierwszy argument to instancja klasy. Drugi argument ('get' . $propertyName) to nazwa metody. Nie widzę nic dziwnego w tym wywołaniu...

$this nie jest instancją klasy(?) getNazwaMetody samo w sobie nie jest dziwne. Dziwne jest to, że w przykładzie, który podałem na samym początku nie ma metody. Jest tylko magiczna (czyli jak dobrze przeczytałem wywoływana automatycznie gdy ktoś chce wywołać nieistniejącą metode), nie ma żadnej innej metody.


Cytat(Ludvik @ 17.08.2006, 09:27 ) *
Dalej...
  1. <?php
  2. call_user_func(array($this, 'get' . $propertyName));
  3. ?>

Tak samo... Pierwszy argument ma być typu callback. Jest to nazwa funkcji albo w przypadku wywoływania metod tablica. Pierwszy elementem tablicy jest referencja do obiektu (albo nazwa klasy, jeżeli wywołujemy statyczną metodę). Drugi element to nazwa metody.

Oni to zrobili po to, aby przeciążyć "magiczne" metody, ale nie tracić zachowania metod programisty getXXX... Jeżeli istnieje metoda getWlasciwosc, a ty poprosisz o dane tak:
  1. <?php
  2. echo $obj->wlasciwosc;
  3. ?>

To i tak efektem będzie wywołanie metody getWlasciwosc...


Thx.
Ludvik
$this jest referencją do obiektu, którego metoda jest wywoływana, czyli po ludzku - do samego siebie. Przy odczycie/przekazywaniu nie różni się niczym od innych referencji.
Jarod
Analizuje ten przykład jeszcze raz i mam wrażenie, że autor troszke zamieszał. Czy metoda __get nie powinna raczej wyglądać tak:
  1. <?php
  2. function __get($propertyName) {
  3.  
  4. //Jeśli istnieje metoda to ją wywołujemy
  5. if (method_exists($this, 'get' , $propertyName))
  6. {
  7. return call_user_func(array($this, 'get' , $propertyName));
  8. }
  9. else
  10. {  //Jeśli nie istnieje to sprawdzamyczy podane propertyName jest w tablicy. A nie sp
    rawdzamy
  11. //tego na samym początku bo to bez sensu.. 
  12. if (!array_key_exists($propertyName, $this->propertyTable))
  13. throw new Exception('Błędna własność '.$propertyName.'!');
  14. else return $this->data[$this->propertyTable[$propertyName]];
  15. }
  16. }
  17. ?>


Po co sprawdzać czy istnieje propertyName przed sprawdzeniem istnienia metody? Po to się to sprawdza (chyba) czy można zwrócić wartość z tablicy propertyTable..?
Ludvik
Tak też może być. Autor klasy zakładał, że nawet w przypadku istnienia metody dostępowej, należy sprawdzić czy dane istnieją w tablicy. Nie ma danych - metoda nie ma sensu...

U Ciebie istnieje możliwość wywołania metod get* nawet w przypadku, gdy dane w tablicy nie istnieją...

Kod metody __set też mi się nie podoba...
Jarod
Cytat(Ludvik @ 21.08.2006, 10:15 ) *
Tak też może być. Autor klasy zakładał, że nawet w przypadku istnienia metody dostępowej, należy sprawdzić czy dane istnieją w tablicy. Nie ma danych - metoda nie ma sensu...

Po co? Jak istnieje metoda to ją wykonujesz a nie pobierasz wartość z tablicy. Jak napiszesz
  1. <?php
  2. __get(Costam)
  3. ?>
to sprawdzane jest czy istniej metoda getCostam, jeśli tak ją wywołujesz a jeśli nie to to zwraca
  1. <?php
  2. $this->data[$this->propertyTable['Costam']]
  3. ?>



Cytat(Ludvik @ 21.08.2006, 10:15 ) *
U Ciebie istnieje możliwość wywołania metod get* nawet w przypadku, gdy dane w tablicy nie istnieją...


Jak wyżej.
Ludvik
Cytat
Po co? Jak istnieje metoda to ją wykonujesz a nie pobierasz wartość z tablicy.

Nie odpowiem na pytanie po co - bo nie jestem autorem tej klasy. Sam bym sprawdzał dopiero w przypadku braku metody dostępowej...
Jarod
Cytat(Ludvik @ 21.08.2006, 16:57 ) *
Nie odpowiem na pytanie po co - bo nie jestem autorem tej klasy. Sam bym sprawdzał dopiero w przypadku braku metody dostępowej...


Ale sam napisałeś:
Cytat(Ludvik @ 21.08.2006, 10:15 ) *
U Ciebie istnieje możliwość wywołania metod get* nawet w przypadku, gdy dane w tablicy nie istnieją...


A to nieprawda - starałem się to wytłumaczyć w poscie wyżej.. :/
Ludvik
Mylisz się. Przeanalizuj dobrze kod. Żeby wywołać metodę get* musi być spełniony warunek (w którym masz błąd składniowy, o którym już wcześniej pisałem...):
  1. <?php
  2. if (method_exists($this, 'get' . $propertyName))
  3. ?>

Czyli musi istnieć odpowiednia metoda. Nie ma tutaj żadnego sprawdzenia, czy metoda odwołuje się do istniejącego pola. To zadanie zostało powierzone właśnie metodzie dostępowej, a nie __get... Tak na prawdę fakt posiadania przez klasę metody getDziwnePole nie gwarantuje w Twoim przypadku posiadania tego pola...
Jarod
Cytat(Ludvik @ 21.08.2006, 18:12 ) *
Czyli musi istnieć odpowiednia metoda. Nie ma tutaj żadnego sprawdzenia, czy metoda odwołuje się do istniejącego pola. To zadanie zostało powierzone właśnie metodzie dostępowej, a nie __get...


Ok - zgadza się. Ale skąd wiesz, że metoda dostępowa tego nie sprawdza? Nie ma napisanego kodu metody dostępowej ale to chyba ona powinna sprawdzać i ewentualnie wyrzucić wyjątek.

Cytat(Ludvik @ 21.08.2006, 18:12 ) *
Tak na prawdę fakt posiadania przez klasę metody getDziwnePole nie gwarantuje w Twoim przypadku posiadania tego pola...


Masz racje. Ale to metoda getDziwnePole powinna sprawdzić czy jest takie pole i jeśli jest to się wykonać a jeśli nie to throw...

Moim zdaniem autor mota bardzo. Pozatym zaglębiając się bardziej w przykłady nie jestem przekonany czy tą książkę pisali experci..
Ludvik
Cytat
Ok - zgadza się. Ale skąd wiesz, że metoda dostępowa tego nie sprawdza? Nie ma napisanego kodu metody dostępowej ale to chyba ona powinna sprawdzać i ewentualnie wyrzucić wyjątek.

Powinna, ale zbytnie zaufanie do programisty stwarza dodatkowe niebezpieczeństwo powstania błędów w kodzie. Co nie zmienia faktu, że ten błąd wyjdzie przy testach i jest raczej niegroźny, a wklepanie tego warunku spowoduje lekką stratę wydajności.
Jarod
Cytat(Ludvik @ 21.08.2006, 18:46 ) *
(..)a wklepanie tego warunku spowoduje lekką stratę wydajności.


A waruenk przed sprawdzaniem nie wpływa na wydajność? Będzie to samo.. Chyba że się myle..


EDIT: Dręczy mnie ejszcze jedna sprawa na którą nie uzyskłame odpowiedzi.
Wydaje mi się, że błąd jest w metodzie validate(). Autor odwołuje suię w ten sposób
  1. <?php
  2. if ($this->address1) {
  3.  $this->errors['address1'] = 'Adres to pole wymagane';
  4.  }
  5. ?>


a powinno być chyba
  1. <?php
  2. if ($this->propertyTable['address1']) {
  3.  $this->errors['address1'] = 'Adres to pole wymagane';
  4.  }
  5. ?>


jeśli nie to dlaczego właśnie tak?
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.