Lección 24 de 75 12 min de lectura

Funciones Anónimas

Las funciones anónimas (también llamadas closures o lambdas) son funciones sin nombre que puedes almacenar en variables, pasar como argumentos o retornar desde otras funciones. Son fundamentales para la programación funcional en PHP.

¿Qué es una función anónima?

Una función anónima es una función que no tiene nombre. Se define usando la palabra clave function sin un identificador:

PHP
<?php

declare(strict_types=1);

// Función con nombre
function sumarNormal(int $a, int $b): int
{
    return $a + $b;
}

// Función anónima asignada a una variable
$sumar = function (int $a, int $b): int {
    return $a + $b;
};

// Llamar a la función anónima
echo $sumar(5, 3); // 8

// Puedes reasignar la variable a otra función
$sumar = function (int $a, int $b): int {
    return $a + $b + 10; // Ahora suma 10 extra
};

echo $sumar(5, 3); // 18
Punto y coma

Las funciones anónimas asignadas a variables terminan con punto y coma, ya que son una expresión de asignación.

Como callbacks

El uso más común de las funciones anónimas es como callbacks, funciones que se pasan como argumento a otras funciones:

PHP
<?php

declare(strict_types=1);

$numeros = [1, 2, 3, 4, 5];

// array_map con función anónima
$cuadrados = array_map(function (int $n): int {
    return $n * $n;
}, $numeros);

print_r($cuadrados); // [1, 4, 9, 16, 25]

// array_filter con función anónima
$pares = array_filter($numeros, function (int $n): bool {
    return $n % 2 === 0;
});

print_r($pares); // [1 => 2, 3 => 4]

// usort con función anónima
$personas = [
    ['nombre' => 'Ana', 'edad' => 25],
    ['nombre' => 'Luis', 'edad' => 30],
    ['nombre' => 'Eva', 'edad' => 22],
];

usort($personas, function (array $a, array $b): int {
    return $a['edad'] <=> $b['edad'];
});

// Ordenado por edad ascendente
print_r($personas);
/*
[
    ['nombre' => 'Eva', 'edad' => 22],
    ['nombre' => 'Ana', 'edad' => 25],
    ['nombre' => 'Luis', 'edad' => 30],
]
*/

Capturar variables con use

Las funciones anónimas no tienen acceso automático a las variables del ámbito donde se definen. Usa use para capturar variables:

PHP
<?php

declare(strict_types=1);

$multiplicador = 10;

// Sin use: la variable no está disponible
$sinUse = function (int $n): int {
    // return $n * $multiplicador; // Error: undefined variable
    return $n * 2;
};

// Con use: captura la variable
$conUse = function (int $n) use ($multiplicador): int {
    return $n * $multiplicador;
};

echo $conUse(5); // 50

// Capturar múltiples variables
$base = 100;
$incremento = 5;

$calcular = function (int $cantidad) use ($base, $incremento): int {
    return $base + ($cantidad * $incremento);
};

echo $calcular(3); // 115

Captura por valor vs por referencia

Por defecto, use captura por valor (copia). Usa & para capturar por referencia:

PHP
<?php

declare(strict_types=1);

// Captura por valor (copia)
$contador = 0;

$incrementarValor = function () use ($contador): int {
    // Modifica la copia local, no el original
    return ++$contador;
};

echo $incrementarValor(); // 1
echo $incrementarValor(); // 1 (siempre parte de 0)
echo $contador;           // 0 (no cambió)

// Captura por referencia
$contadorRef = 0;

$incrementarRef = function () use (&$contadorRef): int {
    return ++$contadorRef;
};

echo $incrementarRef(); // 1
echo $incrementarRef(); // 2
echo $incrementarRef(); // 3
echo $contadorRef;      // 3 (sí cambió)
Cuándo usar referencia

Usa captura por referencia con cuidado. Es útil para acumuladores o cuando necesitas modificar estado externo, pero puede hacer el código difícil de seguir.

Funciones anónimas como argumentos

Puedes definir funciones que reciben otras funciones como parámetros usando el tipo callable:

PHP
<?php

declare(strict_types=1);

// Función que acepta un callback
function aplicar(array $items, callable $transformar): array
{
    $resultado = [];
    foreach ($items as $item) {
        $resultado[] = $transformar($item);
    }
    return $resultado;
}

// Función que filtra usando un callback
function filtrar(array $items, callable $condicion): array
{
    $resultado = [];
    foreach ($items as $item) {
        if ($condicion($item)) {
            $resultado[] = $item;
        }
    }
    return $resultado;
}

$numeros = [1, 2, 3, 4, 5];

// Pasar función anónima
$dobles = aplicar($numeros, function (int $n): int {
    return $n * 2;
});
print_r($dobles); // [2, 4, 6, 8, 10]

$mayoresQue2 = filtrar($numeros, function (int $n): bool {
    return $n > 2;
});
print_r($mayoresQue2); // [3, 4, 5]

Retornar funciones anónimas

Las funciones pueden retornar otras funciones, creando "fábricas de funciones":

PHP
<?php

declare(strict_types=1);

// Fábrica de multiplicadores
function crearMultiplicador(int $factor): callable
{
    return function (int $numero) use ($factor): int {
        return $numero * $factor;
    };
}

$duplicar = crearMultiplicador(2);
$triplicar = crearMultiplicador(3);
$porDiez = crearMultiplicador(10);

echo $duplicar(5);  // 10
echo $triplicar(5); // 15
echo $porDiez(5);   // 50

// Fábrica de validadores
function crearValidadorLongitud(int $min, int $max): callable
{
    return function (string $texto) use ($min, $max): bool {
        $longitud = strlen($texto);
        return $longitud >= $min && $longitud <= $max;
    };
}

$validarUsername = crearValidadorLongitud(3, 20);
$validarPassword = crearValidadorLongitud(8, 100);

var_dump($validarUsername('ab'));       // false
var_dump($validarUsername('usuario'));  // true
var_dump($validarPassword('12345'));    // false
var_dump($validarPassword('password123')); // true

Funciones anónimas inmediatamente invocadas (IIFE)

Puedes definir y ejecutar una función anónima en una sola expresión:

PHP
<?php

declare(strict_types=1);

// IIFE - Immediately Invoked Function Expression
$resultado = (function (): int {
    $a = 10;
    $b = 20;
    return $a + $b;
})();

echo $resultado; // 30

// Útil para encapsular lógica compleja
$config = (function (): array {
    $env = getenv('APP_ENV') ?: 'development';

    return match ($env) {
        'production' => [
            'debug' => false,
            'cache' => true,
            'log_level' => 'error',
        ],
        'staging' => [
            'debug' => true,
            'cache' => true,
            'log_level' => 'warning',
        ],
        default => [
            'debug' => true,
            'cache' => false,
            'log_level' => 'debug',
        ],
    };
})();

print_r($config);

Ejemplo práctico: Sistema de eventos

PHP
<?php

declare(strict_types=1);

// Sistema de eventos simple usando arrays y funciones

// Almacenar listeners en un array
$listeners = [];

// Función para registrar un listener
function registrarEvento(array &$listeners, string $evento, callable $callback): void
{
    if (!isset($listeners[$evento])) {
        $listeners[$evento] = [];
    }
    $listeners[$evento][] = $callback;
}

// Función para emitir un evento
function emitirEvento(array $listeners, string $evento, array $datos = []): void
{
    if (!isset($listeners[$evento])) {
        return;
    }

    foreach ($listeners[$evento] as $callback) {
        $callback($datos);
    }
}

// Registrar listeners
registrarEvento($listeners, 'usuario.creado', function (array $usuario): void {
    echo "Enviando email de bienvenida a {$usuario['email']}\n";
});

registrarEvento($listeners, 'usuario.creado', function (array $usuario): void {
    echo "Registrando en analytics: nuevo usuario {$usuario['nombre']}\n";
});

$logPrefix = '[LOG]';
registrarEvento($listeners, 'usuario.creado', function (array $usuario) use ($logPrefix): void {
    echo "$logPrefix Usuario creado: {$usuario['nombre']}\n";
});

// Emitir evento
$nuevoUsuario = ['nombre' => 'Carlos', 'email' => 'carlos@example.com'];
emitirEvento($listeners, 'usuario.creado', $nuevoUsuario);

// Salida:
// Enviando email de bienvenida a carlos@example.com
// Registrando en analytics: nuevo usuario Carlos
// [LOG] Usuario creado: Carlos
PHP
<?php

declare(strict_types=1);

// Pipeline de transformaciones usando funciones

function crearPipeline(): array
{
    return ['pasos' => []];
}

function agregarPaso(array &$pipeline, callable $paso): void
{
    $pipeline['pasos'][] = $paso;
}

function ejecutarPipeline(array $pipeline, mixed $input): mixed
{
    $resultado = $input;

    foreach ($pipeline['pasos'] as $paso) {
        $resultado = $paso($resultado);
    }

    return $resultado;
}

// Crear pipeline de procesamiento de texto
$pipeline = crearPipeline();

agregarPaso($pipeline, function (string $texto): string {
    return trim($texto);
});

agregarPaso($pipeline, function (string $texto): string {
    return strtolower($texto);
});

agregarPaso($pipeline, function (string $texto): string {
    return preg_replace('/\s+/', ' ', $texto) ?? $texto;
});

agregarPaso($pipeline, function (string $texto): string {
    return ucfirst($texto);
});

$entrada = '   HOLA    MUNDO   ';
$salida = ejecutarPipeline($pipeline, $entrada);

echo $salida; // "Hola mundo"

Ejercicios

Ejercicio 1: Funcion anonima basica

Crea una funcion anonima asignada a la variable $esMayorDeEdad que reciba una edad (int) y retorne true si es 18 o mas, false en caso contrario.

Ver solucion
PHP
<?php
declare(strict_types=1);

$esMayorDeEdad = function (int $edad): bool {
    return $edad >= 18;
};

var_dump($esMayorDeEdad(20)); // true
var_dump($esMayorDeEdad(15)); // false
var_dump($esMayorDeEdad(18)); // true

Ejercicio 2: Captura de variables con use

Crea una funcion anonima que calcule el precio con IVA. La variable $iva (0.21) debe capturarse con use. La funcion recibe un precio (float) y retorna el precio con IVA aplicado.

Ver solucion
PHP
<?php
declare(strict_types=1);

$iva = 0.21;

$calcularPrecioConIva = function (float $precio) use ($iva): float {
    return $precio * (1 + $iva);
};

echo $calcularPrecioConIva(100.0); // 121.0
echo $calcularPrecioConIva(50.0);  // 60.5

Ejercicio 3: Funcion como callback

Usa array_filter con una funcion anonima para filtrar un array de numeros, quedandote solo con los que sean divisibles por 3. Array de entrada: [1, 3, 6, 7, 9, 12, 15, 16].

Ver solucion
PHP
<?php
declare(strict_types=1);

$numeros = [1, 3, 6, 7, 9, 12, 15, 16];

$divisiblesPor3 = array_filter($numeros, function (int $n): bool {
    return $n % 3 === 0;
});

print_r(array_values($divisiblesPor3));
// [3, 6, 9, 12, 15]

¿Te está gustando el curso?

Tenemos cursos premium con proyectos reales y soporte personalizado.

Descubrir cursos premium