Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: odwoływanie się do priv pola, które jest obiektem
Forum PHP.pl > Forum > PHP > Object-oriented programming
dag
Najpierw podam kod:

Klasa String:
  1. <?php
  2. class String {
  3. private $sTxt;
  4.  
  5. public function set( $sTxt ) {
  6. $this->sTxt= $sTxt;
  7. }
  8.  
  9. public function get() {
  10. return $this->sTxt;
  11. }
  12.  
  13.  
  14. public function length() {
  15. return strlen( $this->sTxt );
  16. }
  17.  
  18. public function __toString() {
  19. return $this->get();
  20. }
  21. }
  22.  
  23. ?>


Klasa Coment:
  1. <?php
  2. class Comment {
  3. private $title;
  4.  
  5. public function __construct() {
  6. $this->title = new String();
  7. }
  8.  
  9. public function __call( $sName, $aArguments ) {
  10. return $this->$sName;
  11. }
  12.  
  13. }
  14. ?>


Odwołanie:
  1. <?php
  2. $c1 = new Comment();
  3. $c1->title()->set('jakis tytul');
  4. echo'Dlugosc tekstu to ' . $c1->title()->length() . ' znakow';
  5.  
  6. ?>


Zastosowałem tutaj IMHO brzydkie rozwiązanie z użyciem metody __call.

Innym rozwiązaniem jest zastosowanie public $title. Wówczas odwoływalibyśmy się w ten sposób:
  1. <?php
  2. $c1 = new Comment();
  3. $c1->title->set('jakis tytul');
  4. echo'Dlugosc tekstu to ' . $c1->title->length() . ' znakow';
  5.  
  6. ?>

Czyli bez użycia title(). Jednak chyba nie jest to zbyt dobry pomysł ponieważ zmienna będzie publicznie dostępna, a takto mamy tylko dostęp do jej metod (title->set, title->length, etc.), więc nie można jej np. usunąć z zewnątrz.



Żeby nie zakładać nowego tematu napiszę, że brakuje mi bardzo przeładowywania metod w taki oto sposób:
  1. <?php
  2. class Comment {
  3. public function __construct() {}
  4. public function __construct( $sTitle ) {}
  5. public function __construct( $sTitle, $sBody ) {}
  6. }
  7. ?>

Jestem ciekaw jak sobie radzicie z tym problemem.
matid
IMO klasa Comment powinna wyglądać tak:
  1. <?php
  2. class Comment {
  3. private $title;
  4.  
  5. public function __construct() {
  6. $this->title = new String();
  7. }
  8.  
  9. public function getTitle() {
  10. return $this->title;
  11. }
  12.  
  13. }
  14. ?>

I potem odwołujesz się tak:
  1. <?php
  2. $c1 = new Comment();
  3. $c1->getTitle()->set('jakis tytul');
  4. echo'Dlugosc tekstu to ' . $c1->getTitle()->length() . ' znakow';
  5.  
  6. ?>
dag
Oczywiście wiem o tym ;-) Zapomniałem dopisać to rozwiązanie na Forum. Obecnie korzystam właśnie z takiego

Trochę pokręciłem. To które podałeś matid wydaje się najrozsądniejsze i najbezpieczniejsze.

Jednak czy istnieje jeszcze inne rozwiązanie? Może jakieś wariacje z __get i __call? Chodzi mi o to, aby odwołać się poprzez
  1. <?php
  2. $c1->title->length()
  3. ?>
Jednak jeśli użyję __get to i tak nie sprawdzi się to, ponieważ zmienna $title istnieje i nie dojdzie do wykonania metody __get.

Innym rozwiązaniem może być dodanie jakiegoś przedrostka np. get_ (gdzieś widziałem klasę z wariacją __get lub _call (już nie pamiętam) i przedrostka get_ (wartość) lub Get (odwołanie do metody). Jednak to mija się z celem, już lepiej stworzyć metodę GetTitle() i zwrócić referencję do obiektu, która to jest domyślnie zwracana w php 5.

Zależy mi na tym by wartość była private ze względów bezpieczeństwa. Jestem ciekaw jak rozwiąże to Seth w swoim frameworku Carbon, który ma się odwoływać m.in. tak w swoim systemie szablonów
  1. <?php
  2. {$carbon->content->nowosci->cos_tam->tresc}
  3. ?>

Pozostaje chyba jedynie albo zastępować np. content na getConent(), nowosci na getNowosci() albo zmienić access z private na public.

Druga kwestia

Przeładowywanie metod.
Czy pozostaje jedynie rozwiązanie przez stworzenie setterów?
  1. <?php
  2.  
  3. $c1 = new Comment();
  4. $c1->setTitle();
  5. $c1->setBody();
  6. }
  7. ?>


O ile łatwiej by było gdyby:
  1. <?php
  2. $c1 = new Comment();
  3. $c2 = new Comment( 123 ); //id
  4. $c3 = new Comment( 456, 'Tytul', 'Tresc' ); //id, tytul, tresc
  5. ?>


Można oczywiście skorzystać z funkcji func_get_args" title="Zobacz w manualu PHP" target="_manual
dasko
Cytat
Jednak jeśli użyję __get to i tak nie sprawdzi się to, ponieważ zmienna $title istnieje i nie dojdzie do wykonania metody __get.

Czyżby ? biggrin.gif Sprawdzałeś? Przeczytaj to -> http://www.php.net/manual/en/language.oop5...ading.php#53539
( notka by Dasko tongue.gif )
dag
Cytat(dasko @ 2005-07-02 20:14:49)
Cytat
Jednak jeśli użyję __get to i tak nie sprawdzi się to, ponieważ zmienna $title istnieje i nie dojdzie do wykonania metody __get.

Czyżby ? biggrin.gif Sprawdzałeś? Przeczytaj to -> http://www.php.net/manual/en/language.oop5...ading.php#53539
( notka by Dasko tongue.gif )

Czytałem czytałem i to wielokrotnie ;-)

Dla leniwych:
  1. <?php
  2. class MyClass {
  3.  
  4.  private $foo;
  5.  
  6.  public function __get($name) {
  7.  echo $name . ' doesn't exist...<br />';
  8.  }
  9.  
  10.  public function __set($name, $value) {
  11.  echo 'Cannot set ' . $name . '. The requested property doesn't exist...<br />';
  12.  }
  13.  
  14. }
  15.  
  16. // class' instance
  17. $obj = new MyClass;
  18. // trying to access foo - __get() is called
  19. echo $obj->foo;
  20. // trying to set foo - __set() is called
  21. $obj->foo = 'newValue';
  22. ?>



Cytat
The feature was tested on php 5.1-dev.


Ja mam php Version 5.0.3 i wyskakuje mi
Cytat
Cannot access private property MyClass::$foo
dasko
Pozostaje mi polecić php 5.1 winksmiley.jpg
matid
Skoro chcesz się odwoływać do tego w ten sposób:
$cm->title->lenght();
No to jakby na to nie patrzeć 'title' jest własnością publiczną i żadna kwestia bezpieczeństwa nie jest naruszona.

Jest to wprawdzie przeciw zasadzie hermetyzacji, no ale skoro koniecznie tego potrzebujesz to chyba innego wyjścia nie ma.
dag
Cytat(matid @ 2005-07-02 21:10:50)
Skoro chcesz się odwoływać do tego w ten sposób:
$cm->title->lenght();
No to jakby na to nie patrzeć 'title' jest własnością publiczną i żadna kwestia bezpieczeństwa nie jest naruszona.

Jest to wprawdzie przeciw zasadzie hermetyzacji, no ale skoro koniecznie tego potrzebujesz to chyba innego wyjścia nie ma.

Właśnie dlatego nie chcę aby $title było public. Zaciekawiło mnie to ponieważ Seth podawał na swoim blogu koncepcję swojego frameworka i odwoływał się w przykładzie właśnie w ten sposób.

Zostaję przy private i stworzeniu gettera.

Ciekawi mnie jeszcze druga kwestia, którą opisałem mianowicie jak sobie radzicie z przeładowywaniem metod. Czy korzystacie z funkcji func_get_args i tym podobnych czy raczej jesteście za tworzeniem dodatkowych setterów?


  1. <?php
  2.  
  3. class String {
  4. private $sTxt;
  5.  
  6. public function set( $sTxt ) {
  7. $this->sTxt = $sTxt;
  8. }
  9.  
  10. public function get() {
  11. return $this->sTxt;
  12. }
  13.  
  14. public function length() {
  15. return strlen( $this->sTxt );
  16. }
  17.  
  18. public function __toString() {
  19. return $this->get();
  20. }
  21. }
  22.  
  23. class Comment {
  24. private $title;
  25. private $body;
  26.  
  27. public function __construct() {
  28. $this->title = new String();
  29. $this->body = new String();
  30. }
  31.  
  32. public function getTitle() {
  33. return $this->title->__toString();
  34. }
  35.  
  36. public function setTitle( $sTxt ) {
  37. $this->title->set( $sTxt );
  38. }
  39.  
  40. public function getBody() {
  41. return $this->body->__toString();
  42. }
  43.  
  44. public function setBody( $sTxt ) {
  45. $this->body->set( $sTxt );
  46. }
  47.  
  48. }
  49.  
  50.  
  51. $cb = new Comment();
  52. $cb->setTitle('sdafas');
  53. $cb->setBody('body');
  54.  
  55. echo $cb->getTitle();
  56. echo $cb->getBody();
  57.  
  58. ?>

Myślę, że to będzie najrozsądniejsze rozwiązanie. Jeśli będę chciał odwołać się do jakiejś metody String przesłonię ją (Facade) w klasie Comment.
matid
Ja osobiście nie lubię dodawać kolejnych setterów do containera. U mnie container ma zwracać poszczególne instancje, dodawać je lub usuwać. Czyli robie np. tak:
  1. <?php
  2. $Container->addObject( new Parameter( 'Name', 'Value' ) );
  3. $Container->getObject( 'Name' )->setValue( 'New Value' );
  4. echo $Container->getObject( 'Name' )->getValue();
  5. ?>

Ale jak to mówią - każdy robi jak mu się podoba winksmiley.jpg
dag
Cytat(matid @ 2005-07-02 21:36:21)
Ja osobiście nie lubię dodawać kolejnych setterów do containera. U mnie container ma zwracać poszczególne instancje, dodawać je lub usuwać. Czyli robie np. tak:
  1. <?php
  2. $Container->addObject( new Parameter( 'Name', 'Value' ) );
  3. $Container->getObject( 'Name' )->setValue( 'New Value' );
  4. echo $Container->getObject( 'Name' )->getValue();
  5. ?>

Ale jak to mówią - każdy robi jak mu się podoba winksmiley.jpg

Masz rację ;-) Coś dzisiaj ciężko idzie mi myślenie ;-) Co będzie jeśli rozbuduję klasę String? Aż strach pomyśleć ;-) Dodawanie nowej funkcjonalności (czyli nowych metod) w innych klasach ;/ a takto jedynie zostanie mi korzystanie z nowych features ;-)
Imperior
Z metodami __get() czy też może i __call() trzeba bardzo uważać. Ja kiedyś sobie przez __get() pobierałem moduły jądra i jakież moje zdziwienie było, kiedy pojawiały mi się błędy... Powód? Pobrany moduł przez __get() chciał pobrać inny moduł. Myślałem, że tak się da, a jednak PHP5(1) ma blokade na takie odwołania (ochrona przed zapętleniem).

(1) Było to kilka miesięcy temu, dopisałem się do innego zgłoszenia na bugtracku, nie wiem jak jest obecnie - warto wpierw sprawdzić to, zanim będzie się używać.
hawk
@dag: Rozwiązanie jest proste:
  1. <?php
  2. class Comment {
  3. private $aAttributes = array(
  4. 'title' => null,
  5. );
  6.  
  7. public function __construct() {
  8. $this->aAttributes['title'] = new String();
  9. }
  10.  
  11. public function __get($sName) {
  12. return array_key_exists($sName, $this->aAttributes) ? $this->aAttribute[$sName] : null;
  13. }
  14. }
  15. ?>


Co do przeładowania konstruktora: robienie dodatkowych setterów nie wchodzi w grę, bo to zupełnie inna funkcjonalność niż konstruktor z wieloma parametrami. Nie widzę przeszkód w dodaniu wartości domyślnej i func_get_args.

@matid: A czy ten Container nie przypomina za bardzo hashmapy worriedsmiley.gif ?
dag
  1. <?php
  2. class Type {
  3. private $mType;
  4.  
  5. public function set( $mType ) {
  6. $this->mType = $mType;
  7. }
  8.  
  9. public function get() {
  10. return $this->mType;
  11. }
  12.  
  13. public function __toString() {
  14. return $this->get();
  15. }
  16. }
  17.  
  18. class String extends Type {
  19. public function length() {
  20. return strlen( $this->get() );
  21. }
  22. }
  23.  
  24. class Input extends String {
  25.  
  26. public function toForm( $sName ) {
  27. $sForm = '<input type=\"text\" name=\"' . $sName . '\"';
  28.  
  29. if( $this->get() != '' ) {
  30. $sForm .= ' value=\"' . $this->get() . '\"';
  31. }
  32.  
  33. return $sForm . '/>';
  34. }
  35.  
  36. }
  37.  
  38. abstract class Bean {
  39. protected $aAttributes = array();
  40.  
  41. public function __get( $sName ) {
  42. return array_key_exists( $sName, $this->aAttributes ) ? $this->aAttributes[ $sName ] : null;
  43. }
  44.  
  45. public function toForm() {
  46. $aForms = array();
  47.  
  48. foreach( $this->aAttributes as $key=>$value ) {
  49. $aForms[ $key ] = $this->$key->toForm( $key );
  50. }
  51.  
  52. return $aForms;
  53. }
  54. }
  55.  
  56. class CommentBean extends Bean {
  57.  
  58. public function __construct() {
  59. $this->aAttributes[ 'title' ] = new Input();
  60. $this->aAttributes[ 'body' ] = new Input();
  61. }
  62. }
  63.  
  64.  
  65.  
  66. $cb = new CommentBean();
  67. $cb->title->set('tytul1');
  68. $cb->body->set('tresc1');
  69. $aForms = $cb->toForm();
  70. echo $aForms['title'];
  71.  
  72. $dlugosc = new Input();
  73. $dlugosc->set( $cb->title->length() );
  74. echo '<br/><br/>';
  75. echo $dlugosc->toForm( 'dlugosc' );
  76.  
  77. echo '<hr/><hr/>';
  78.  
  79. //normalne wykonanie metody __toString()
  80. echo $cb->title;
  81.  
  82. echo '<hr/>';
  83.  
  84. //przecinek zamiast kropki
  85. echo $cb->title,'<hr/>';
  86.  
  87. //kropka zamiast przecinka
  88. echo $cb->title.'<hr/>';
  89. ?>

Ciekawi mnie kwestia kropki i przecinka. Gdy jest kropka php nie wykonuje metody __toString(), a gdy przecinek wykonuje. Gdy zastosujemy przecinek to czy
  1. <?php
  2. echo $cb->title, '<hr/>';
  3. ?>
jest równoznaczne z:
  1. <?php
  2. echo $cb->title;
  3. echo '<hr/>';
  4. ?>
questionmark.gif na to wygląda.


Najlpeszmy i "najczystszym" rozwiązaniem było by chyba wprowadzenie czwartego typu access ;-), który by miał większość właściwości private jednak pozwalał by na zwrócenie obiektu. ;-)
hawk
Problem z kropką i __toString() to znany bug php.
dr_bonzo
To jest raczej feature, ale nadal bardzo uciazliwy smile.gif
Cytat
It is worth noting that the __toString method will only be called when it is directly combined with echo() or print().
--Manual


Cytat
Ciekawi mnie kwestia kropki i przecinka. Gdy jest kropka php nie wykonuje metody __toString(), a gdy przecinek wykonuje. Gdy zastosujemy przecinek to czy


Przy echo, gdy rozdzielasz argumenty (echo to nie funkcja) przecinkiem, kazdy z argumentow jest wypisywany osobno -- __toString() zadziala, a przy laczeniu stringow kropka __toStrint() nie jest uruchamiane.
hawk
Taaaaak... this is not a bug, this is a feature. To nic, że kompletnie bez sensu, ale przecież "feature" brzmi dumnie tongue.gif.
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.