01-10-2025 | Última actualización: 30-10-2025 | PHP function 7 min
Veamos el tema importante de las funciones variables en PHP. En la documentación oficial su título es "variable functions".
Definición: 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 permíteme explicarte las siguientes ventajas:
- Esto brinda flexibilidad, porque uno como programador decide qué función o método va a trabajar 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 sencillo 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 como mencioné anteriormente.
Pero no quiero que te confundas, podría parecer 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 un string que viene de config.php o de una base de datos
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 para entender mejor el concepto. Más adelante haré un mini proyecto 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
Usercon 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 y di una introducción 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. Sin embargo, 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 preparado "La función multiply no es callable...".
Caso de uso real: Rutas
Este es un mini proyecto interesante que incluso te permitirá comprender cómo funcionan 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
addes quien nos permite agregar nuevas rutas. - El método
runejecuta 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]();
}
Notas finales:
$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_callableycall_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.