ugrás a tartalomhoz

PHP OOP, referencia, tömb (valamelyik ezek közül) probléma

PiG · 2005. Jún. 3. (P), 15.11
Sziasztok!

Akadt egy problémám, amit a következő kóddal próbálnék illusztrálni. Utána, ha addig eljuttok, a kérdéseim:
  1. <?php  
  2. class Bolha{  
  3.     var $_nev;  
  4.     function Bolha($nev){$this->_nev=$nev;}  
  5. }//Bolha  
  6. class Kutya{  
  7.     var $_nev;  
  8.     var $_bolhak=array();  
  9.     var $_bolhaListazo;  
  10.     function Kutya($nev){  
  11.         $this->_nev=$nev;  
  12.         //+++++++++  1. hely +++++++++++++++  
  13.         $this->_bolhaListazo=new BolhaListazo();  
  14.         $this->_bolhaListazo->_kutya=&$this;  
  15.     }  
  16.     function ujBolha(&$bolha){if (is_object($bolha)) $this->_bolhak[]=&$bolha;}  
  17.     function bolhaLista(){  
  18.         //+++++++++  2. hely +++++++++++++++  
  19.         //$this->_bolhaListazo=new BolhaListazo();  
  20.         //$this->_bolhaListazo->_kutya=&$this;  
  21.         $this->_bolhaListazo->lista();  
  22.     }  
  23. }  
  24. class BolhaListazo{  
  25.     var $_kutya;  
  26.     var $_bolhak=array();  
  27.     function BolhaListazo(){}  
  28.     function lista(){  
  29.         echo $this->_kutya->_nev.'<br />';  
  30.         echo 'Bolhák száma: '.count($this->_kutya->_bolhak);  
  31.         $this->_bolhak=&$this->_kutya->_bolhak;  
  32.         foreach ($this->_bolhak as $bolha){  
  33.             echo $bolha->_nev;  
  34.         }  
  35.     }  
  36. }  
  37.   
  38. $bloki=new Kutya('Blöki');  
  39. $bloki->ujBolha(new Bolha('Józsi'));  
  40. $bloki->ujBolha(new Bolha('Pisti'));  
  41. $bloki->ujBolha(new Bolha('Peti'));  
  42. $bloki->bolhaLista();  
  43. ?>  
Kérdéseim a következők:
ha az 1. hely-nél szerepel ez a kód:
$this->_bolhaListazo=new BolhaListazo();
$this->_bolhaListazo->_kutya=&$this;

akkor nem a kívánt eredményt kapom, azaz a bolhák száma 0 lesz és nem lesznek kilistázva a bolháim.
Ha ugyanez a két sor a 2. hely-en szerepel, akkor minden megy.
De bárhol szerepel az előbbi két sor, a echo $this->_kutya->_nev.'<br />'; mindig kiírja a kutya nevét, szóval az objektumreferencia megvan.
Szeretném valamilyen módon működővé tenni úgy, hogy a konstruktorban (1. hely) legyen az a két sor.
Ehhez kéne tanács!

P][G
 
1

PHP 4 bug/feature

bbalint · 2005. Jún. 4. (Szo), 11.48
úgy látszik, a $this nem akar olyan jól működni, mint kéne neki (se régebbi, se vadiúj PHP-val se – próbáltam: 4.3.3 és 4.3.11).
lehet a Zend baja, de Zend 2-ővel (PHP5) működött trendesen ...

bbalint
2

kis debug

bbalint · 2005. Jún. 4. (Szo), 11.57
ráadásul, a debug_zval_dump() függvény is azt mondja a $kutyára, hogy csak egy referencia van rá, tehát ...

az a sejtésem, hogy a $this csak ideiglenesen létezik, aztán mindenképp megszünteti a változót,

a PHP5 így jobban ojjektum-orientált, aztán a $this tényleg az aktuális objektumot jelenti.

bbalint
3

a miért

bbalint · 2005. Jún. 4. (Szo), 12.58
Zend kettőben nem "hack"ként van megoldva a $this, hanem külön van programozva:
PHP5 esetén van egy "globális" this (Execute Gloabls, EG(this)), amit mindig "frissít"
PHP4-ben "csak" egy $this változót hoz létre (és szüntet meg) a metódus-hívás elején/végén.

bbalint
4

recode

bbalint · 2005. Jún. 4. (Szo), 13.01
megírtam ugyanezt, viszont nem úgy, hogy a BolhaListazora, "ráerőszakolom" a kutyát, hanem a konstruktorának adom át:
  1. <?php  
  2.  class állat{  
  3.   var $nev;  
  4.     
  5.   function állat($nev){  
  6.    $this->nev = $nev;  
  7.   }  
  8.  }  
  9.    
  10.  class bolha extends állat{  
  11.   function bolha($nev){  
  12.    $this->állat($nev);  
  13.   }  
  14.  }  
  15.    
  16.  class ló extends állat{  
  17.   function ló($nev){  
  18.    $this->állat($nev);  
  19.   }  
  20.  }  
  21.    
  22.  class bolhalistázó{  
  23.   var $kutyuli;  
  24.     
  25.   function bolhalistázó(&$kutyuli){  
  26.    if(get_class($kutyuli) == 'kutyuli')  
  27.     $this->kutyuli =& $kutyuli;  
  28.    else  
  29.     print('Ő nem egy kutyuli, hanem egy \''.get_class($kutyuli).'\', te!<br/>');  
  30.   }  
  31.     
  32.   function lista(){  
  33.    print('Kutyuli neve: '.$this->kutyuli->nev.'<br/>');  
  34.      
  35.    if($this->kutyuli->bolhák){  
  36.     $bolhák = array();  
  37.     foreach($this->kutyuli->bolhák as $bolha)  
  38.      $bolhák[] = $bolha->nev;  
  39.       
  40.     print('Ő bolhás, bolhái: '.implode(', '$bolhák).'<hr/>');  
  41.    }else  
  42.     print('Ő nem bolhás<hr/>');  
  43.   }  
  44.  }  
  45.    
  46.  class kutyuli extends állat{  
  47.   var $bolhák;  
  48.   var $bolhalistazo;  
  49.     
  50.   function kutyuli($nev){  
  51.    $this->állat($nev);  
  52.    $this->bolhák = array();  
  53.    $this->bolhalistázó =& new bolhalistázó($this);  
  54.   }  
  55.     
  56.   function bolha(&$bolha){  
  57.    if(get_class($bolha) == 'bolha')  
  58.     $this->bolhák[] =& $bolha;  
  59.    else  
  60.     print('Ő nem egy bolha, hanem egy \''.get_class($bolha).'\'!<br/>');  
  61.   }  
  62.     
  63.   function bolhalista(){  
  64.    $this->bolhalistázó->lista();  
  65.   }  
  66.  }  
  67.    
  68.  $Döme =& new kutyuli('Döme');  
  69.  $Döme->bolhalista();  
  70.    
  71.  $Kincsem =& new ló('Kincsem');  
  72.  $Józsi =& new bolha('Józsi');  
  73.  $Pisti =& new bolha('Pisti');  
  74.  $Peti =& new bolha('Peti');  
  75.    
  76.  $Bolhás =& new kutyuli('Bolhás');  
  77.    
  78.  $Bolhás->bolha($Kincsem);  
  79.  $Bolhás->bolha($Józsi);  
  80.  $Bolhás->bolha($Pisti);  
  81.  $Bolhás->bolha($Peti);  
  82.    
  83.  $Bolhás->bolhalista();  
  84. ?>  
így nekem szépen és jól működik:
Kutyuli neve: Döme
Ő nem bolhás
--
Ő nem egy bolha, hanem egy 'ló'!
Kutyuli neve: Bolhás
Ő bolhás, bolhái: Józsi, Pisti, Peti
--


bbalint
5

Köszi!

PiG · 2005. Jún. 4. (Szo), 18.40
Köszi a foglalkozást, a megoldást még tanulmányozom!
Egyébként azért még utánanéztem a dolognak, és több helyen írnak erről a viselkedésről, azaz a konstruktorban a $this-re való referencia létrehozásáról. Érdeklődőknek ajánlom! A probléma lényege:
... The answer lies in when the object was first created. In PHP 4, the new statement does not return a reference by default. Rather, when the $myvar object was created, it returned a copy separate from the one referenced by the $myref variable. Thus, because they are separate instances of the same object, their variables are completely independent. To overcome this and achieve the desired result, we use the reference-binding operator when creating the objects...

Tehát az általam írt kódban a
$bloki=new Kutya('Blöki');

erre írva:
$bloki=&new Kutya('Blöki');

már működőképes!
Egyébként, ha valakinél elérhető a PHP Anthology c. könyv, abban direkt ajánlják, hogy a new elé mindig tegyünk &-t (46. oldal)!
Let’s look at some other situations in which you might need to use references…

// Make sure $myObject is a reference to
// the variable created by the new keyword
$myObject = &new MyClass();


This looks odd at first, but remember, a variable created by the new keyword is being passed here—even if you can’t see it. The reference operator saves PHP from having to create a copy of the newly-created object to store in $myObject.

Ezek után úgy teszek!

P][G
6

kódolási stílus

bbalint · 2005. Jún. 4. (Szo), 22.01
hát, én nemt'om. én azért teszek és-jelet (ampershand) a new-nál, mivel a PHP manualba és PHP forráskódban úgy magyarázták ki, hogy amikor objektumot hozol létre és ad az ember értékül egy változónak:
  1. <?php $változó = new StdClass(); ?>  
a PHP4 először létrehoz egy StdClass objektumot majd annak értékét teszi bele a változóba, majd az objektumot megszünteti.
  1. <?php $változó =& new StdClass(); ?>  
ilyenkor pedig nem a létrejött objektum értékét, hanem az objektumra egy referenciát (magát az objektumot) teszi bele a változóba.

szóval, utóbbi változattal, memóriát, "időt" spórolok ezért használom.

bbalint
7

Na igen

PiG · 2005. Jún. 4. (Szo), 22.57
Hát ezek után már én is fogok...

P][G
11

nem stílus kérdése

Hodicska Gergely · 2005. Jún. 6. (H), 11.50
Egyszerűen a feladat szabja meg. Ha például a konstruktorban egy tömben eltárolod az elemet egy collection tagjaként (szülő tárolja a gyerekeit), akkor csak & alkalmazásával tudod elérni, hogy a tömbben tárolt, és a visszaadott objektum ugyanaz legyen. Plusz ilyen esetekben az is nagyon fontos, hogy ha van egy függvényed, amivel a szülőtől elkérhető a gyerek, akkor a definiciójána is legyen a függvénynév előtt &, különben az esetleges változtatások nem jelennek meg a szülő áltál tárolt verzióban.

Érdemes ezt a témát egyszer mindenkinek szépen lerendezni, kipróbálni a különböző lehetőségeket, mert ellenkező esetben nagyon aljas, nehezen debugolható hibákat vihetünk programjainkba.


Felhő
8

Újabb 'probléma'

PiG · 2005. Jún. 6. (H), 09.04
Sziasztok!

A bolhás példánál maradva:
  1. <?php  
  2. class Bolha{  
  3.     var $_nev;  
  4.     function Bolha(){}  
  5. function setNev($nev){$this->_nev=$nev;}  
  6. }//Bolha  
  7. class Kutya{  
  8.     var $_bolhak=array();  
  9.     function Kutya(){}  
  10.     function ujBolha(&$bolha){$this->_bolhak[]=&$bolha;}  
  11. }//Kutya  
  12. $bloki=new Kutya();  
  13. for ($i=0;$i<5;$i++){  
  14.     $bolha=&new Bolha();  
  15.     $bolha->setNev('bolha_'.$i);  
  16.     $bloki->ujBolha($bolha);  
  17. }  
  18. ?>  
A gondom a következő:
function ujBolha(&$bolha){$this->_bolhak[]=&$bolha;}

Így adunk új bolhát a kutyához. Hogy elkerüljük az esetlegesen bazi nagy objektumok másolgatását, ezért referenciák szerepelne. bbalint is így írta :-)
Csakhogy a for ciklusban az értékadás nem fog működni, mivel a végén lesz 5 darab bolha_4 nevű kutyánk. (a kiíratást lespóroltam a kódbból, de így lesz). Ennek oka egyértelműen a referenciák használata.
Megoldás lenne, ha lenne a bolha objektum konstruktorában adnánk meg annak nevét is és így hajtanánk végre a ciklust:
for ($i=0;$i<5;$i++){
$bloki->ujBolha(new Bolha('bolha_'.$i));
}

Viszont előfordulhat, hogy a $bolha létrehozása után még egy csomó más tulajdonságát is be szeretnénk állítani. Ekkor nem tehetjük meg, hogy minden esetre felkészülve alkotjuk meg a konstruktor, mert az már átláthatlan lenne. Marad az, hogy nem használunk referenciát.
Így van, vagy valamit nagyon elnéztem?
(PHP 4.x)

P][G
9

ööö

bbalint · 2005. Jún. 6. (H), 10.04
hát, izé. nekem a fenti for(;;);-os kód rendesen bolha_0, bolha_1, bolha_2, bolha_4 és bolha_5 Bolhákat adogatja rá a Kutyára ...

ami talán (biztos) segítség, hogy a ciklusban nem egy változóba teszed bele az összes Bolhát, hanem egy "Bolhás" tömbbe:
  1. <?php  
  2.   $bolhak = array();  
  3.   for($i = 0; $i < 5; ++$i){  
  4.     $bolhak[$i] =& new Bolha();  
  5.     $bolhak[$i]->setNev('bolha_'.$i);  
  6.       
  7.     $bloki->ujBolha($bolhak[$i]);  
  8.   }  
  9.   // itt akár nyugodtan unset()ni is lehet a $bolhak változót; az elemeikre van rendesen két (vagy több) referencia is  
  10. ?>  
szóval, valami ilyesmi.

bbalint
10

Tömb - igen

PiG · 2005. Jún. 6. (H), 10.28
A tömbös megoldásra én is gondoltam.
A 'kutya-bolhásítás' csak egy leegyszerűsítése egy bonyolultabb problémának, ami nekem nem volt hajlandó működni. Mint írod, neked alapból működik a fenti kód. Úgy látszik az 'eredeti' kódban van még valahol máshol gubanc.
Egyébként köszi!

P][G