To, že PHP v dohledné době zaznamená výraznou změnu hlavně v oblasti týkající se objektů, je poměrně známá věc. Poprvé o tom padla na Rootu zmínka v rozhovoru Jirky Koska s jedním z autorů PHP, a že objektově orientované programování ve stávajícím PHP je řehole, jste se mohli přesvědčit při čtení seriálu PHP v objetí objektů.
Nová verze PHP s lepšími objekty je na spadnutí. Již delší dobu si ji můžete dokonce vyzkoušet v už druhé alfa verzi na www.php.net. V tomto článku bych se s vámi rád podělil o některé první dojmy.
Objekty se předávají odkazem
Změna v sémantice jazyka je opravdu radikální. Konečně můžete zapomenout na předávání odkazů na objekt (&=) a místo džungle zapomenutých ampersandů se věnovat skutečnému programování.
Dříve se s objektem zacházelo jako s obyčejnou proměnnou, takže když jste objekt přiřadili nějaké proměnné, ve skutečnosti se objekt zkopíroval a každá proměnná od toho okamžiku představovala jiný objekt. Přirozenější prací s objektem je předávání referencí (odkazem), ale to se ve „starém“ PHP muselo explicitně říct právě přiřazovacím příkazem s ampersandem (např. $b &= $a), což byl právě kámen úrazu. Na nutnost použití
& (a to nejen v přiřazovacích příkazech, ale i v definicích hlavičky funkcí, pokud jste chtěli objekty předávat jako parametry nebo je vracet jako výsledky funkcí) se často zapomínalo a vedlo to k mnoha záludným chybám. Od nynějška povinnost použití & odpadá a programování se tak stává mnohem intuitivnějším. Pokud chcete, aby dvě proměnné ukazovaly na jeden objekt, jednoduše napíšete $b = $a a hotovo.
Mějme následující kód:
<?php class Test { var $hodn = 0; function zmen() { echo ++$this->a; } } $a = new Test(); $b = $a; $a->zmen(); $b->zmen(); ?>
Nové chování PHP ilustrují rozdílné výstupy. Zatímco dříve byste dostali 11, nyní to bude očekávaná dvanáctka.
Sjednocené konstruktory
Dříve se musely kontruktory (inicializační metody tříd, které se automaticky volají při vytvoření nového objektu) jmenovat stejně jako třída – podle toho se také v kódu rozpoznaly. Mělo to jednu nevýhodu. Je velmi časté a obvyklé, že se z konstruktorů tříd na některém místě volá i konstruktor nadřazené (rodičovské) třídy. Změna názvu třídy nebo výměna rodičovské třídy, ze které se dědí, ústila v nutnost opravy volání konstruktorů na různých místech kódu.
Nyní se konstruktory všech tříd označují stejně, a to identifikátorem __construct. Pokud chcete volat rodičovskou verzi konstruktoru, jednoduše napíšete parent::__construct(). Pokud u třídy konstruktor nespecifikujete, automaticky se použije konstruktor z nadřazené třídy (nebo prázdný konstruktor, pokud třída žádnou super-třídu nemá).
<?php class Test { function __construct() { echo 'Test::__construct()<br>'; } } class PodTest extends Test { } class PodPodTest extends PodTest { function __construct() { echo 'PodPodTest::__construct(), '; parent::__construct(); } } $a = new Test(); $b = new PodTest(); $c = new PodPodTest(); ?>
(Všimněte si volání konstruktoru nadřazené třídy pomocí identifikátoru parent::. Podobně se dají volat verze rodičovských metod z předefinovávané metody.)
Výstup:
Test::__construct() Test::__construct() PodPodTest::__construct(), Test::__construct()
Destruktory nově
Jestliže máme konstruktory sloužící k inicializaci objektu v době jeho vzniku, můžou se hodit i destruktory určené pro úklidové práce v čase zániku objektu. Ačkoliv nejsou destruktory v objektově orientovaných jazycích nic nového, v připravované verzi PHP se objevují poprvé.
Chcete-li třídě definovat destruktor, vytvořte novou metodu a pojmenujte ji __destruct. Destruktor se samočinně volá těsně před ukončením existence objektu. Objekt je automaticky zničen tehdy, když v programu neexistuje proměnná, která by na něj ukazovala. Tehdy není žádná možnost, jak s objektem pracovat a jádro PHP nemá důvod takový objekt dále držet v paměti.
Programátor má možnost uvolnit objekt z paměti i ručně. Slouží k tomu nový příkaz (a nové klíčové slovo) delete.
Jak a hlavně kdy se destruktory volají, je dobře vidět na následujícím příkladu:
<?php class Test { var $nazev; function __construct($nazev) { $this->nazev = $nazev; } function __destruct() { echo 'Destrukce objektu ' . $this->nazev . '<br>'; } } function bod() { static $n = 0; echo "Kontrolní bod $n<br>"; $n++; } function delej() { $b = new Test('B'); } bod(); $a = new Test('A'); bod(); delej(); bod(); $c = new Test('C'); bod(); delete $c; bod(); $d = new Test('D'); bod(); $d = 'ahoj'; bod(); ?>
Výstupem bude:
Kontrolní bod 0 Kontrolní bod 1 Destrukce objektu B Kontrolní bod 2 Kontrolní bod 3 Destrukce objektu C Kontrolní bod 4 Kontrolní bod 5 Destrukce objektu D Kontrolní bod 6 Destrukce objektu A
…z čehož lze vypozorovat, že objekt A byl odstraněn až na konci skriptu, protože až tam končí oblast platnosti proměnné $a, která na něj udržuje odkaz. Objekt B byl uvolněn ihned po opuštění funkce
delej(), kdy se znepřístupnila držitelka reference na něj – lokální proměnná $b. Rovněž můžeme vidět, že destruktory objektů C a D se volaly ihned po vykonání příkazu delete $c v prvním případě a přiřazení textového řetězce do proměnné $d v případě druhém.
Pokračování příště…