Wie rufe ich eine Funktion aus einer in einer Variablen gespeicherten Zeichenfolge auf?

288

Ich muss in der Lage sein, eine Funktion aufzurufen, aber der Funktionsname ist in einer Variablen gespeichert. Ist dies möglich? z.B:

Funktion foo ()
{
  // Code hier
}}

Funktionsleiste ()
{
  // Code hier
}}

$ functionName = "foo";
// Ich muss die Funktion basierend auf $ functionName aufrufen
Eidechse
quelle

Antworten:

464

$functionName() oder call_user_func($functionName)

stricken
quelle
85
Wenn Sie die Funktion eines Objekts aufrufen müssen, dessen Name in einer Variablen enthalten ist, gehen Sie wie folgt vor: call_user_func (Array ($ obj, $ func), $ params)
BlackDivine
1
Siehe auch meine Antwort unten, wenn Sie sich mit Klassen und Methoden beschäftigen. Ich habe nicht erwartet, was ich gefunden habe.
Chris K
Wie kann ich einen Funktionsdefinitionsfehler aufheben, wenn die Funktion nicht definiert wurde? Reicht wenn ($ functionName) aus?
SaidbakR
14
@ sємsєм: Sie sollten die is_callableFunktion verwenden. Es wird geprüft, ob eine Variable etwas enthält, das als Funktion aufgerufen werden kann (sei es der Name einer Funktion, ein Array, das eine Objektinstanz und einen Funktionsnamen enthält, oder eine anonyme Funktion / Schließung)
knittl
6
@ Pacerier: Geben Sie den Klassennamen als erstes Element des Arrays an:call_user_func(array('ClassName', 'method'), $args)
knittl
121

Meine Lieblingsversion ist die Inline-Version:

${"variableName"} = 12;

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

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

Sie können auch Variablen oder Ausdrücke in die Klammern setzen!

Lajos Meszaros
quelle
1
@marcioAlmada: poste einen Kommentar, anstatt die Antworten anderer auf diese Weise zu bearbeiten; es fügt Verwirrung hinzu - es ist nicht klar, wer was gesagt hat [zu den Neugierigen: Erklärungen siehe Revisionen]
kryger
4
Ok @kryger. Zeile 2 {"functionName"}();ist nicht legal und löst einen Syntaxfehler aus.
Marcio
4
Vielen Dank, für die Antworten, es gibt tatsächlich einen Fehler, sogar in PHP 5.4, so dass die Syntax nur mit Objektmethoden funktioniert. Den Beitrag bearbeitet.
Lajos Meszaros
17

Wie bereits erwähnt, gibt es einige Möglichkeiten, dies mit der möglicherweise sichersten Methode zu erreichen, call_user_func()oder wenn Sie müssen, können Sie auch den Weg von gehen $function_name(). Es ist möglich, Argumente mit diesen beiden Methoden zu übergeben

$function_name = 'foobar';

$function_name(arg1, arg2);

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

Wenn die von Ihnen aufgerufene Funktion zu einem Objekt gehört, können Sie beide verwenden

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

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

Wenn Sie jedoch die $function_name()Methode verwenden möchten, ist es möglicherweise eine gute Idee, die Existenz der Funktion zu testen, wenn der Name in irgendeiner Weise dynamisch ist

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

Falls jemand anderes von Google hierher gebracht wird, weil er versucht hat, eine Variable für eine Methode innerhalb einer Klasse zu verwenden, ist das folgende Codebeispiel, das tatsächlich funktioniert. Keiner der oben genannten Punkte hat für meine Situation funktioniert. Der Hauptunterschied besteht &in der Erklärung $c = & new...und &$cWeitergabe call_user_func.

Mein spezieller Fall ist, wenn jemandes Code implementiert wird, der mit Farben und zwei Mitgliedsmethoden lighten()und darken()aus der Klasse csscolor.php zu tun hat . Aus irgendeinem Grund wollte ich, dass derselbe Code in der Lage ist, Aufhellen oder Abdunkeln aufzurufen, anstatt ihn mit Logik auszuwählen. Dies kann das Ergebnis meiner Sturheit sein, nicht nur if-else zu verwenden oder den Code zu ändern, der diese Methode aufruft.

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

Beachten Sie, dass das Ausprobieren $c->{...}nicht funktioniert hat. Nachdem ich den vom Leser bereitgestellten Inhalt am Ende der Seite von php.net gelesen hatte call_user_func, konnte ich das Obige zusammenfügen. Beachten Sie auch, dass $paramsein Array bei mir nicht funktioniert hat:

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

Dieser obige Versuch würde eine Warnung über die Methode geben, die ein zweites Argument (Prozent) erwartet.

Chris K.
quelle
Was macht "$ c = & new CSS_Color"? Ich spreche über den Verstärker (&). Was ändert sich das?
Danon
@danon & ist "Adresse von" oder der "Referenz" -Operator. Ich instanziiere ein neues Objekt der Klasse CSS_Color und weise dann $ c seine Referenz (auch bekannt als Adresse) zu. Ich hasse es, Code zu wiederholen, deshalb verwende ich oft variable Variablennamen.
Chris K
1
Ich verstehe nicht. Werden Objekte nicht sowieso überall als Referenz übergeben? Ich meine, wenn Sie ein $ c an eine Funktion übergeben und ändern, wird auch das ursprüngliche Objekt betroffen sein, oder? Egal ob du das benutzt hast oder nicht, habe ich recht?
Danon
Ich habe verschiedene Kombinationen von Symbolen ausprobiert und nur beim Lesen der Benutzerkommentare auf php.org konnte ich Arbeitscode erhalten. Sie müssen diese Diskussion mit Personen beginnen, die für das Verhalten von PHP verantwortlich sind.
Chris K
Für das zweite Beispiel, das Sie falsch verstanden haben, haben Sie nach call_user_func_array
Tofandel
12

Ein paar Jahre zu spät, aber das ist jetzt imho die beste Art:

$x = (new ReflectionFunction("foo"))->getClosure();
$x();
interner Serverfehler
quelle
3
Weg zu OOP für mich, aber ich habe es mit +1 bewertet, da noch niemand Reflection-Klassen erwähnt hat.
Lajos Meszaros
Verstehe diese Antwort nicht. Welches Problem löst es genau?
David Spector
9

Der Vollständigkeit halber können Sie auch eval () verwenden :

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

Ist call_user_func()jedoch der richtige Weg.

karim79
quelle
8
Es sollte beachtet werden, dass eval () das gleiche Verhalten zulässt, aber auch die Ausführung von PHP-Code ermöglicht. Die Variable könnte also verwendet werden, um das System zu entführen. Call_user_func () bringt solche Sicherheitsprobleme nicht mit sich.
John
4
evalist keine Lösung für diese Frage.
ankr
1
Bitte tun Sie dies nicht, da ein Angreifer Ihren Server vollständig auslöschen könnte.
andreszs
9

Lösung: Verwenden Sie PHP7

Hinweis: Eine zusammengefasste Version finden Sie unter TL; DR am Ende der Antwort.

Alte Methoden

Update : Eine der hier erläuterten alten Methoden wurde entfernt. Weitere Erläuterungen zu anderen Methoden finden Sie in anderen Antworten. Diese werden hier nicht behandelt. Übrigens, wenn diese Antwort Ihnen nicht hilft, sollten Sie Ihre Inhalte erneut aktualisieren. Die Unterstützung für PHP 5.6 wurde im Januar 2019 beendet (jetzt werden sogar PHP 7.0 und 7.1 nicht mehr unterstützt). Siehe unterstützte VersionenWeitere Informationen finden .

Wie bereits erwähnt, könnten wir in PHP5 (und auch in neueren Versionen wie PHP7) Variablen als Funktionsnamen verwenden, verwenden call_user_func()undcall_user_func_array() (was ich persönlich hasse, diese Funktionen) usw.

Neue Methoden

Ab PHP7 wurden neue Möglichkeiten eingeführt:

Hinweis: Alles in <something>Klammern bedeutet einen oder mehrere Ausdrücke, um etwas zu bilden, z. B. <function_name>Ausdrücke, die einen Funktionsnamen bilden.

Dynamischer Funktionsaufruf: Funktionsname On-the-Fly

Wir können einen oder mehrere Ausdrücke in Klammern als Funktionsnamen in nur einem Durchgang verwenden, und zwar in Form von:

(<function_name>)(arguments);

Beispielsweise:

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

Hinweis: Obwohl das Entfernen der Klammern str_replace()kein Fehler ist, macht das Setzen von Klammern den Code lesbarer. Dies ist jedoch manchmal nicht möglich, z. B. während der Verwendung. . Operator verwenden. Um konsistent zu sein, empfehle ich Ihnen, die Klammern immer zu setzen.

Dynamischer Methodenaufruf: Methodenname On-the-Fly

Genau wie bei dynamischen Funktionsaufrufen können wir dies auch mit Methodenaufrufen tun, die von geschweiften Klammern anstelle von Klammern umgeben sind (für zusätzliche Formulare navigieren Sie zum Abschnitt TL; DR):

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

Sehen Sie es in einem Beispiel:

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

$bar = "another thing";

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

Dynamischer Methodenaufruf: Die Array-Syntax

Ein eleganterer Weg, der in PHP7 hinzugefügt wird, ist der folgende:

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

Als Beispiel:

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

Hinweis: Der Vorteil dieser Methode gegenüber der vorherigen besteht darin, dass Sie sich nicht für den Anruftyp interessieren (dh ob er statisch ist oder nicht).

Zusätzliches Beispiel: Verwenden anonymer Klassen

Um die Dinge etwas komplizierter zu machen, können Sie eine Kombination aus anonymen Klassen und den oben genannten Funktionen verwenden:

$bar = "SomeThing";

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

TL; DR (Schlussfolgerung)

Im Allgemeinen sind in PHP7 alle folgenden Formulare möglich:

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

Hauptsächlich basierend auf diesem PHP-Vortrag .

MAChitgarha
quelle
1
Schöne Liste von Ausdrücken, enthält auch:(expressions)->$bar()
Necro
1
@Necro Danke, ich habe es hinzugefügt!
MAChitgarha
7

Dynamische Funktionsnamen und Namespaces

Nur um einen Punkt über dynamische Funktionsnamen hinzuzufügen, wenn Namespaces verwendet werden.

Wenn Sie Namespaces verwenden, funktioniert Folgendes nur, wenn sich Ihre Funktion im globalen Namespace befindet:

namespace greetings;

function hello()
{
    // do something
}

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

Was ist zu tun?

Sie müssen call_user_func()stattdessen verwenden:

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

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

Wenn Sie die Antwort von @Chris K ergänzen und die Methode eines Objekts aufrufen möchten, können Sie sie mit Hilfe eines Abschlusses mithilfe einer einzelnen Variablen aufrufen:

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"

Ich habe hier ein weiteres Beispiel gepostet

David
quelle
3

Was ich aus dieser Frage und den Antworten gelernt habe. Vielen Dank an alle!

Angenommen, ich habe diese Variablen und Funktionen:

$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. Funktion ohne Argumente aufrufen

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

    Hallo!

  2. Funktion mit 1 Argument aufrufen

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

    Lieber John, hallo!

  3. So rufen Sie eine Funktion mit einem oder mehreren Argumenten auf Dies ist nützlich, wenn Sie Ihre Funktionen dynamisch aufrufen und jede Funktion eine andere Anzahl von Argumenten hat. Dies ist mein Fall, den ich gesucht (und gelöst) habe. call_user_func_arrayist der Schlüssel

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

    Liebe Sarah, wie geht es dir?

Yay bye!

asmasyakirah
quelle
2

Verwenden Sie die Funktion call_user_func.

PaulJWilliams
quelle
1

Der folgende Code kann helfen, dynamische Funktionen in PHP zu schreiben. Jetzt kann der Funktionsname durch die Variable '$ current_page' dynamisch geändert werden.

$current_page = 'home_page';
$function = @${$current_page . '_page_versions'};
$function = function() {
    echo 'current page';
};
$function();
rakesh.falke
quelle
Anonyme Funktionen (dh dynamische Funktionen) unterscheiden sich von variablen Funktionen (dh dynamischer Aufruf von Funktionen). Außerdem ersetzen Sie $functionVariablen ohne Angabe von Gründen.
MAChitgarha
0

Der einfachste Weg, eine Funktion sicher unter Verwendung des in einer Variablen gespeicherten Namens aufzurufen, ist:

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

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

Ich habe wie unten verwendet, um Migrationstabellen in Laravel dynamisch zu erstellen.

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

Ein unkonventioneller Ansatz, der mir in den Sinn kam, ist, dass die Funktionen, die Sie "dynamisch" aufrufen möchten, bereits in Ihrem Code definiert sind, es sei denn, Sie generieren den gesamten Code durch eine superautonome KI, die sich selbst schreibt Base. Warum also nicht einfach nach der Saite suchen und das berüchtigte tun?ifelse Tanz machen, um die ... du verstehst, was ich meine?

z.B.

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

Sogar switch-case kann verwendet werden , wenn Sie den milden Geschmack von nicht wie tun ifelseLeiter.

Ich verstehe, dass es Fälle gibt, in denen die Funktion dynamisch aufgerufen wird " eine absolute Notwendigkeit wäre ( wie eine rekursive Logik, die sich selbst modifiziert ). Aber die meisten alltäglichen trivialen Anwendungsfälle können einfach ausgewichen werden.

Es beseitigt eine Menge Unsicherheiten in Ihrer Anwendung und gibt Ihnen die Möglichkeit, eine Fallback-Funktion auszuführen, wenn die Zeichenfolge keiner der verfügbaren Funktionsdefinitionen entspricht. MEINER BESCHEIDENEN MEINUNG NACH.

Mohd Abdul Mujib
quelle
Wenn der Wert der Variablen bereits der Name der Funktion ist, warum funktioniert die zusätzliche Arbeit? Es wäre auch gut, ein solides Feedback zu erhalten, um zu sehen, ob die Funktion von PHP existiert, bevor Sie sie ausführen: if (method_exists($functionName)) $functionName(); else //exception or handle
Necro
2
Ja, Ihr Punkt ist gültig, aber da der Funktionsname dynamisch ist, wird in Ihrer Logik jede Zeichenfolge ausgeführt, die kommt, wenn eine Funktion mit diesem Namen vorhanden ist, unabhängig davon, ob diese Funktion in der aktuellen Situation relevant ist oder nicht. Mein Ansatz stellt sicher, dass nur eine bestimmte Reihe von Funktionen ausgeführt werden kann, andernfalls kann ein Fehler ausgelöst werden. Es kann durch Arrays und als solche vereinfacht werden.
Mohd Abdul Mujib
-11

Ich weiß nicht, warum du das verwenden musst, klingt für mich überhaupt nicht so gut, aber wenn es nur eine kleine Anzahl von Funktionen gibt, könntest du ein if / elseif-Konstrukt verwenden. Ich weiß nicht, ob eine direkte Lösung möglich ist.

so etwas wie $ foo = "bar"; $ test = "foo"; echo $$ test;

Sollte die Leiste zurückgegeben werden, können Sie es versuchen, aber ich glaube nicht, dass dies für Funktionen funktioniert

Flo
quelle