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($total2);
    }
}

$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:

object(Test)#1 (0) {
}

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

Historial de cambios

Versión Descripción
7.1.0 Las funciones anónimas no pueden tener un cierre sobre superglobals, $this, o cualquier variable con el mismo nombre que un parámetro.
5.4.0 Las funciones anónimas pueden usar $this, así como ser declaradas státicamente.
5.3.0 Las funciones anónimas se encuentran disponibles.

Notas

Nota: Es posible usar func_num_args(), func_get_arg(), y func_get_args() desde dentro de un cierre.