PHP-ben van ugyan static kulcsszó, de ettől függetlenül a nem statikus metódusokat is meghívhatjuk statikusan. A legtöbben úgy tudják, hogy egy metódus belsejében annak alapján lehet eldönteni hogy statikus metódushívás történt-e vagy nem, hogy létezik-e a $this változó. Ez azonban sajnos nem mindig igaz.
Kicsit alaposabban körüljártam a dolgot, és írtam egy demót, letölthető innen. A demóban két osztály van, a B osztályban vizsgáljuk a $this-t, az A osztályból pedig B metódusát hívjuk. Nézzük, mit csinál az index.php:
A 7-8 sorban az index.php-ből hívjuk egy B típusú objektum metódusát, az eredmény nem meglepő, a $this B típusú objektum. Ezután a 11. sorban statikusan hívjuk B metódusát, itt a $this értéke NULL, ebben sincs semmi meglepő. A 14. sorban létrehozunk egy A típusú objektumot, a továbbiakban ezzel dolgozunk. Először A egy metódusából hívjuk B metódusát, itt a $this szintén B típusú, ez is magától értetődő.
Az érdekesség most jön. Az A objektum nem-statikusan hívott metódusából hívjuk B metódusát statikusan. Ekkor a B metódusában a $this egy A típusú objektum lesz - a hívó objektum, tehát a metódushívás környezete (!!).
Nem tudom hogy ez bug vagy feature, ha bug akkor ismert-e vagy nem, még nem jártam utána, de lehet hogy fogok. Elég durva, nehezen felderíthető hibák következhetnek ebből a viselkedésből.
Valószínűleg az történik, hogy a futtatókörnyezet metódushívásnál először a $this-t rakja rá a veremre, ha nem statikus a hívás. A metódus pedig innen veszi le a $this-t - vagy amit ott éppen talál - mikor szüksége van rá. Statikus híváskor persze nem kapják meg ezt a paramétert, és mikor szükség lenne rá, akkor leveszik a veremről amit ott éppen találnak. Ez most az A típusú hívó objektum. Ennek némileg ellentmond, hogy ha átadok A metódusának egy paramétert, akkor is az $a objektum lesz a B-ben a $this, pedig ez alapján a paraméternek kellene lenni. Nem tudom, hogy pontosan mi történik, 3 ötletem van:
* az interpreter leveszi az összes paramétert a veremről a metódushívás végén és átteszi máshova (miért tenné?)
* a $this változó nem első, hanem utolsó paraméterként adódik át, így a következő metódus a hívási listán azt találja a verem tetején. Ez se valószínű, az OO kódok nem így szoktak futni.
* a hívási listához tartozó $this objektumok külön veremben vannak, nem együtt a többi paraméterrel. Ez tűnik számomra a legvalószínűbbnek, bár ez is elég furcsa.
Majd ha jutottam valamire a dologgal akkor írok :)
Végül a teszt végén még A metódusát hívjuk statikusan, abból pedig B metódusát szintén statikusan, ilyenkor - nem meglepő - a $this értéke null lesz.
Kicsit alaposabban körüljártam a dolgot, és írtam egy demót, letölthető innen. A demóban két osztály van, a B osztályban vizsgáljuk a $this-t, az A osztályból pedig B metódusát hívjuk. Nézzük, mit csinál az index.php:
A 7-8 sorban az index.php-ből hívjuk egy B típusú objektum metódusát, az eredmény nem meglepő, a $this B típusú objektum. Ezután a 11. sorban statikusan hívjuk B metódusát, itt a $this értéke NULL, ebben sincs semmi meglepő. A 14. sorban létrehozunk egy A típusú objektumot, a továbbiakban ezzel dolgozunk. Először A egy metódusából hívjuk B metódusát, itt a $this szintén B típusú, ez is magától értetődő.
Az érdekesség most jön. Az A objektum nem-statikusan hívott metódusából hívjuk B metódusát statikusan. Ekkor a B metódusában a $this egy A típusú objektum lesz - a hívó objektum, tehát a metódushívás környezete (!!).
Nem tudom hogy ez bug vagy feature, ha bug akkor ismert-e vagy nem, még nem jártam utána, de lehet hogy fogok. Elég durva, nehezen felderíthető hibák következhetnek ebből a viselkedésből.
Valószínűleg az történik, hogy a futtatókörnyezet metódushívásnál először a $this-t rakja rá a veremre, ha nem statikus a hívás. A metódus pedig innen veszi le a $this-t - vagy amit ott éppen talál - mikor szüksége van rá. Statikus híváskor persze nem kapják meg ezt a paramétert, és mikor szükség lenne rá, akkor leveszik a veremről amit ott éppen találnak. Ez most az A típusú hívó objektum. Ennek némileg ellentmond, hogy ha átadok A metódusának egy paramétert, akkor is az $a objektum lesz a B-ben a $this, pedig ez alapján a paraméternek kellene lenni. Nem tudom, hogy pontosan mi történik, 3 ötletem van:
* az interpreter leveszi az összes paramétert a veremről a metódushívás végén és átteszi máshova (miért tenné?)
* a $this változó nem első, hanem utolsó paraméterként adódik át, így a következő metódus a hívási listán azt találja a verem tetején. Ez se valószínű, az OO kódok nem így szoktak futni.
* a hívási listához tartozó $this objektumok külön veremben vannak, nem együtt a többi paraméterrel. Ez tűnik számomra a legvalószínűbbnek, bár ez is elég furcsa.
Majd ha jutottam valamire a dologgal akkor írok :)
Végül a teszt végén még A metódusát hívjuk statikusan, abból pedig B metódusát szintén statikusan, ilyenkor - nem meglepő - a $this értéke null lesz.

