Funciones variables en PHP: cómo ejecutar cualquier función en tiempo de ejecución

01-10-2025 | PHP function 7 min

Veamos el tema importante de las funciones variables en PHP. En la documentación dice "variable functions".

Vamos con la definición oficial: Una función variable es simplemente una variable que guarda el nombre de una función. Y esto inyecta flexibilidad y dinamismo. En otras palabras el valor de la variable es el nombre de la función.

function add(int $a, int $b): int {
    return $a + $b;
}

function multiply(int $a, int $b): int {
    return $a * $b;
}

$operation = 'multiply'; 
$result = $operation(3, 9); 

echo $result; // Imprime 27

Nota que $operation contiene el nombre de la función multiply, y al llamarla con paréntesis, se ejecuta dicha función.

La sintaxis es extraña al principio, pero dejame explicarte las siguientes ventajas:

  1. Esto brinda flexibilidad, porque uno como programador decide qué función o método va a funcionar en tiempo de ejecución, sin estar atado a un nombre fijo.
  2. Este dinamismo nos permite implementar patrones de diseño más avanzados como estrategia, inyección de dependencias, middleware y otros.

Veamos este ejemplo:

$strategies = [
    'add' => function($a, $b) { return $a + $b; },
    'sub' => function($a, $b) { return $a - $b; },
    'mul' => function($a, $b) { return $a * $b; },
    'div' => function($a, $b) { return $b != 0 ? $a / $b : 'Error: Division by zero'; },
];

echo 'Add: ' . $strategies['add'](3, 9) . PHP_EOL; // Imprime Add: 12
echo 'Sub: ' . $strategies['sub'](9, 3) . PHP_EOL; // Imprime Sub: 6
echo 'Mul: ' . $strategies['mul'](1, 6) . PHP_EOL; // Imprime Mul: 6
echo 'Div: ' . $strategies['div'](7, 5) . PHP_EOL; // Imprime Div: 1.4

echo 'By zero: ' . $strategies['div'](9, 0) . PHP_EOL; // Imprime By zero: Error: Division by zero

Esto es un ejemplo del patrón de diseño llamado estrategia. Como puedes ver es un array asociativo llamado $strategies cuyo key es el nombre de las operaciones y el value es una función anónima que implementa la operación. El punto es entender que podemos llamar a cualquier función en tiempo de ejecución, sin estar atados a un nombre fijo.

Pero no quiero que te confundas, parece que uso "nombres fijos" porque escribí manualmente las palabras add, sub, mul, div, pero en un escenario real esto es dinámico, y dicha información puede venir de una base de datos, un archivo de configuración, o cualquier otra fuente externa.

$strategies['add'](3, 9); // Aquí 'add' podría ser cualquier string dinámico

Nota que el código no sabe de antemano qué operación ejecutará. La decisión siempre se toma en tiempo de ejecución y eso es lo que realmente significa dinamismo de funciones variables.

Ejemplos básicos

Veamos más ejemplos básicos, para entender mejor el concepto. Más adelante haré un ejemplo de rutas como caso de uso real.

  1. Clase User:
class User 
{
    public function __construct(
        private string $name
    ) {}

    public function greet() 
    {
        return 'Hola, ' . $this->name;
    }
}

$user = new User('Graaan');
$method = 'greet'; // De nuevo, aquí 'greet' podría ser cualquier string dinámico

echo $user->$method(); // Imprime "Hola, Graaan"
  1. Clase User con métodos estáticos:
class User 
{
    public static function greet(string $name): string 
    {
        return "Hello, " . $name;
    }
}

$method = 'greet';

echo User::$method('Graaan'); // Imprime "Hello, Graaan!"

Function handling Functions

En el artículo Argumentos de Funciones en PHP: Guía Práctica y Profesional hablamos de las famosas Function handling Functions, ahora quiero mencionar las que son útiles en este contexto.

  1. call_user_func: Significa "llamar a una función de usuario". Pero para que se entienda bien yo lo llamo "llamar a una función creada por el usuario".
  2. call_user_func_array: Es similar a call_user_func, pero permite pasar los parámetros como un array.
function add(int $a, int $b): int {
    return $a + $b;
}

function multiply(int $a, int $b): int {
    return $a * $b;
}

echo call_user_func('add', 3, 3); // Imprime 6

$numbers = [3, 9];
echo call_user_func_array('multiply', $numbers); // Imprime 27

Cómo puedes ver, esto nos permite llamar a las funciones de manera dinámica, como en los ejemplos anteriores. En resumen, con call_user_func_array pasamos un array de datos, en cambio con call_user_func pasamos dicha información de la manera tradicional.

Variable handling Functions

Este es un tema que merece otro artículo donde pueda profundizar más. Pero aquí te quiero dejar una rápida introducción de la función is_callable porque aplica directamente a nuestro objetivo.

Primero que nada, ¿qué son las "variable handling Functions"?

Son un conjunto de funciones diseñadas para inspeccionar, validar o manipular variables, es interesante porque nos permiten obtener información valiosa sobre el tipo, el estado o las características de una variable en tiempo de ejecución.

Sería bueno saber si una variable es una función válida antes de intentar ejecutarla. Aquí es donde entra en juego is_callable.

function add(int $a, int $b): int {
    return $a + $b;
}

$operation = 'add';
// $operation = 'multiply';

if (is_callable($operation)) {
    echo $operation(3, 9); // Imprime 12
} else {
    echo "La función $operation no es callable.";
}

Puedes descomentar $operation = 'multiply'; para ver qué pasa cuando la función no existe. En este caso is_callable devuelve false y se imprime el mensaje de error "La función multiply no es callable.".

Caso de uso real: Rutas

Este es un caso interesante que incluso te permitirá comprender los frameworks modernos respecto al enrutamiento. Es simple, pero poderoso.

class Router
{
    private $routes = [];

    public function add(string $path, callable $handler): void
    {
        $this->routes[$path] = $handler;
    }

    public function run(string $path): string
    {
        if (isset($this->routes[$path])) {
            return call_user_func($this->routes[$path]);            
        }

        return '404 Not Found...';
    }
}

La clase Router tiene un array llamado $routes para guardar la ruta como key y sus respuestas (handlers) como value.

  • El método add es quien nos permite agregar nuevas rutas.
  • El método run ejecuta el manejador (handler) correspondiente a la ruta solicitada.

Nota importante: isset es una función que verifica si una variable está definida, por ello la uso en el bloque if del método run, para asegurarme de que la ruta existe antes de ejecutarla.

Veamos cómo usar esta clase:

$router = new Router();

$router->add('/hello', function() {
    return 'Hello [...]';
});

$router->add('/goodbye', function() {
    return 'Goodbye [...]';
});

echo $router->run('/hello') . PHP_EOL; // Imprime "Hello [...]"
echo $router->run('/goodbye') . PHP_EOL; // Imprime "Goodbye [...]"
echo $router->run('/not-found') . PHP_EOL; // Imprime "404 Not Found..."

La clave de este ejercicio está aquí call_user_func($this->routes[$path]), donde se ejecuta la función anónima correspondiente a la ruta solicitada. Si la ruta no existe, se devuelve un mensaje de error "404 Not Found...".

Aunque también podemos trabajar con el siguiente código:

if (is_callable($this->routes[$path])) {
    return $this->routes[$path]();
}
  • $this->routes[$path] es un string que contiene el nombre de la función a ejecutar, luego se ejecuta gracias a los paréntesis ().
  • Puedes usar una combinación de is_callable y call_user_func: tu decides.

Pero ojo, este es un ejemplo simple, sin embargo, la idea es que los frameworks modernos usan este mismo concepto para manejar las rutas. Ahí vemos la evidencia respecto a la flexibilidad y dinamismo que ofrecen las funciones variables.

Como puedes ver, son esenciales para construir sistemas robustos y escalables.

Tips de Laravel y PHP

Te enviaré únicamente temas profesionales de programación cada domingo.

    Puedes cancelar en cualquier momento.