¿Cómo llamar a una función desde una cadena almacenada en una variable?

288

Necesito poder llamar a una función, pero el nombre de la función se almacena en una variable, ¿es esto posible? p.ej:

función foo ()
{
  // código aquí
}

barra de funciones ()
{
  // código aquí
}

$ functionName = "foo";
// necesito llamar a la función en función de lo que es $ functionName
Lagartija
fuente

Respuestas:

464

$functionName() o call_user_func($functionName)

knittl
fuente
85
Si necesita llamar a la función de un objeto cuyo nombre está en una variable, haga esto: call_user_func (array ($ obj, $ func), $ params)
BlackDivine
1
También vea mi respuesta a continuación si se trata de clases y métodos. No esperaba lo que encontré.
Chris K
¿Cómo podría anular el error de definición de función si la función no se definió? ¿If ($ functionName) es suficiente?
SaidbakR
14
@ sємsєм: debe usar la is_callablefunción. Verificará si una variable contiene algo que se puede llamar como una función (ya sea el nombre de una función, una matriz que contiene una instancia de objeto y un nombre de función, o una función / cierre anónimo)
knittl
66
@Pacerier: proporcione el nombre de clase como primer elemento de la matriz:call_user_func(array('ClassName', 'method'), $args)
knittl
121

Mi versión favorita es la versión en línea:

${"variableName"} = 12;

$className->{"propertyName"};
$className->{"methodName"}();

StaticClass::${"propertyName"};
StaticClass::{"methodName"}();

¡También puede colocar variables o expresiones dentro de los corchetes!

Lajos Meszaros
fuente
1
@marcioAlmada: publique un comentario en lugar de editar las respuestas de los demás de esta manera; agrega confusión - no está claro quién dijo qué [para los curiosos: ver las revisiones para explicaciones]
kryger 02 de
44
Ok @kryger. La línea 2 {"functionName"}();no es legal y arrojará un error de sintaxis.
marcio
44
Gracias a todos, por las respuestas, de hecho arroja un error, incluso en php 5.4, por lo que la sintaxis solo funciona con métodos de objeto. Editado el post.
Lajos Meszaros
17

Como ya se mencionó, hay algunas maneras de lograr esto, posiblemente con el método más seguro call_user_func()o, si es necesario, también puede seguir la ruta $function_name(). Es posible pasar argumentos utilizando ambos métodos como tal

$function_name = 'foobar';

$function_name(arg1, arg2);

call_user_func_array($function_name, array(arg1, arg2));

Si la función que está llamando pertenece a un objeto, aún puede usar cualquiera de estos

$object->$function_name(arg1, arg2);

call_user_func_array(array($object, $function_name), array(arg1, arg2));

Sin embargo, si va a utilizar el $function_name()método, puede ser una buena idea probar la existencia de la función si el nombre es dinámico

if(method_exists($object, $function_name))
{
    $object->$function_name(arg1, arg2);
}
olliefinn
fuente
12

En caso de que Google traiga a alguien más aquí porque estaban tratando de usar una variable para un método dentro de una clase, la siguiente es una muestra de código que realmente funcionará. Nada de lo anterior funcionó para mi situación. La diferencia clave es &en la declaración de $c = & new...y &$cse pasa call_user_func.

Mi caso específico es cuando se implementa el código de alguien que tiene que ver con colores y métodos de dos miembros lighten()y darken()de la clase csscolor.php. Por alguna razón, quería que el mismo código pudiera llamar a aclarar u oscurecer en lugar de seleccionarlo con lógica. Esto puede ser el resultado de mi terquedad de no solo usar if-else o cambiar el código que llama a este método.

$lightdark="lighten"; // or optionally can be darken
$color="fcc";   // a hex color
$percent=0.15;  
include_once("csscolor.php");
$c = & new CSS_Color($color);
$rtn=call_user_func( array(&$c,$lightdark),$color,$percent);

Tenga en cuenta que intentar cualquier cosa con $c->{...}no funcionó. Al leer detenidamente el contenido aportado por el lector en la parte inferior de la página de php.net call_user_func, pude reconstruir lo anterior. Además, tenga en cuenta que $paramscomo una matriz no funcionó para mí:

// This doesn't work:
$params=Array($color,$percent);
$rtn=call_user_func( array(&$c,$lightdark),$params);

Este intento anterior daría una advertencia sobre el método que espera un segundo argumento (porcentaje).

Chris K
fuente
¿Qué hace "$ c = & new CSS_Color"? Estoy hablando del amplificador (&). ¿Qué significa esto y cambiar?
Danon
@danon & es "dirección de" o el operador de "referencia". Estoy instiando un nuevo objeto de clase CSS_Color y luego asignando su referencia (también conocida como dirección) a $ c. Odio repetir el código, así que a menudo uso nombres de variables variables.
Chris K
1
No entiendo. ¿No se pasan los objetos por referencia a todas partes? Quiero decir, si pasas $ c a una función y la cambias, el objeto original también se verá afectado, ¿verdad? No importa si usaste esto o no, ¿estoy en lo cierto?
Danon
Probé diferentes combinaciones de símbolos y solo mientras leía los comentarios de los usuarios en php.org pude obtener el código de trabajo. Deberá comenzar esa discusión con las personas responsables del comportamiento de php.
Chris K
Para el segundo ejemplo que leyó mal, estaba buscando call_user_func_array
Tofandel
12

Algunos años tarde, pero esta es la mejor manera ahora en mi humilde opinión:

$x = (new ReflectionFunction("foo"))->getClosure();
$x();
error de servidor interno
fuente
3
Way to OOP para mí, pero le di un +1, ya que nadie mencionó las clases de Reflection todavía.
Lajos Meszaros
No entiendo esta respuesta. ¿Qué problema está resolviendo exactamente?
David Spector
9

En aras de la integridad, también puede utilizar eval () :

$functionName = "foo()";
eval($functionName);

Sin embargo, call_user_func()es la forma correcta.

karim79
fuente
8
Cabe señalar que eval () permitirá el mismo comportamiento pero también permitirá ejecutar cualquier código PHP. Entonces la variable podría usarse para secuestrar el sistema. Call_user_func () no trae tales problemas de seguridad.
John
44
evalNo es una solución a esta pregunta.
ankr
1
No hagas esto porque un atacante podría borrar tu servidor por completo.
andreszs
9

Solución: use PHP7

Nota: Para una versión resumida, vea TL; DR al final de la respuesta.

Viejos métodos

Actualización : se ha eliminado uno de los métodos antiguos explicados aquí. Consulte otras respuestas para obtener una explicación sobre otros métodos, no está cubierto aquí. Por cierto, si esta respuesta no te ayuda, deberías volver a actualizar tus cosas. La compatibilidad con PHP 5.6 finalizó en enero de 2019 (ahora incluso PHP 7.0 y 7.1 no son compatibles). Consulte las versiones compatibles para obtener más información.

Como otros mencionaron, en PHP5 (y también en versiones más nuevas como PHP7) podríamos usar variables como nombres de funciones, usar call_user_func()y call_user_func_array()(que, personalmente, odio esas funciones), etc.

Nuevos métodos

A partir de PHP7, hay nuevas formas introducidas:

Nota: Todo entre <something>paréntesis significa una o más expresiones para formar algo, por ejemplo <function_name>, expresiones que forman el nombre de una función.

Llamada de función dinámica: nombre de la función sobre la marcha

Podemos usar una o más expresiones entre paréntesis como el nombre de la función de una sola vez, en forma de:

(<function_name>)(arguments);

Por ejemplo:

function something(): string
{
    return "something";
}

$bar = "some_thing";

(str_replace("_", "", $bar))(); // something

// Possible, too; but generally, not recommended, because makes your code more complicated
(str_replace("_", "", $bar))()(); 

Nota: Aunque eliminar los paréntesis str_replace()no es un error, poner paréntesis hace que el código sea más legible. Sin embargo, a veces no puede hacerlo, por ejemplo, mientras usa el .operador. Para ser coherente, te recomiendo poner los paréntesis siempre.

Llamada de método dinámico: nombre del método sobre la marcha

Al igual que las llamadas a funciones dinámicas, podemos hacer lo mismo con las llamadas a métodos, rodeadas de llaves en lugar de paréntesis (para formas adicionales, navegue a TL; sección DR):

$object->{<method_name>}(arguments);
$object::{<method_name>}(arguments);

Véalo en un ejemplo:

class Foo
{
    public function another(): string
    {
        return "something";
    }
}

$bar = "another thing";

(new Something())->{explode(" ", $bar)[0]}(); // something

Llamada de método dinámico: la sintaxis de matriz

Una forma más elegante agregada en PHP7 es la siguiente:

[<object>, <method_name>](arguments);
[<class_name>, <method_name>](arguments); // Static calls only

Como ejemplo:

class Foo
{
    public function nonStaticCall()
    {
        echo "Non-static call";
    }
    public static function staticCall()
    {
        echo "Static call";
    }
}

$x = new X();

[$x, "non" . "StaticCall"](); // Non-static call
[$x, "static" . "Call"](); // Static call

Nota: El beneficio de usar este método sobre el anterior es que no le importa el tipo de llamada (es decir, si es estático o no).

Ejemplo adicional: uso de clases anónimas

Para complicar un poco las cosas, puede usar una combinación de clases anónimas y las características anteriores:

$bar = "SomeThing";

echo (new class {
    public function something()
    {
        return 512;
    }
})->{strtolower($bar)}(); // 512

TL; DR (Conclusión)

En general, en PHP7, es posible usar los siguientes formularios:

// Everything inside `<something>` brackets means one or more expressions
// to form something

// Dynamic function call
(<function_name>)(arguments)

// Dynamic method call on an object
$object->{<method_name>}(arguments)
$object::{<method_name>}(arguments)

// Dynamic method call on a dynamically-generated object
(<object>)->{<method_name>}(arguments)
(<object>)::{<method_name>}(arguments)

// Dynamic method call, statically
ClassName::{<method_name>}(arguments)
(<class_name>)::{<method_name>}(arguments)

// Dynamic method call, array-like (no different between static and non-static calls
[<object>, <method_name>](arguments)

// Dynamic method call, array-like, statically
[<class_name>, <method_name>](arguments)

Basado principalmente en esta charla PHP .

MAChitgarha
fuente
1
Buena lista de expresiones, también incluye:(expressions)->$bar()
Necro
1
@Necro ¡Gracias, lo agregué!
MAChitgarha
7

Nombres de funciones dinámicas y espacios de nombres

Solo para agregar un punto sobre los nombres de funciones dinámicas cuando se usan espacios de nombres.

Si está utilizando espacios de nombres, lo siguiente no funcionará, excepto si su función está en el espacio de nombres global:

namespace greetings;

function hello()
{
    // do something
}

$myvar = "hello";
$myvar(); // interpreted as "\hello();"

¿Qué hacer?

Tienes que usar call_user_func()en su lugar:

// if hello() is in the current namespace
call_user_func(__NAMESPACE__.'\\'.$myvar);

// if hello() is in another namespace
call_user_func('mynamespace\\'.$myvar);
Jivan
fuente
3

Complementando la respuesta de @Chris K si desea llamar al método de un objeto, puede llamarlo usando una sola variable con la ayuda de un cierre:

function get_method($object, $method){
    return function() use($object, $method){
        $args = func_get_args();
        return call_user_func_array(array($object, $method), $args);           
    };
}

class test{        

    function echo_this($text){
        echo $text;
    }
}

$test = new test();
$echo = get_method($test, 'echo_this');
$echo('Hello');  //Output is "Hello"

Publiqué otro ejemplo aquí

David
fuente
3

Lo que aprendí de esta pregunta y las respuestas. ¡Gracias a todos!

Digamos que tengo estas variables y funciones:

$functionName1 = "sayHello";
$functionName2 = "sayHelloTo";
$functionName3 = "saySomethingTo";

$friend = "John";
$datas = array(
    "something"=>"how are you?",
    "to"=>"Sarah"
);

function sayHello()
{
echo "Hello!";
}

function sayHelloTo($to)
{
echo "Dear $to, hello!";
}

function saySomethingTo($something, $to)
{
echo "Dear $to, $something";
}
  1. Para llamar a la función sin argumentos

    // Calling sayHello()
    call_user_func($functionName1); 

    ¡Hola!

  2. Llamar a la función con 1 argumento

    // Calling sayHelloTo("John")
    call_user_func($functionName2, $friend);

    Querido John, hola!

  3. Para llamar a la función con 1 o más argumentos Esto será útil si está llamando dinámicamente a sus funciones y cada función tiene un número diferente de argumentos. Este es mi caso que he estado buscando (y resuelto). call_user_func_arrayes la llave

    // You can add your arguments
    // 1. statically by hard-code, 
    $arguments[0] = "how are you?"; // my $something
    $arguments[1] = "Sarah"; // my $to
    
    // 2. OR dynamically using foreach
    $arguments = NULL;
    foreach($datas as $data) 
    {
        $arguments[] = $data;
    }
    
    // Calling saySomethingTo("how are you?", "Sarah")
    call_user_func_array($functionName3, $arguments);

    Querida Sarah, ¿cómo estás?

Adios adios

asmasyakirah
fuente
2

Use la función call_user_func.

PaulJWilliams
fuente
1

El siguiente código puede ayudar a escribir funciones dinámicas en PHP. ahora el nombre de la función se puede cambiar dinámicamente por la variable '$ current_page'.

$current_page = 'home_page';
$function = @${$current_page . '_page_versions'};
$function = function() {
    echo 'current page';
};
$function();
rakesh.falke
fuente
Las funciones anónimas (es decir, funciones dinámicas) difieren de las funciones variables (es decir, llamar a las funciones dinámicamente). Además, está reemplazando la $functionvariable sin ningún motivo.
MAChitgarha
0

La forma más fácil de llamar a una función de forma segura utilizando el nombre almacenado en una variable es,

//I want to call method deploy that is stored in functionname 
$functionname = 'deploy';

$retVal = {$functionname}('parameters');

He usado lo siguiente para crear tablas de migración en Laravel dinámicamente,

foreach(App\Test::$columns as $name => $column){
        $table->{$column[0]}($name);
}
Debashis Prusty
fuente
-5

Un enfoque poco convencional que se me ocurrió es que, a menos que esté generando todo el código a través de una IA súper ultra autónoma que se escribe , hay muchas posibilidades de que las funciones que desea llamar "dinámicamente" ya estén definidas en su código base. Entonces, ¿por qué no solo verificas la cuerda y haces el infame ifelsebaile para convocar a ... entiendes mi punto?

p.ej.

if($functionName == 'foo'){
  foo();
} else if($functionName == 'bar'){
  bar();
}

Incluso switch-casepuede usarse si no le gusta el sabor suave de la ifelseescalera.

Entiendo que hay casos en los que " llamar dinámicamente la función " sería una necesidad absoluta ( como una lógica recursiva que se modifica a sí misma ). Pero la mayoría de los casos de uso triviales cotidianos se pueden esquivar.

Elimina mucha incertidumbre de su aplicación, a la vez que le da la oportunidad de ejecutar una función alternativa si la cadena no coincide con ninguna de las definiciones de las funciones disponibles. EN MI HUMILDE OPINIÓN.

Mohd Abdul Mujib
fuente
Si el valor de la variable ya es el nombre de la función, ¿por qué el trabajo extra? También sería bueno obtener comentarios sólidos para ver si la función existe por php antes de ejecutarla: if (method_exists($functionName)) $functionName(); else //exception or handle
Necro
2
Sí, su punto es válido, pero dado que el nombre de la función es dinámico, en su lógica cualquier cadena que venga se ejecutará si existe una función con ese nombre, independientemente de si esa función es relevante en la situación actual o no. Mi enfoque se asegura de que solo se puedan ejecutar ciertas funciones, de lo contrario se puede generar un error. Puede ser simplificado por arrays y como tal.
Mohd Abdul Mujib
-11

No sé por qué tienes que usar eso, no me suena tan bien, pero si solo hay una pequeña cantidad de funciones, podrías usar una construcción if / elseif. No sé si es posible una solución directa.

algo así como $ foo = "bar"; $ prueba = "foo"; prueba de eco $$;

debería volver a la barra, puedes probar pero no creo que esto funcione para las funciones

Flo
fuente