Comment appeler une fonction à partir d'une chaîne stockée dans une variable?

288

Je dois pouvoir appeler une fonction, mais le nom de la fonction est stocké dans une variable, est-ce possible? par exemple:

fonction foo ()
{
  // code ici
}

barre de fonction ()
{
  // code ici
}

$ functionName = "foo";
// j'ai besoin d'appeler la fonction en fonction de ce qui est $ functionName
Lézard
la source

Réponses:

464

$functionName() ou call_user_func($functionName)

knittl
la source
85
Si vous devez appeler la fonction d'un objet dont le nom est dans une variable, procédez comme suit: call_user_func (array ($ obj, $ func), $ params)
BlackDivine
1
Voir aussi ma réponse ci-dessous si vous traitez avec des classes et des méthodes. Je ne m'attendais pas à ce que j'ai trouvé.
Chris K
Comment puis-je annuler une erreur de définition de fonction si la fonction n'a pas été définie? Est-ce que si ($ functionName) suffit?
SaidbakR
14
@ sємsєм: Vous devez utiliser la is_callablefonction. Il vérifiera si une variable contient quelque chose qui peut être appelé en tant que fonction (que ce soit le nom d'une fonction, un tableau contenant une instance d'objet et un nom de fonction, ou une fonction / fermeture anonyme)
knittl
6
@Pacerier: Fournissez le nom de classe comme premier élément du tableau:call_user_func(array('ClassName', 'method'), $args)
knittl
121

Ma version préférée est la version en ligne:

${"variableName"} = 12;

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

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

Vous pouvez également placer des variables ou des expressions entre crochets!

Lajos Meszaros
la source
1
@marcioAlmada: postez un commentaire au lieu de modifier les réponses des autres de cette manière; cela ajoute de la confusion - on ne sait pas qui a dit quoi [aux curieux: voir les révisions pour des explications]
kryger
4
Ok @kryger. La ligne 2 {"functionName"}();n'est pas légale et générera une erreur de syntaxe.
marcio
4
Merci les gars, pour les réponses, cela génère en effet une erreur, même en php 5.4, de sorte que la syntaxe ne fonctionne qu'avec les méthodes objet. Modifié le message.
Lajos Meszaros
17

Comme déjà mentionné, il existe plusieurs façons d'y parvenir, la méthode la plus sûre étant peut-être call_user_func()ou si vous le devez, vous pouvez également suivre la voie $function_name(). Il est possible de passer des arguments en utilisant ces deux méthodes

$function_name = 'foobar';

$function_name(arg1, arg2);

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

Si la fonction que vous appelez appartient à un objet, vous pouvez toujours utiliser

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

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

Cependant, si vous allez utiliser la $function_name()méthode, il peut être judicieux de tester l'existence de la fonction si le nom est en quelque sorte dynamique

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

Dans le cas où quelqu'un d'autre est amené ici par Google parce qu'il essayait d'utiliser une variable pour une méthode dans une classe, ce qui suit est un exemple de code qui fonctionnera réellement. Rien de ce qui précède n'a fonctionné pour ma situation. La principale différence réside &dans la déclaration $c = & new...et la &$ctransmission call_user_func.

Mon cas spécifique est celui de l'implémentation du code de quelqu'un concernant les couleurs et les deux méthodes membres lighten()et darken()de la classe csscolor.php. Pour une raison quelconque, je voulais que le même code puisse appeler éclaircir ou assombrir plutôt que de le sélectionner avec logique. Cela peut être le résultat de mon entêtement à ne pas simplement utiliser if-else ou à changer le code appelant cette méthode.

$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);

Notez qu'essayer quoi que ce soit avec $c->{...}n'a pas fonctionné. En parcourant le contenu fourni par le lecteur au bas de la page de php.net call_user_func, j'ai pu reconstituer ce qui précède. Notez $paramségalement qu'un tableau ne fonctionnait pas pour moi:

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

Cette tentative ci-dessus donnerait un avertissement sur la méthode qui attend un deuxième argument (pourcentage).

Chris K
la source
que fait "$ c = & new CSS_Color"? Je parle de l'ampli (&). Qu'est-ce que cela & change?
Danon
@danon & est "l'adresse de" ou l'opérateur "de référence". J'instancie un nouvel objet de classe CSS_Color puis j'affecte sa référence (aka adresse) à $ c. Je déteste répéter le code, donc j'utilise souvent des noms de variables variables.
Chris K
1
Je ne comprends pas. Les objets ne sont-ils pas passés par référence partout de toute façon? Je veux dire, si vous passez un $ c à une fonction et que vous la modifiez, l'objet d'origine sera également affecté, non? Peu importe que vous l'utilisiez ou non, ai-je raison?
Danon
J'ai essayé différentes combinaisons de symboles et ce n'est qu'en lisant les commentaires des utilisateurs sur php.org que j'ai pu obtenir du code de travail. Vous devrez commencer cette discussion avec les personnes responsables du comportement de php.
Chris K du
Pour le deuxième exemple que vous avez mal lu, vous cherchiez call_user_func_array
Tofandel
12

Quelques années de retard, mais c'est la meilleure manière maintenant à mon humble avis:

$x = (new ReflectionFunction("foo"))->getClosure();
$x();
Erreur Interne du Serveur
la source
3
Voie pour OOP pour moi, mais je lui ai donné un +1, car personne n'a encore mentionné de cours de réflexion.
Lajos Meszaros
Je ne comprends pas cette réponse. Quel problème résout-il exactement?
David Spector
9

Par souci d'exhaustivité, vous pouvez également utiliser eval () :

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

Cependant, call_user_func()c'est la bonne façon.

karim79
la source
8
Il est à noter que eval () permettra le même comportement mais il permettrait également d'exécuter n'importe quel code PHP. Ainsi, la variable pourrait être utilisée pour détourner le système. Le call_user_func () n'apporte pas de tels problèmes de sécurité.
John
4
evaln'est pas une solution à cette question.
ankr
1
Veuillez ne pas le faire car un attaquant pourrait anéantir complètement votre serveur.
andreszs
9

Solution: utilisez PHP7

Remarque: Pour une version résumée, voir TL; DR à la fin de la réponse.

Anciennes méthodes

Mise à jour : l'une des anciennes méthodes expliquées ici a été supprimée. Reportez-vous à d'autres réponses pour obtenir des explications sur d'autres méthodes, elles ne sont pas traitées ici. Soit dit en passant, si cette réponse ne vous aide pas, vous devriez retourner la mise à niveau de votre contenu. Le support de PHP 5.6 a pris fin en janvier 2019 (désormais même PHP 7.0 et 7.1 ne sont plus pris en charge). Voir les versions prises en charge pour plus d'informations.

Comme d'autres l'ont mentionné, en PHP5 (et également dans les versions plus récentes comme PHP7), nous pourrions utiliser des variables comme noms de fonction, utiliser call_user_func()et call_user_func_array()(qui, personnellement, je déteste ces fonctions), etc.

De nouvelles méthodes

Depuis PHP7, de nouvelles méthodes ont été introduites:

Remarque: tout ce qui se trouve <something>entre crochets signifie une ou plusieurs expressions pour former quelque chose, par exemple <function_name>, des expressions formant un nom de fonction.

Appel de fonction dynamique: nom de la fonction à la volée

Nous pouvons utiliser une ou plusieurs expressions entre parenthèses comme nom de fonction en une seule fois, sous la forme de:

(<function_name>)(arguments);

Par exemple:

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))()(); 

Remarque: Bien que la suppression des parenthèses autour str_replace()ne soit pas une erreur, la mise en parenthèses rend le code plus lisible. Cependant, vous ne pouvez pas le faire parfois, par exemple lorsque vous utilisez l' .opérateur. Pour être cohérent, je vous recommande de toujours mettre les parenthèses.

Appel de méthode dynamique: nom de méthode à la volée

Tout comme les appels de fonction dynamiques, nous pouvons faire de même avec les appels de méthode, entourés d'accolades au lieu de parenthèses (pour les formulaires supplémentaires, accédez à TL; section DR):

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

Voyez-le dans un exemple:

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

$bar = "another thing";

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

Appel de méthode dynamique: la syntaxe du tableau

Une manière plus élégante ajoutée en PHP7 est la suivante:

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

Par exemple:

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

Remarque: L'avantage d'utiliser cette méthode par rapport à la précédente est que vous ne vous souciez pas du type d'appel (c'est-à-dire qu'il soit statique ou non).

Exemple supplémentaire: utilisation de classes anonymes

Pour compliquer les choses, vous pouvez utiliser une combinaison de classes anonymes et des fonctionnalités ci-dessus:

$bar = "SomeThing";

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

TL; DR (Conclusion)

Généralement, en PHP7, l'utilisation des formes suivantes est possible:

// 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)

Basé principalement sur cette conversation PHP .

MAChitgarha
la source
1
Belle liste d'expressions, comprend également:(expressions)->$bar()
Necro
1
@Necro Merci, je l'ai ajouté!
MAChitgarha
7

Noms et espaces de noms dynamiques

Juste pour ajouter un point sur les noms de fonctions dynamiques lors de l'utilisation d'espaces de noms.

Si vous utilisez des espaces de noms, les éléments suivants ne fonctionneront que si votre fonction se trouve dans l'espace de noms global:

namespace greetings;

function hello()
{
    // do something
}

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

Que faire?

Vous devez utiliser à la call_user_func()place:

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

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

Complétant la réponse de @Chris K si vous voulez appeler la méthode d'un objet, vous pouvez l'appeler en utilisant une seule variable à l'aide d'une fermeture:

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"

J'ai posté un autre exemple ici

David
la source
3

Ce que j'ai appris de cette question et des réponses. Merci a tous!

Disons que j'ai ces variables et fonctions:

$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. Pour appeler une fonction sans arguments

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

    salut!

  2. Pour appeler une fonction avec 1 argument

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

    Cher John, bonjour!

  3. Pour appeler une fonction avec 1 ou plusieurs arguments Cela sera utile si vous appelez dynamiquement vos fonctions et que chaque fonction a un nombre d'arguments différent. C'est mon cas que j'ai recherché (et résolu). call_user_func_arrayC'est la clé

    // 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);

    Chère Sarah, comment vas-tu?

Au revoir!

asmasyakirah
la source
2

Utilisez la fonction call_user_func.

PaulJWilliams
la source
1

Le code suivant peut aider à écrire une fonction dynamique en PHP. maintenant le nom de la fonction peut être changé dynamiquement par la variable '$ current_page'.

$current_page = 'home_page';
$function = @${$current_page . '_page_versions'};
$function = function() {
    echo 'current page';
};
$function();
rakesh.falke
la source
Les fonctions anonymes (c'est-à-dire les fonctions dynamiques) diffèrent des fonctions variables (c'est-à-dire les fonctions d'appel de manière dynamique). En outre, vous remplacez $functionvariable sans aucune raison.
MAChitgarha
0

La façon la plus simple d'appeler une fonction en toute sécurité en utilisant le nom stocké dans une variable est,

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

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

J'ai utilisé comme ci-dessous pour créer dynamiquement des tables de migration dans Laravel,

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

Une approche non conventionnelle qui m'est venue à l'esprit est que, sauf si vous générez tout le code via une IA super ultra autonome qui s'auto-écrit , il y a de fortes chances que les fonctions que vous souhaitez appeler "dynamiquement" soient déjà définies dans votre code base. Alors pourquoi ne pas simplement vérifier la corde et faire la tristement célèbre ifelsedanse pour invoquer le ... vous obtenez mon point.

par exemple.

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

Même switch-casepeut être utilisé si vous n'aimez pas le goût fade de l' ifelseéchelle.

Je comprends qu'il y a des cas où " appeler dynamiquement la fonction " serait une nécessité absolue ( comme une logique récursive qui se modifie ). Mais la plupart des cas d'utilisation triviaux quotidiens peuvent simplement être évités.

Il élimine beaucoup d'incertitude de votre application, tout en vous donnant la possibilité d'exécuter une fonction de secours si la chaîne ne correspond à aucune des définitions des fonctions disponibles. A MON HUMBLE AVIS.

Mohd Abdul Mujib
la source
Si la valeur de la variable est déjà le nom de la fonction, pourquoi le travail supplémentaire? il serait également bon d'obtenir une rétroaction solide pour voir si la fonction existe par php avant de l'exécuter: if (method_exists($functionName)) $functionName(); else //exception or handle
Necro
2
Oui, votre point est valide, mais comme le nom de la fonction est dynamique, dans votre logique, toute chaîne qui vient doit être exécutée si une fonction de ce nom existe, que cette fonction soit pertinente dans la situation actuelle ou non. Mon approche s'assure que seul un certain groupe de fonctions peut être exécuté, sinon une erreur peut être levée. Il peut être simplifié par des tableaux et en tant que tels.
Mohd Abdul Mujib
-11

Je ne sais pas pourquoi vous devez utiliser cela, cela ne me semble pas si bon du tout, mais s'il n'y a qu'une petite quantité de fonctions, vous pouvez utiliser une construction if / elseif. Je ne sais pas si une solution directe est possible.

quelque chose comme $ foo = "bar"; $ test = "foo"; test d'écho $$;

devrait retourner la barre, vous pouvez essayer, mais je ne pense pas que cela fonctionnera pour les fonctions

Flo
la source