V dalším článku něco o late static bindings (LSB), věc, která by objektové programování v PHP měl a posunout opět o něco dál. V podstatě lze říci, že LSB je o statické dědičnosti, která v PHP nefungovala tak, jak by si člověk zvyklý z jiných jazyků představoval. Lecos vypadá v PHP 5.3 lépe, ale neradujme se předčasně.
Jedna z prvních zmíněk o LSB vyplula na povrch zejména při prezentaci o připravovaném Zend Frameworku - viz Zend Webcast z roku 2005. Z komunity se ozvaly hlasy, že implementace ActiveRecordu prezentovaná na konferenci není v PHP možná. V PHP 5.3 by se to mělo změnit, ale pozor, neradujme se předčasně.
Late static bindings - základy
Nebudu se zmiňovat do detailů o co v LSB jde, už nyní je možné nakouknout do manuálu. Nejlépe si to ukážeme na příkladu.
<?php
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
self::who();
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test(); // Vypise: A
?>
Následující kód je klasickou ukázkou toho, když se někdo pokusí o statickou dědičnost pomocí self
,
self
a __CLASS__
nás odkáže na třídu ke které volaná metoda patří podle definice.
V našem případě tedy B::test() zavolá A::who(), nikoli B::who(), jak bychom se mohli domnívat. V PHP 5.3 to již bude možné, následující kód nám již vypíše písmeno B.
<?php
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
static::who(); // Tady doslo ke zmene.
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test(); // Vystup: "B"
?>
Pomocí static::who()
se uskuteční vazba na správnou metodu dle aktuální reference až v době běhu
skriptu, takže se opravdu zavolá B::who(). Teď bychom ještě uvítali takovéto polymorfní runtime chování i pro __CLASS__
,
abychom nemuseli pořád opisovat metodu who() do všech zděděných tříd, bez toho by
chování LSB nebylo úplné. Ukážeme si to na příkladě již zmiňovaného ActiveRecordu.
Základní koncepce ActiveRecordu vypadá takto:
<?php
class ActiveRecord {
public static function findByPk($id)
{
// implementace
}
}
class Blog extends ActiveRecord {}
Blog::findByPk(1);
?>
Má to fungovat tak, že jméno zděděné třídy - Blog - bude určovat tabulku, nad kterou se budou provádět dané operace, třeba hledání záznamu dle primárního klíče. V PHP až do verze 5.2 nebylo možné (resp. byl nějaký hack přes debug_backtrace()) získat staticky název třídy, ke která právě volaná statická funkce patří. V PHP 5.3 to již půjde:
<?php
class ActiveRecord
{
public static function getClass()
{
return __CLASS__;
}
public static function findByPk($id)
{
$calledClass = static::getClass();
//tady se uplatni LSB
}
}
class Blog extends ActiveRecord
{
public static function getClass()
{
return __CLASS__;
}
}
Blog::findByPk(1);
?>
V proměnné $calledClass bude naše očekávané Blog. Hurá, ale opět tady máme neustále přepisování getClass() v potomcích. PHP 5.3 nám nabídne toužebně očekávanou funkci get_called_class(), která nám poskytne kýžený polymorfní runtime prostředek pro získání názvu třídy.
<?php
class ActiveRecord
{
public static function findByPk($id)
{
$calledClass = get_called_class();
//LSB pro get_class() / __CLASS__
}
}
class Blog extends ActiveRecord {}
Blog::findByPk(1);
?>
Není všechno zlato, co se třpytí
To vypadá zdánlivě výborně, teď bychom si do naší třídy Blog, chtěli přidat třeba kód pro logování, uděláme to nějak takto:
<?php
class ActiveRecord
{
public static function findByPk($id)
{
$calledClass = get_called_class();
}
}
class Blog extends ActiveRecord
{
public static function findByPk($id)
{
//kod pro logovani
// Then the parent should do the magic.
parent::findByPk($id);
}
}
Blog::findByPk(1);
?>
A tady právě narazíme, v $calledClass bude ActiveRecord, nikoli Blog. A to jako proč??
Chyba je ve volání parent::findByPk()
. Parent:: nám totiž dá jednoznačnou
referenci na ActiveRecord a LSB se neuplatní! Hm, dá se to sice obejít:
<?php
class ActiveRecord
{
public static function findByPk($id, $calledClass = null)
{
//dostaneme nazev tridy v parametru
if ($calledClass === null) {
$calledClass = get_called_class();
}
}
}
class Blog extends ActiveRecord
{
public static function findByPk($id)
{
//kod pro logovani
//hack - volame parent s nazem nasi tridy
parent::findByPk($id, __CLASS__);
}
}
Blog::findByPk(1);
?>
Ale to je zase hack. Uvidíme, jak to dopadne, u mě tedy začíná počáteční nadšení pomalu opadat.
Komentáře (4)
třpytit se je vyjmenované slovo a píšeme ho s tvrdým Y :D
Opraveno, já jsem na tom s tou češtinou dost bídně, o AJ už ani nemluvě, ach jo.
au, to bolí:
zvyklí = zvyklý (podle vzoru mladý)
Díky, ja sem prostě dobra češtin, opraveno
Komentáře jsou uzavřeny.