Funciones anónimas
Las funciones anónimas, también conocidas como cierres
(closures), permiten
la creación de funciones que no tienen un nombre especificado. Son más útiles como
valor de los parámetros de llamadas de retorno
,
pero tienen muchos otros usos.
Las funciones anónimas están implementadas utilizando la clase
Closure.
Ejemplo #1 Ejemplo de función anónima
<?php
echo preg_replace_callback('~-([a-z])~', function ($coincidencia) {
return strtoupper($coincidencia[1]);
}, 'hola-mundo');
// imprime holaMundo
?>
Los cierres también se pueden usar como valores de variables; PHP automáticamente
convierte tales expresiones en instancias de la
clase interna Closure. La asignación de un cierre a una
variable emplea la misma sintaxis que cualquier otra asignación, incluido el
punto y coma final:
Ejemplo #2 Ejemplo de asignación de variable de una función anónima
<?php
$saludo = function($nombre)
{
printf("Hola %s\r\n", $nombre);
};
$saludo('Mundo');
$saludo('PHP');
?>
Los cierres también pueden heredar variables del ámbito padre. Cualquier
variable debe ser pasada al constructor de lenguaje use
.
Desde PHP 7.1, estas variables no deben incluir superglobals,
$this, o variables con el mismo nombre que un parámetro.
Ejemplo #3 Heredar variables de un ámbito padre
<?php
$mensaje = 'hola';
// Sin "use"
$ejemplo = function () {
var_dump($mensaje);
};
$ejemplo();
// Heredar $mensaje
$ejemplo = function () use ($mensaje) {
var_dump($mensaje);
};
$ejemplo();
// El valor de la variable heredada está cuando la función
// está definida, no cuando se le invoca
$mensaje = 'mundo';
$ejemplo();
// Reiniciar el mensaje
$mensaje = 'hola';
// Heredar por referencia
$ejemplo = function () use (&$mensaje) {
var_dump($mensaje);
};
$ejemplo();
// El valor cambiado en el ámbito padre
// se refleja dentro de la llamada a la función
$mensaje = 'mundo';
$ejemplo();
// Los cierres también aceptan argumentos normales
$ejemplo = function ($arg) use ($mensaje) {
var_dump($arg . ' ' . $mensaje);
};
$ejemplo("hola");
?>
El resultado del ejemplo
sería algo similar a:
Notice: Undefined variable: message in /example.php on line 6
NULL
string(4) "hola"
string(4) "hola"
string(4) "hola"
string(5) "mundo"
string(10) "hola mundo"
Heredar variables del ámbito padre no
es lo mismo que usar variables globales.
Las variables globales existen en el ámbito global, lo que implica que no
importa qué función se esté ejecutando. El ámbito padre de un cierre es la
función en la que dicho cierre fue declarado (no necesariamente la función
desde la que se llamó). Vea el siguiente ejemplo:
Ejemplo #4 Cierres y ámbito
<?php
// Un carro de compras básico que contiene una lista de productos añadidos
// y la cantidad de cada producto. Incluye un método que
// calcula el precio total de los artículos del carro usando un
// cierre como llamada de retorno.
class Carro
{
const PRECIO_MANTEQUILLA = 1.00;
const PRECIO_LECHE = 3.00;
const PRECIO_HUEVOS = 6.95;
protected $productos = array();
public function añadir($producto, $cantidad)
{
$this->productos[$producto] = $cantidad;
}
public function obtenerCantidad($producto)
{
return isset($this->productos[$producto]) ? $this->productos[$producto] :
FALSE;
}
public function obtenerTotal($impuesto)
{
$total = 0.00;
$llamadaDeRetorno =
function ($cantidad, $producto) use ($impuesto, &$total)
{
$precioUnidad = constant(__CLASS__ . "::PRECIO_" .
strtoupper($producto));
$total += ($precioUnidad * $cantidad) * ($impuesto + 1.0);
};
array_walk($this->productos, $llamadaDeRetorno);
return round($total, 2);
}
}
$mi_carro = new Carro;
// Añadir algunos artículos al carro
$mi_carro->añadir('mantequilla', 1);
$mi_carro->añadir('leche', 3);
$mi_carro->añadir('huevos', 6);
// Imprimir el total con un impuesto de venta del 5%.
print $mi_carro->obtenerTotal(0.05) . "\n";
// El resultado es 54.29
?>
Ejemplo #5 Vinculación automática de $this
<?php
class Test
{
public function testing()
{
return function() {
var_dump($this);
};
}
}
$object = new Test;
$function = $object->testing();
$function();
?>
El resultado del ejemplo sería:
Salida del ejemplo anterior en PHP 5.3:
Notice: Undefined variable: this in script.php on line 8
NULL
A partir de PHP 5.4.0, cuando se declara en el contexto de una clase, la clase actual está
vinculada a ella automáticamente, haciendo que $this
esté disponible
dentro del ámbito de la función. Si no se desea esta vinculación automática
de la clase actual, se deberían usar
funcions anónimas
estáticas en su lugar.
Static anonymous functions
A partir de PHP 5.4, las funciones anónimas pueden ser declaradas estáticamente. Esto
evita tener la clase actual vinculada automáticamente a
ellas. Los objetos tampoco podrían vincularse a ellas durante la ejecución.
Ejemplo #6 Intentar usar $this
dentro de una función anónima estática
<?php
class Foo
{
function __construct()
{
$func = static function() {
var_dump($this);
};
$func();
}
};
new Foo();
?>
El resultado del ejemplo sería:
Notice: Undefined variable: this in %s on line %d
NULL
Ejemplo #7 Intentar vincular un objeto a una función anónima estática
<?php
$func = static function() {
// cuerpo de la función
};
$func = $func->bindTo(new StdClass);
$func();
?>
El resultado del ejemplo sería:
Warning: Cannot bind an instance to a static closure in %s on line %d