Closures

Closures

Las closures son como funciones anónimas que se guardan en una variable.

$funcion = function ($a, $b){
    return $a + $b;
};
echo get_class($funcion); // Es un objeto, y es de tipo Closure

Ejemplo closure

Como las closures no son ejecutadas hasta que son llamadas, se suelen utilizar como callbacks. Vamos a ver un ejemplo:

El array_map itera sobre cada elemento aplicandoles la función, el ‘use’ es necesario para pasarle a la función el parámetro externo ‘greeting’

// example of use closure as callback
$names = ['john', 'jane', 'joe'];

$greeting = 'Hello, ';

$sentences = array_map(
    callback: function (string $name) use ($greeting): string {
        return $greeting.$name;
    },
    array: $names,
);
var_dump($sentences);
// [
//     'Hello, John',
//     'Hello, Jane',
//     'Hello, Joe',
// ] 

Arrow functions

Se utiliza con una sintaxis más simple, y siempre tienen que devolver un valor, a diferencia de las funciones normales. Se pueden usar la sugerencia de tipo

$addition = fn(int $a, int $b): int => $a + $b;
 
$result = $addition(2, 3);
 
echo $result; // Output: 5

Otra de las diferencias es que no es necesario incluir elementos externos dentro de las funciones flechas, estas automáticamente capturan las variables externas en donde está definida. En el ejemplo no es necesario usar ‘use ($greetings)’ porque está capturando automáticamente esa variable.

$greeting = 'Hello, ';
 
$greet = fn (string $name): string =>
    $name === 'Ashley'
        ? 'Hello, Ash Allen'
        : $greeting.ucfirst($name);
 
echo $greet('Ashley'); // Output: Hello, Ash Allen
echo $greet('john'); // Output: Hello, John

Closures vs callbacks

Como hemos visto un closure es una función anónima que puede ser asignada a una variable y pasarla por ahí como otra variable más.

En cambio un callback, es una función que es pasada a otra función como un argumento. Es una función que es llamada por otra función.

El parámetro que se pasa, puede ser un callback o simplemente una función normal.

Ejemplo Función Normal

En este ejemplo, se le pasa el nombre de la función que tiene que llamar y el valor con el cual llamarlo, como esa función está definida en este ámbito pues se ejecuta

// Define a function that takes a callback as an argument
function performOperation($callback, $value) {
    // Call the provided callback function with the given value
    return $callback($value);
}

// Define a simple function that squares a number
function square($number) {
    return $number * $number;
}

// Use the performOperation function with a normal function as a callback
$result = performOperation('square', 5);

// Output the result
echo $result; // Output: 25

Ejemplo usando closure as callback

En esta se le pasa el closure como parámetro y se llama a la función

// Define a function that takes a callback as an argument
function performOperation($callback, $value) {
    // Call the provided callback function with the given value
    return $callback($value);
}

// Use a closure as a callback to double a number
$double = function ($number) {
    return $number * 2;
};

// Use the performOperation function with a closure as a callback
$result = performOperation($double, 7);

// Output the result
echo $result; // Output: 14

Callable

Si por ejemplo la función espera un Closure como argumento, si nosotros le pasamos el nombre de una función para que la ejecute, nos va a dar un error, dado que closure es una clase que representa una función anónima o una función flecha, no una función normal.

Esto funciona

function executeClosure(Closure $closure)
{
    return $closure();
}
 
// ✅ This will work...
executeClosure(function () {
    return 'Hello World';
});
 
// ✅ This will work...
executeClosure(fn () => 'Hello World');

Pero esto no

function executeClosure(Closure $closure)
{
    return $closure();
}
 
function sayHello()
{
    return 'Hello World';
}
 
// ❌ This will not work...
executeClosure('sayHello');

En cambio si cambiamos nuestro sugerencia de tipo a callable en vez de Closure, entonces podemos pasarle una función normal.

function executeClosure(callable $closure)
{
    return $closure();
}
 
function sayHello()
{
    return 'Hello World';
}
 
// ✅ This will work...
executeClosure('sayHello');

First Class callable syntax

En php a partir de la 8.1 se introduce esta sintaxis. Esto permite convertir un callable a una función anónima que puede ser tratada como una closure.

Ejemplo de cómo llamar a una función normal cuando el parámetro es Closure usando esta sintaxis

function executeClosure(Closure $closure)
{
    return $closure();
}
 
function sayHello()
{
    return 'Hello World';
}
 
// ✅ This will work...
executeClosure(sayHello(...));