Diversi linguaggi di programmazione supportano costrutti per la definizione a runtime di una funzione anonima: con questo termine si intende una funzione comune in tutte le sue caratteristiche ad ogni altra funzione ma senza un'associazione esplicita con un nome. Concettualmente si tratta di funzioni che dovrebbero essere considerate one shot, cioè da chiamare una sola volta nel punto stesso della definizione.
Un linguaggio dove questo paradigma è estremamente comune è Javascript per via del suo ampio utilizzo delle metodologie event-driven, cioè azioni da avviare in base agli eventi che si sono verificati. In senso lato queste funzioni prendono il nome di callback.
L'implementazione in PHP
Dalla versione 5.3.0 è possibile definire funzioni anonime utilizzandole direttamente come callback all'interno di una chiamata di funzioni o assegnarle ad una variabile per poterle richiamare più di una volta. La sintassi è estremamente semplice e richiede l'uso della parola chiave function omettendo il nome della funzione e facendo seguire, dove necessario, il carattere ";" alla dichiarazione della funzione.
Una volta acquisita la funzione anonima in una variabile è possibile eseguirla attraverso l'operatore ()
a cui si possono passare i parametri ordinati. In alternativa è possibile usare le funzioni predefinite call_user_func
e call_user_func_array
. Queste funzioni accettano come primo parametro la variabile contenente la funzione anonima ma differiscono per il modo in cui devono essere specificati gli argomenti da passare: nel primo caso saranno gli argomenti della funzione predefinita mentre nel secondo caso saranno raccolti all'interno di un array. Queste tre modalità sono quindi valide:
<php
$sum = function ($a, $b) {
return $a + $b;
};
$sum(2, 3); // 5
call_user_func($sum, 2, 3); // 5
call_user_func_array($sum, array(2, 3)); // 5
Come si può vedere dall'esempio, in realtà la sintassi è la stessa che si utilizzerebbe dichiarando una funzione non anonima e passando il nome di quella funzione all'interno della variabile $sum
.
Accedere alle variabili esterne
Le funzioni anonime hanno uno scope estremamente rigido e di default possono accedere esclusivamente ai parametri che vengono loro passati, non possono cioè usare nessuna variabile dichiarata all'esterno della stessa. L'accesso alle variabili esterne deve essere reso esplicito tramite la parola chiave use
in questo modo:
<php
$n = 50;
call_user_func(function () {
echo $n*2;
}); // Errore
call_user_func(function () use ($n) {
echo $n*2;
}); // 100
In realtà il valore della variabile esterna viene copiato all'interno della funzione anonima nel momento in cui questa viene definita (e non nel momento in cui viene chiamata). Per poter accedere al valore attuale della variabile esterna è necessario passarla per riferimento tramite l'operatore &
che precede il nome del parametro nella dichiarazione della funzione.
La classe Closure
Nonostante questa sintassi specifica, in PHP, dalla versione 5.4.0, una funzione anonima è in realtà un'istanza della classe Closure
, il cui costruttore è privato rendendo impossibile usare l'operatore new
. Ogni oggetto derivato da questa classe ha due metodi: bindTo
e call
(questo solo dalla versione 7.0).
Il metodo bindTo
permette di associare la closure ad uno specifico oggetto con accesso alle proprietà e ai metodi dello stesso, indipendentemente dal livello di visibilità: è possibile quindi modificare o leggere proprietà pubbliche, protette o private. Questo metodo accetta due parametri: il primo è il contesto della variabile, in pratica il valore che assumerà la variabile $this
all'interno della funzione mentre il secondo è lo scope che sarà imposto alla funzione permettendo o negando l'accesso ai vari livelli di visibilità .
<php
class Hello
{
public $hello = 'Hello';
}
class Ciao
{
private $hello = 'Ciao';
}
$say = function ($name) {
echo $this->hello . ', ' . $name;
};
$hello = new Hello();
$sayHello = $say->bindTo($hello, $hello);
$sayHello('Andrea'); // Hello, Andrea
$ciao = new Ciao();
$sayCiao = $say->bindTo($ciao, 'Ciao');
$sayCiao('Andrea'); // Ciao, Andrea
Come si può notare dall'esempio lo scope può essere il nome della classe o, in alternativa, l'oggetto istanziato. La chiamata al metodo bindTo
restituisce una copia della funzione anonima modificata per essere associata all'oggetto indicato.
Dalla versione 7.0 di PHP la classe Closure
presenta anche il metodo call
che accetta come primo parametro l'oggetto a cui la funzione deve essere temporaneamente associata, seguito da un numero variabile di parametri corrispondenti agli argomenti da passare alla funzione anonima. In questo modo è possibile ottenere il risultato della chiamata alla funzione anonima senza passare per un binding temporaneo. L'esempio precedente ad esempio può essere riscritto in questo modo:
<php
// Dichiarazione classi e funzione anonima
// [...]
$hello = new Hello();
$say->call($hello, 'Andrea'); // Hello, Andrea
$ciao = new Ciao();
$say->call($ciao, 'Andrea'); // Ciao, Andrea
La classe Closure
presenta anche un metodo statico per associare una funzione anonima ad un oggetto: bind
. Usando lo stesso esempio precedente:
<php
// Dichiarazione classi e funzione anonima
// [...]
$hello = new Hello();
$sayHello = Closure::bind($say, $hello, $hello);
$sayHello('Andrea'); // Hello, Andrea
$ciao = new Ciao();
$sayCiao = Closure::bind($say, $ciao, 'Ciao');
$sayCiao('Andrea'); // Ciao, Andrea