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:
- 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.
- 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.
- 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"
- 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.
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".call_user_func_array
: Es similar acall_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
ycall_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.