This\is\lame\r\n … è un namespace, almeno in PHP 5.3

Finalmente la lunga saga dell’introduzione del supporto per i namespace in PHP 5.3 è giunta, forse, al termine nelle ultime ore. Finalmente perché la discussione è andata avanti per diversi mesi, con lunghi thread su php.internals aperti in maniera pressoché perpetua ma spesso abbastanza inconcludenti. Vi anticipo fin da subito il finale della storia, ma vi avviso che secondo me non è dei migliori: è stato deciso di sostituire :: con il carattere \ per identificare la separazione dei namespace.

Il problema principale della scelta di :: come separatore per i namespace, secondo i core developer di PHP, risiede nel fatto che in alcuni casi può crearsi un’ambiguità non risolvibile dall’interprete poiché lo stesso :: viene usato anche per accedere ai metodi di classe statici. Ecco un esempio pratico:


1 // -------------------------------------- 2 // primo caso: Classe::methodoStatico() 3 4 class Foo { 5 public static function bar() { 6 echo "Foo::bar()"; 7 } 8 } 9 10 Foo::bar(); 11 12 // -------------------------------------- 13 // secondo caso: Namespace::funzione()14 15 namespace Foo; 16 17 function bar() { 18 echo "Foo::bar()"; 19 } 20 21 Foo::bar();

L’ambiguità delle righe 10 e 21 risulta evidente, tanto che anche l’interprete in questo caso non saprebbe come comportarsi. Per questo motivo gli sviluppatori di PHP hanno pensato di aggirare il problema cambiando appunto in \ il separatore per i namespace.

Se doveste chiedermi cosa ne penso vi direi che, così di primo acchito e pur non avendo da offrire soluzioni in merito alla questione, per conto mio l’idea non è stata delle migliori. Senza alcun dubbio c’erano dei problemi, ma se da un lato è vero che così vengono risolte possibili ambiguità, con la scelta di \ vedo l’introduzione di bug subdoli dovuti alla mancata attenzione nell’uso della quotazione singola o, in caso contrario, un errato escaping.


$singleQuote = new ReflectionClass('foo\bar\className'); $doubleQuoteWithEscape = new ReflectionClass("foo\\bar\\$classNameInVar");

Ad ogni modo vi fornisco alcuni link la cui lettura può tornarvi utili per farvi un’opinione sulla decisione presa:

Ancora su lambda e closure in PHP

Anche se a pochi giorni di distanza, ho pensato di tornare a parlare di funzioni lambda e closure in PHP approfittando di un nuovo aggiornamento della RFC (con relativa patch che implementa quanto descritto) in cui sono state finalizzate alcune idee e sono stati introdotti alcuni nuovi concetti.

Prima di tutto è stata presa una decisione per quanto riguarda la sintassi dichiarativa, infatti precedentemente erano state formulate due possibilità. La prima prevedeva la cattura delle variabili esterne alla chiusura tramite una nuova keyword creata ad hoc, lexical:


$lambda = function($arg1, $arg2) { lexical $reference, &$valueByRef; // ...};

La seconda, quella che è stata effettivamente scelta, prevede invece il riutilizzo della keyword già esistente use:


$lambda = function($arg1, $arg2) use ($reference, &$valueByRef) { // ...};

Qualcuno preferiva la prima opzione e personalmente ero dello stesso avviso, ma alla fine la scelta è stata dettata dall’intenzione di raggiungere un compromesso in maniera tale da ottenere le stesse funzionalità mantenendo la consistenza con l’attuale semantica di PHP e senza intaccare la retrocompatibilità.

La prima estensione nella RFC riguarda invece l’utilizzo della keyword static per stabilire se importare o meno $this all’interno di una chiusura definita all’interno del metodo di una classe. Non effettuare l’importazione di $this all’interno di una chiusura quando non necessario permette di ottenere un risparmio non solo in termini di velocità d’esecuzione, anche si tratta di differenze veramente minimali, ma anche e soprattutto di memoria, evitando che delle long-lived closure possano tenere in vita inutilmente le istanze delle classi in cui esse sono state create. La chiusura che cattura $this infatti impedisce che il refcount interno per l’oggetto che referenzia possa arrivare a 0 e ciò ne comporta l’esclusione dal processo automatico di raccolta delle risorse che viene effettuato dal garbage collector. Vediamo un esempio pratico con tanto di risultato:


class NRK { private $_whoAmI = __CLASS__; public function foo() { $method = __METHOD__; return function() use ($method) { return sprintf("created in %s (w/ \$this):\n%s", $method, print_r($this, true) ?: 'null' ); }; } public function bar() { $method = __METHOD__; return static function() use ($method) { return sprintf("created in %s (w/o \$this):\n%s", $method, print_r($this, true) ?: 'null' ); }; } } $nrk = new NRK(); $fun1 = $nrk->foo(); $fun2 = $nrk->bar(); echo $fun1(), "\n", $fun2(); /*created in NRK::foo (w/ $this): NRK Object ( [_whoAmI:NRK:private] => NRK ) created in NRK::bar (w/o $this): null */

In realtà trovo che sarebbe molto più comodo e pulito avere $this importata in automatico nel caso essa venga esplicitamente richiamata nel corpo della closure, per il compilatore non sarebbe difficile accorgersene ed agire di conseguenza, ad ogni modo non mi piace l’uso della keyword static dal momento che rende ancora più verbosa la definizione di una chiusura.

Proseguendo con le nuove funzionalità, nell’ottica di una maggiore integrazione con PHP sono state estese le classi ReflectionMethod e ReflectionFunction implementando il metodo getClosure() che permette di ottenere una funzione lambda generata dinamicamente partendo da una funzione o da un metodo di istanza/classe:


class NRK { public function instanceMeth() { return __METHOD__; } public static function staticMeth() { return __METHOD__; } public static function methWithArgs($a, $b) { return $a + $b; } } $instance = new NRK(); $class = new ReflectionClass('NRK'); $fun1 = $class->getMethod('instanceMeth')->getClosure($instance); $fun2 = $class->getMethod('staticMeth')->getClosure(); $fun3 = $class->getMethod('methWithArgs')->getClosure(); echo $fun1(), "\n", $fun2(), "\n", $fun3(5, 3); /*NRK::instanceMeth NRK::staticMeth 8 */

Per finire, un’altra novità ispirata dall’implementazione dell’oggetto Closure riguarda la generalizzazione del concetto di oggetto invocabile attraverso l’implementazione di un nuovo metodo magico, __invoke(). Un esempio vale più di mille parole:


class CallableObject { public function __invoke () { return 'Guess what?'; } } $callable = new CallableObject(); $notCallable = new stdClass(); echo '$callable is ', is_callable($callable) ? '' : 'not ', "a callable object\n"; echo '$notCallable is ', is_callable($notCallable) ? '' : 'not ', "a callable object\n"; echo $callable(); echo $notCallable(); /*$callable is a callable object $notCallable is not a callable object Guess what? Fatal error: Function name must be a string in [...] */

Tirando le somme lo stato dell’implementazione di lambda e closure in PHP sembrerebbe essere in uno stato particolarmente avanzato al punto tale che lo stesso Andi Gutmans su php.internals ha chiesto, soprattutto ai release manager, di pensare se sia effettivamente possibile includere tutto questo già in PHP 5.3. In effetti PHP 5.3 sarà già ricco di novità di per sè (namespace e collaterali, LSB, __callStatic, etc) quindi l’introduzione di un’altra nuova feature è da valutare bene, tuttavia vista la buona salute della proposta e dei lavori per implementarla sarebbe un peccato dover aspettare un altro anno o più. Intanto però c’è anche chi si chiede: ma alla fine le novità di PHP 6 saranno rappresentate solo dal supporto per Unicode?

Lambda e closure in PHP 5.3? Forse sì, forse no, ma intanto proviamo.

Negli ultimi giorni sulla mailing list di sviluppo di PHP si sta discutendo sulla possibilità di estendere il linguaggio in maniera tale da introdurre i concetti di funzioni lambda e di closure tanto cari a molti linguaggi dinamici come Ruby, Python, JavaScript, etc. In realtà si potrebbe quasi considerare come un ritorno di fiamma, già in passato ci furono discussioni simili anche se poco convinte e poco convincenti, tuttavia questa volta si intravede la possibilità di sfociare in una decisione furba nonostante non ci sia particolare interesse nell’implementazione da parte di alcuni dei core-dev (tanto per cambiare). Attualmente il tutto ruota intorno a una RFC che, oltre a mettere in evidenza alcuni scenari sull’utilizzo di funzioni lambda e chiusure e riportare i dettagli implementativi nell’engine di Zend, allega alcune patch con alcune piccole varianti sul tema da applicare ai sorgenti nei rami di sviluppo di PHP 5.3 e PHP 6.0 per poter toccare con mano quanto si sta discutendo.


$fun = function($name = 'guy') { echo "Hi $name!"; }; printf("Tipo: %s\n", get_class($fun)); // Type: Closureprintf("Callable: %s\n", is_callable($fun)); // Callable: 1

Come possiamo notare, è stato implementato un nuovo tipo in ZE denominato Closure. Closure risulta essere invocabile, alla stessa stregua di funzioni e metodi:


echo $fun(); // Hi guy!echo $fun('NRK'); // Hi NRK!

Il primo pensiero va a questo punto a tutte quelle funzioni in PHP che accettano delle callback ma in cui comodità ed espressività vengono pesantemente minate dall’attuale modus operandi del linguaggio che prevede due opzioni:

  • definire una funzione tradizionale da usare come argomento callback ma sotto forma di stringa (il nome della funzione stessa), dando quindi origine a potenziali bugs galore.
  • creare una funzione a runtime usando create_function, opzione che oltre a risultare poco elegante alla vista rischia di introdurre un’altra buona dose di problemi in termini di manutenibilità del codice.

Ecco quindi un confronto tra come è possibile scrivere codice oggi e la possibilità di sviluppare la stessa soluzione attraverso l’utilizzo delle funzioni lambda:


$array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); /* senza funzioni lambda */function reducer($a, $b) { return $a * $b; } $res = array_reduce($array, 'reducer', 1); // 3628800/* senza funzioni lambda */$res = array_reduce($array, create_function('$a, $b', 'return $a * $b;'), 1); // 3628800/* con funzion lambda - versione "lunga" */$reducer = function($a, $b) { return $a * $b; }; $res = array_reduce($array, $reducer, 1); // 3628800/* con funzion lambda - versione "breve" */$res = array_reduce($array, function($a, $b) { return $a * $b; }, 1); // 3628800

Il fatto che l’oggetto Closure sia a tutti gli effetti visibile in userland permette anche di sfruttare l’hinting dei tipi nel caso volessimo limitare a questo tipo di oggetto alcuni parametri delle nostre funzioni:


function iterate($array, Closure $fun) { foreach ($array as $element) $fun($element); } iterate($array, 'function'); // genera un Catchable Fatal Error, Argument 2 passed to // iterate() must be an instance of Closure, string given

Essendo Closure una vera e propria classe (che, per la cronaca, attualmente espone solo un metodo __toString() seppur vuoto) potrebbe implementare in un ipotetico futuro diverse funzionalità interessanti e chissà, potrebbe anche essere serializzabile. Ad ogni modo, dopo le funzioni lambda passiamo a vedere le closure:


$array = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); $sum = 0; $fun = function($element) { lexical &$sum; // ... $sum += $element; }; array_walk($array, $fun); echo $sum; // 55

Possiamo notare l’introduzione della keyword lexical (c’è anche la variante use a seconda della patch che si decide di utilizzare) che ci permette di catturare i riferimenti alle variabili esterne al corpo della chiusura. L’introduzione di lexical è dovuta al fatto che con questa implementazione non viene catturato tutto l’ambiente lessicale in cui viene creata la chiusura, come accade invece in altri linguaggi, per motivi legati principalmente alle regole di scope di PHP. Usando lexical possiamo pertanto decidere quali variabili catturare, ma è da notare che attualmente per i tipi numerici o stringa, che per default in PHP sono passati per valore, è necessario creare manualmente un riferimento anteponendo l’ampersand alla variabile catturata, come da esempio. Tecnicamente la cattura per riferimento dovrebbe essere di default anche per questi tipi e lo stesso creatore della patch è del medesimo avviso, tanto più che il terribile global si comporta nello stesso modo creando sempre riferimenti.

In linea di massima l’introduzione di funzioni lambda e closure in PHP sarebbe interessante e in certe situazioni renderebbe la scrittura dei programmi un po’ meno arrabattata con la possibilità di ottenere codice molto più facilmente generalizzabile e più verificabile/manutenibile. Provare è abbastanza facile: basta avere un ambiente configurato per compilare PHP, scaricare i sorgenti di PHP 5.3 o 6.0 dal repository CVS (sì, usano ancora CVS…), applicare una delle patch disponibili, compilare e installare.

Pre e post hook a funzioni o metodi in PHP

Curiosando qua e là in PECL ho scoperto una nuova estensione per PHP abbastanza interessante, funcall, che permette di aggiungere degli hook a funzioni o metodi prima o dopo la loro esecuzione, invocandoli in automatico. Per esempio:


function funzione($foo, $bar) { echo "Foo: $foo, Bar: $bar"; } function pre_funzione($args) { // $args -> argomenti passati alla funzione (come array)} function post_funzione($args, $result, $time) { // $args -> argomenti passati alla funzione (come array) // $result -> contiene il valore ritornato della funzione // $time -> tempo impiegato nel processare la funzione} fc_add_pre('funzione','pre_funzione'); fc_add_post('funzione','post_funzione'); funzione('lol', 'wut');

In questo caso pre_funzione e post_funzione verranno invocate automaticamente chiamando normalmente funzione e, ovviamente, possono essere attaccate più di una pre / post callback con la possibilità di ottenerne l’elenco a runtime sotto forma di array attraverso la funzione fc_list. La prima cosa che può venire in mente a questo punto è il pattern design by contract e in effetti questa estensione potrebbe tornare utile in questo senso, anche se ammetto che è tutto da verificare (leggasi: voglio provare bene prima di esserne sicuro). Rimane comunque da verificare la stabilità di questa estensione, dal momento che parrebbe recente, e quanto impatti in termini prestazionali un suo eventuale utilizzo, ad ogni modo ho già un caso in cui potrebbe tornarmi utile. Giusto per andare leggermente off-topic: in Ruby 2.0 questa funzionalità sarà supportata nativamente mentre ad oggi è disponibile solo attraverso l’utilizzo di moduli come DesignByContract.

Microbenchmark: Ruby 1.9.0 contro tutti (… non proprio…)

Il rilascio di Ruby 1.9 è previsto intorno a Natale e con il passare dei giorni la curiosità dei rubyisti si fa sempre più intensa. Stuzzicato da un microbenchmark pubblicato da Antonio Cangiano sul suo blog in cui ha messo a confronto la velocità di esecuzione di Ruby 1.8.6, Ruby 1.9.0 e Python 2.5.1 usando volutamente una funzione ricorsiva che calcolasse i primi 35 numeri della successione di Fibonacci, ho pensato che sarebbe stato interessante fare lo stesso test con i suoi script ma con condizioni diverse. A differenza di Antonio, che ha eseguito il suddetto microbenchmark sul suo MacBook Pro con OS X 10.5, ho pensato di effettuare i miei test su Windows chiamando in causa a questo punto anche IronRuby e, tanto per fare, anche PHP e Lua. Mi sarebbe piaciuto provare anche JRuby, ma purtroppo non ho nessuna esperienza e non saprei nemmeno da dove cominciare.

  • Configurazione: Microsoft Windows XP SP2 (x86) – AMD Athlon64 3700+ (2,2 GHz) – 2 GB RAM
  • Dettaglio versioni:
    • Ruby 1.9.0 (SVN rev. 14067 compilata con VC++ 2005)
      ruby 1.9.0 (2007-12-01 patchlevel 0) [i386-mswin32_80]
    • Ruby 1.8.6 (Ruby OneClick Installer 1.8.6-25)
      ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]
    • IronRuby 1.0 pre-alpha (SVN rev. 65 compilata con CSC di VS 2005)
      IronRuby Pre-Alpha (1.0.0.0) on .NET 2.0.50727.832
    • Python 2.5.1 (Standard Python 2.5.1 Windows Installer)
      Python 2.5.1 (r251:54863, Apr 18 2007, 08:51:08) [MSC v.1310 32 bit (Intel)]
    • Python 3.0a1 (Standard Python 3.0a1 Windows Installer)
      Python 3.0a1 (py3k:57844, Aug 31 2007, 16:54:27) [MSC v.1310 32 bit (Intel)]
    • PHP 5.2.5 (Binari ufficiali per Win32 – versione TS – no debug – Zend Engine 2.2.0)
      PHP 5.2.5 (cli) (built: Nov  8 2007 23:18:51)
    • PHP 5.3.0 (Snapshot ufficiale – versione TS – no debug – Zend Engine 2.3.0)
      PHP 5.3.0-dev (cli) (built: Dec  1 2007 04:19:04)
    • PHP 6.0.0 (Snapshot ufficiale – versione TS – no debug – Zend Engine 3.0.0-dev)
      PHP 6.0.0-dev (cli) (built: Dec  1 2007 02:04:58)
    • Lua 5.1.2 (LuaBinaries per Win32 di LuaForge)
      Lua 5.1.2  Copyright (C) 1994-2007 Lua.org, PUC-Rio

Le build di Ruby 1.9 e IronRuby sono state effettuate direttamente da me allineandomi questa mattina con i trunk nei rispettivi repository SVN, nel primo caso senza badare a ottimizzazioni particolari e nel secondo caso usando la modalità release. Per le versioni di sviluppo di PHP ho prelevato questa mattina da snaps.php.net i binari ufficiali e ho effettuato i test con la configurazione (php.ini) di default, così come per la versione stabile 5.2.5. Lo script per Python è stato leggermente modificato per la versione 3.0 dal momento che print è diventata una funzione e funziona diversamente dalla versione 2.5.

Gli script usati per Ruby, IronRuby e Python sono gli stessi di Antonio, aggiungo solo le mie varianti Lua e PHP (occhio, si tratta solamente di un becero port ai rispettivi linguaggi). Ecco come si presenta la versione in Lua:


function fib(n) if n == 0 or n == 1 then return n end return fib(n - 1) + fib(n - 2) end for n = 0, 35 do print("n=" .. n .. " => " .. fib(n)) end

Ed ecco invece la versione in PHP:


function fib($n) { if ($n == 0 || $n == 1) return $n; return fib($n - 1) + fib($n - 2); } for ($i = 0; $i <= 35; $i++) echo "n=", $i, " => ", fib($i), "\n";

I tempi riportati di seguito rappresentano la media calcolata sui risultati di 6 esecuzioni scartando la prima. Ecco i risultati ottenuti:

Ruby 1.9.0 : 21,123s
Ruby 1.8.6 : 94,736s
IronRuby : 26,123s
Python 2.5.1 : 33,827s
Python 3.0a1 : 48,718s
PHP 5.2.5 : 91,672s
PHP 5.3.0 : 77,813s
PHP 6.0.0 : 73,969s
Lua 5.1.2 : 14,912s

Ora un po’ di considerazioni:

  • Ruby 1.9 è dannatamente più veloce di Ruby 1.8.6. Punto.
  • IronRuby non è nemmeno uscito dallo stato di pre-alpha e già offre prestazioni molto buone, vicine a quelle di Ruby 1.9 che invece si sta avvicinando al rilascio. Ottimo!
  • Mi incuriosisce la differenza tra i tempi di Ruby 1.8.6 registrati da Antonio su OS X e quelli registrati da me su Windows: 159 secondi rispetto a 95 secondi mi sembra un abisso, soprattutto perché immagino che un Core 2 Duo 2.2 GHz sia più performante di un Athlon64 3700+ 2.2GHz.
  • Python 3.0 è più lento di Python 2.5.1, ma la versione 3.0a1 risale a fine Agosto per cui sarebbe il caso di ripetere lo stesso benchmark con una versione più aggiornata.
  • PHP sembra migliorare abbastanza con il passaggio da Zend Engine da 2.2.0 a 2.3.0, ma comunque le prende alla grande da Ruby 1.9 e Python.
  • Lua li straccia senza alcun ritegno.
  • Ordine di arrivo: Lua 5.1.2, Ruby 1.9.0, IronRuby 1.0 pre-alpha, Python 2.5.1, Python 3.0a1, PHP 6.0, PHP 5.3, PHP 5.2.5, Ruby 1.8.6.

Ovviamente un singolo microbenchmark di questo tipo non può essere assolutamente considerato come un valido indicatore delle prestazioni offerte da un linguaggio in situazioni reali, però i risultati sono comunque curiosi. A quanto pare i rubyisti avranno già un bel regalo assicurato per questo Natale :-)

PS: noto al termine della scrittura che Antonio ha scritto un altro articolo, aggiungendo Lisp e Haskell.