Ci siamo lasciati alla fine dell'anno scorso con un articolo che aveva iniziato a parlare delle novità che sarebbero state introdotte in PHP 5.3. Dopo qualche settimana ci ritroviamo in questa sede per continuare il discorso iniziato precedentemente parlando di altre funzionalità e librerie interessanti introdotte con questa nuova release. Purtroppo, nonostante sia passato un po' di tempo, PHP 5.3 non è stato ancora rilasciato (mentre la Zend continua a concentrarsi sui bugfix di PHP 4 che, a quanto pare, non era poi così stabile e sicuro come molti credevano) ma possiamo comunque testare la nuova versione scaricandola dal sito http://snaps.php.net/.
Senza divagare, accenno solamente al fatto che dal sito che contiene gli snapshots delle ultime o imminenti versioni di PHP potete scaricare la versione 6.0; io consiglio di provare questa versione a chiunque fosse interessato ad allargare la propria conoscenza di PHP e fosse intenzionato a trovarsi sul binario giusto quando, finalmente, PHP 6.0 verrà rilasciato.
Tornando ad argomenti più pertinenti, in questo articolo parleremo di alcune funzionalità interessanti (come il late static binding) che verranno apprezzate parecchio da chi sfrutta la metaprogrammazione.
Late static binding
PHP, continuerò a ripeterlo, è purtroppo rimasto un po' indietro rispetto alle attese dei programmatori per quanto riguarda quelle funzionalità che hanno reso celebre linguaggi quali Python e Ruby. Le motivazioni per cui questi due linguaggi stanno sempre più prendendo piede sono molte altre, ma quello che andremo a trattare è sicuramente uno dei motivi: sto parlando di tutti quei concetti che possono essere racchiusi sotto il paradigma della "metaprogrammazione", e che sono così tanto utilizzati dagli ultimi framework MVC che spopolano adesso.
Finalmente qualcuno ha deciso di iniziare a rendere il linguaggio un po' più libero sintatticamente, ed una delle prima reazioni a questa decisione è l'introduzione del late static binding. Senza dilungarci in una spiegazione tecnica e noiosa, vediamo subito un esempio autoesplicativo.
Partiamo da un semplice esempio scritto in PHP 5:
<?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(); // Output: A;
?>
Come potete notare il comportamento del codice è abbastanza strano: vogliamo che il metodo statico test
restituisca il nome delle classe da cui è stato chiamato, ma siccome nelle versioni di PHP precedenti alla 5.3 un metodo statico viene associato in fase di compilazione alla classe in cui questi è definito, non c'è modo di ottenere nativamente questo comportamento.
Prendiamo invece questo esempio scritto per PHP 5.3:
<?php class A { public static function who() { echo __CLASS__; } public static function test() { // qui viene attivato il late static binding static::who(); } } class B extends A { public static function who() { echo __CLASS__; } } B::test(); // Output: B; ?>
Dopo averlo eseguito ci accorgiamo della "magia": grazie alla keywork static
utilizzata al posto di self
attiviamo il late static binding: il nome viene risolto in fase di esecuzione e quindi la chiamata al metodo statico viene associata correttamente alla classe che abbiamo utilizzato (nel nostro caso B
).
Questo comportamento farà la gioia di molti ma soprattutto di tutti quelli che per molto tempo hanno cercato di emulare gli ActiveRecord in PHP senza riuscirci se non con accrocchi poco eleganti (si pensi a CakePHP per esempio). A dir la verità tempo fa si era discusso per aggiungere un hack allo Zzend Framework che permettesse di eseguire un metodo statico nel corretto scope: l'hack in questione utilizzava la funzione debug_backtrace
per capire quale era la classe a cui "bindare" il metodo, ma fortunatamente è stato scartato perché estremamente lento e veramente poco utilizzabile.
Con l'introduzione di questa funzionalità, è stata aggiunta una funzione che è in grado di restituire il nome della classe da cui viene richiamato un metodo statico: la funzione in questione è get_called_class()
e si comporta allo stesso modo della funzione who
vista precedentemente:
<?php
class A
{
public static function who()
{
echo get_called_class();
}
}
class B extends A {}
B::who(); // Output: B;
?>
Purtroppo questa funzionalità presenta qualche comportamento imprevisto (che per alcuni è un bug, per altri una feature [?!] e per altri ancora è semplicemente il corretto comportamento che dovrebbe avere questa funzionalità in base alle specifiche): facciamo conto di voler fare l'override di una funzione statica in una sottoclasse, magari per aggiungere qualche funzionalità decorativa come un logger. Come sappiamo in PHP per accedere al metodo sovrascritto dobbiamo usare la keyword parent
: il problema però nel nostro caso è che ciò che sembra corretto non lo è per nulla, e ci ritroviamo ad avere un risultato bizzarro:
<?php
class A
{
public static function who()
{
echo __CLASS__;
}
}
class B extends A
{
public static function who()
{
echo "Log ....";
parent::who();
}
}
B::who(); // Output: A!
?>
Se ci si accorge che le specifiche non sono corrette non è il caso di cambiarle senza menare troppo il can per l'aia?
__callStatic e chiamate statiche dinamiche
Probabilmente qualcuno nel team di PHP ha capito che un linguaggio duttile è un linguaggio che funziona. I membri del team hanno quindi hanno deciso di dedicarsi anche a questo e le modifiche si sentono: oltre al late static binding hanno lavorato parecchio sulla duttilità delle chiamate statiche aggiungendo due nuove funzionalità.
In primis il metodo __callStatic
, che se implementato in una classe permette di fare l'overload di una chiamata ad un metodo statico, un po' come il metodo magico __call
. Vediamo un semplice esempio anche se il funzionamento dovrebbe essere chiaro:
<?php class A { static function __callStatic( $method_name, $arguments ) { echo "Metodo chiamato: ", $method_name, "n"; echo "Argomenti:n"; foreach( $arguments as $argument ) { echo "t+ ", $argument, "n"; } } } A::a( "simple", array( "test" )); A::sum( 1, 2, 3, 4, 5 ); ?>
Ovviamente dopo l'aggiunta di questo metodo magico non si poteva continuare a tralasciare il fatto che una delle più duttili funzionalità di PHP (quella che permette di chiamare un metodo o una proprietà di un oggetto utilizzando una variabile stringa che ne contiene il nome) non fosse supportata nel caso in cui si volesse accedere ad un metodo statico di una classe variabile. Fortunatamente anche qui hanno cambiato tendenza e come sempre un esempio vale più di mille parole:
<?php $class = 'MyWonderfulClass'; $class::myWonderfulMethod(); $class::$member; $class::WONDERFUL_CONSTANT; $namespace = 'it'; $namespace::$class::method(); ?>
Entrambe queste funzionalità insieme al late static binding faranno la gioia di molti programmatori. Finalmente vediamo una luce all'orizzonte :)
Conclusione
Eccoci giunti alla fine del nostro articolo. Abbiamo finito per concentrarci solamente sulle nuove funzionalità introdotte che, per un motivo o per l'altro, hanno a che vedere con i metodi e le proprietà statiche. Seppur possa sembrare un caso, in realtà questo non lo è: fino a PHP 5.3 la gestione delle chiamate statiche era enormemente in dietro rispetto al modo in cui venivano gestite le chiamate normali. Quindi lo sforzo degli sviluppatori della versione 5.3 si è concentrato su questo punto per piano piano riuscire ad avvicinare l'attuale versione di PHP (che ricordo a breve diventerà l'unica supportata - quindi consiglio un caldo upgrade nel caso in cui per qualunque motivo a me sconosciuto non l'abbiate ancora fatto) alla prossima major release.
Restano da analizzare alcune librerie aggiunte, come il supporto nativo per MySQL che andrà a sostituire l'attuale porting della libreria mysql_client. Di questo e di altre interessanti novità comunque ci occuperemo in un'altra sede.