Funciones anónimas
Las funciones anónimas, también conocidas como 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 ($match) {
return strtoupper($match[1]);
}, 'hola-mundo');
// imprime holaMundo
?>
Las funciones anónimas 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 una función
anónima 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
$greet = function($name)
{
printf("Hola %s\r\n", $name);
};
$greet('Mundo');
$greet('PHP');
?>
Las funciones anónimas también pueden heredar variables del ámbito padre.
Cualquier variable debe ser pasada mediante el 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.
La declaración del tipo de retorno de la función debe ser puesta
despues del constructor de lenguaje use
.
Ejemplo #3 Heredar variables de un ámbito padre
<?php
$message = 'hola';
// Sin "use"
$example = function () {
var_dump($message);
};
$example();
// Heredar $message
$example = function () use ($message) {
var_dump($message);
};
$example();
// El valor de la variable heredada está cuando la función
// está definida, no cuando se le invoca
$message = 'mundo';
$example();
// Reiniciar el mensaje
$message = 'hola';
// Heredar por referencia
$example = function () use (&$message) {
var_dump($message);
};
$example();
// El valor cambiado en el ámbito padre
// se refleja dentro de la llamada a la función
$message = 'mundo';
$example();
// Las funciones anónimas también aceptan argumentos normales
$example = function ($arg) use ($message) {
var_dump($arg . ' ' . $message);
};
$example("hola");
?>
// El tipo de retorno se declara despues de la expresión use
$example = function () use ($message): string {
return "hola $message";
};
var_dump($example());
?>
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"
string(10) "hola mundo"
Desde PHP 8.0.0, la lista de variables heredadas del ámbito padre puede
incluir una coma final, la cual será ignorada.
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:
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
funciones anónimas
estáticas en su lugar.
Funciones anónimas estáticas
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