Desde la versión 5.3, PHP nos ofrece funciones anónimas (o una suerte de) a las que casi casi podemos considerar First-class citizens.
Luego de experimentar un poco en un pequeño proyecto, estas son mis pequeñas conclusiones.
Lo básico
Las funciones anónimas son (como es de esperar) objetos, más particularmente instancias de la clase Closure. Como tales, pueden tener métodos y propiedades (en la versión 5.4 tienen un único método: bindTo, además de los magic methods).
Los ejemplos clásicos de uso de funciones anónimas se pueden ver con los clásicos map y reduce:
$list = array(1, 2, 3, 4);
$sum = function($memo, $current) {
return $memo + $current;
};
echo array_reduce($list, $sum, 0); // 10
$square = function($current) {
return $current * $current;
};
echo array_reduce(array_map($square, $list), $sum); // 30
Como otros valores, pueden ser definidas en distintos contextos así como tambien ser ejecutadas. En este aspecto pareciera ser que los “Closures” no se corresponden con otros objetos*:
// Ok
$myFn = function() {
// Ok
$anotherFn = function() { echo 'Hello World'; };
return $anotherFn;
};
// Ok
var_dump($myFn()); // object(Closure)#2 (0) { }
// Parse error: syntax error, unexpected '('
$myFn()();
// Ok
$myFn()->__invoke(); // Hello World
// Ok
$myFn->__invoke()->__invoke(); // Hello World
// Ok
$result = $myFn();
$result(); // Hello World
// Ok
$inArray = array(function() { echo 'Hello World'; });
// Ok
$inArray[0](); // "Hello World"
// Constants may only evaluate to scalar values
define('MY_CONSTANT', function() {});
class Foo {
// Parse error: syntax error, unexpected T_FUNCTION
const IS_GET = function() {};
// Parse error: syntax error, unexpected T_FUNCTION
private $foo = function() {};
// Parse error: syntax error, unexpected T_FUNCTION
private $fns = array(
'foo' => function() {}
);
}
$myObject = new stdClass();
// Ok
$myObject->fn = function() { echo 'Hello World'; };
// Fatal error: Call to undefined method stdClass::fn()
//$myObject->fn();
// Ok
$myObject->fn->__invoke(); // "Hello World"
// Parse error: syntax error, unexpected '('
($myObject->fn)();
// Ok
$holder = $myObject->fn;
$holder(); // "Hello World"
*Test realizados sobre la versión 5.4.0beta2-dev
Closures
Los closures propiamente dichos están disponibles, pero (gran sorpresa) requieren explícitar las referencias deseadas:
$foo = 1;
$bar = 2;
$fn = function() use($foo, $bar) {
echo $foo + $bar;
};
$fn(); // 3
Como dato curioso, podemos referenciar las variables, para que el valor que toma la función (en caso de que sean primitivas) siempre sea el actual y no el inicial:
$foo = 1;
$bar = 2;
$fn = function() use(&$foo, $bar) {
echo $foo + $bar;
};
$foo = 10;
$fn(); // 12
Obviamente, las referecias a $foo y $bar quedan ligadas sin importar si están disponibles en el scope donde se ejecute $fn.
Binding
Desde PHP 5.4, esta disponible el método Closure#bindTo (y su versión estática Closure::bind) que nos permiten cambiar el thisValue de la función anónima, definiendo inclusive la visibilidad que tendrá dentro del objeto/clase en cuestión.
La firma del método consta de dos argumentos, siendo el primero el objeto al cual se bindeará la función y el segundo la clase que definirá su visibilidad. En caso de no definir este último argumento, la función tendrá disponible únicamente los métodos y propiedades públicos. En caso de definir la clase o el objeto (del cual tomará la clase).
Para clarificar esto, dada una clase Foo y una función anónima $fn:
class Foo {
static private $staticPrivate = 'staticPrivate';
static protected $staticProtected = 'staticProtected';
static public $staticPublic = 'staticPublic';
private $private = 'private';
protected $protected = 'protected';
public $public = 'public';
}
$foo = new Foo;
// Nuestra funcion anonima
$fn = function($name, $isStatic = false) {
echo ($isStatic ?
Foo::$$name :
$this->{$name}) . '<br/>';
};
Podemos bindear la función de la siguiente manera:
$bindedFn = $fn->bindTo($foo); // Binding con scope de clase Foo $bindedWithScope = $fn->bindTo($foo, 'Foo'); // Que es similar a $bindedWithScope = $fn->bindTo($foo, $foo);
Lo que resultará de la siguiente manera:
$bindedFn('public'); // public
$bindedFn('protected'); // Fatal error: Cannot access protected...
$bindedFn('private'); // Fatal error: Cannot access private...
$bindedFn('staticPublic', true); // staticPublic
$bindedFn('staticProtected', true); // Fatal error: Cannot access protected...
$bindedFn('staticPrivate', true); // Fatal error: Cannot access private...
$bindedWithScope('public'); // public
$bindedWithScope('protected'); // protected
$bindedWithScope('private'); // private
$bindedWithScope('staticPublic', true); // staticPublic
$bindedWithScope('staticProtected', true); // staticProtected
$bindedWithScope('staticPrivate', true); // staticPrivate
ReflectionFunction
A la hora de manipular nuestras funciones anónimas, tenemos la posibilidad de aplicar reflection sobre las mismas utilizando la clase ReflectionFunction*, obteniendo así información sobre la misma (nombre, argumentos, archivo donde fue definida, etc).
*Es una lástima que la documentación oficial sea tan deplorable.
Esta clase es especialmente útil a la hora de generar llamadas on the fly, permitiendo manipular los argumentos con los que se llamará a la función.
Un ejemplo de esto se puede ver en este fragmento de código de del pequeño proyecto en cuestión:
private function fillArguments($fn, $arguments = array()) {
if(!is_callable($fn)) {
return $arguments;
}
$reflection = new \ReflectionFunction($fn);
$names = $reflection->getParameters();
array_shift($names);
foreach ($names as $arg) {
$arguments[] = $this->request->value(
$arg->getName(),
$arg->isOptional() ? $arg->getDefaultValue() : null
);
}
return $arguments;
}
public function exec($fn) {
$arguments = $this->fillArguments($fn, array($this->memo));
return call_user_func_array(\Closure::bind(
$fn,
$this->executionContext,
$this->executionContext
), $arguments);
}
Resumiendo
Lo bueno
- La sintaxis es consistente con las funciones y métodos.
- Soportan closures.
- Soportan binding.
- Y reflection!
Lo malo
- El lookup de métodos y propiedades de los objetos hace imposible emular métodos con funciones anónimas (aunque, se puede hackear mediante el método mágico __call).
- Requiere la invocación (excesivamente) explícita (_invoke) en algunos casos.
- No se puden declarar funciones anónimas como atributos de clases.
- A la clase Closure le falta bastante azúcar.
- La documentación oficial deja bastante que desear.

Magnífico artículo Valentin.
Gracias