Come chiamare una funzione da una stringa memorizzata in una variabile?


288

Devo essere in grado di chiamare una funzione, ma il nome della funzione è memorizzato in una variabile, è possibile? per esempio:

funzione foo ()
{
  // codice qui
}

barra delle funzioni ()
{
  // codice qui
}

$ functionName = "foo";
// Ho bisogno di chiamare la funzione in base a $ functionName

Risposte:


464

85
Se è necessario chiamare la funzione di un oggetto il cui nome è in una variabile, procedere come segue: call_user_func (array ($ obj, $ func), $ params)
BlackDivine,

1
Vedi anche la mia risposta qui sotto se hai a che fare con classi e metodi. Non mi aspettavo quello che ho trovato.
Chris K,

Come posso annullare l'errore di definizione della funzione se la funzione non è stata definita? Se ($ functionName) è sufficiente?
SaidbakR,

14
@ sємsєм: dovresti usare la is_callablefunzione. Verificherà se una variabile contiene qualcosa che può essere chiamato come una funzione (sia esso il nome di una funzione, un array contenente un'istanza di oggetto e un nome di funzione o una funzione / chiusura anonima)
knittl

6
@Pacerier: fornire il nome della classe come primo elemento dell'array:call_user_func(array('ClassName', 'method'), $args)
knittl

121

La mia versione preferita è la versione in linea:

${"variableName"} = 12;

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

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

Puoi anche inserire variabili o espressioni tra parentesi!


1
@marcioAlmada: pubblica un commento invece di modificare le risposte degli altri in questo modo; aggiunge confusione - non è chiaro chi ha detto cosa [ai curiosi: vedi revisioni per spiegazioni]
kryger

4
Ok @kryger. La riga 2 {"functionName"}();non è legale e genererà un errore di sintassi.
marcio,

4
Grazie ragazzi, per le risposte, in effetti genera un errore, anche in php 5.4, quindi la sintassi funziona solo con metodi oggetto. Ha modificato il post.
Lajos Meszaros,


17

Come già accennato, ci sono alcuni modi per raggiungere questo obiettivo con il metodo forse più sicuro call_user_func()o, se necessario, puoi anche seguire il percorso di $function_name(). È possibile passare argomenti usando entrambi questi metodi in questo modo

$function_name = 'foobar';

$function_name(arg1, arg2);

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

Se la funzione che stai chiamando appartiene a un oggetto, puoi comunque utilizzare uno di questi

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

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

Tuttavia, se si intende utilizzare il $function_name()metodo, può essere una buona idea verificare l'esistenza della funzione se il nome è in qualche modo dinamico

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

12

Nel caso in cui qualcun altro venga portato qui da Google perché stava cercando di utilizzare una variabile per un metodo all'interno di una classe, il seguente è un esempio di codice che funzionerà effettivamente. Nessuna delle precedenti ha funzionato per la mia situazione. La differenza chiave sta &nella dichiarazione di $c = & new...e &$cinoltrata call_user_func.

Il mio caso specifico è quando si implementa il codice di qualcuno che ha a che fare con colori e metodi a due membri lighten()e darken()dalla classe csscolor.php. Per qualsiasi motivo, volevo che lo stesso codice fosse in grado di chiamare schiarire o scurire piuttosto che selezionarlo con la logica. Questo potrebbe essere il risultato della mia testardaggine non solo per usare if-else o per cambiare il codice che chiama questo metodo.

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

Nota che provare qualcosa con $c->{...}non ha funzionato. Dopo aver esaminato il contenuto fornito dal lettore nella parte inferiore della pagina di php.net call_user_func, sono stato in grado di mettere insieme quanto sopra. Inoltre, nota che $paramscome un array non ha funzionato per me:

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

Questo precedente tentativo darebbe un avvertimento sul metodo in attesa di un secondo argomento (percentuale).


cosa fa "$ c = & new CSS_Color"? Sto parlando dell'amplificatore (&). Che cosa cambia e questo?
Danon,

@danon & è "indirizzo di" o l'operatore "riferimento". Sto istiantando un nuovo oggetto della classe CSS_Color e quindi assegnando il suo riferimento (aka indirizzo) a $ c. Odio ripetere il codice, quindi uso spesso nomi di variabili variabili.
Chris K,

1
Non capisco. Gli oggetti non sono comunque passati per riferimento ovunque? Voglio dire, se passi una $ c a una funzione e la cambi, anche l'oggetto originale sarà influenzato, giusto? Non importa se lo hai usato o no, ho ragione?
Danon,

Ho provato diverse combinazioni di simboli e solo mentre leggevo i commenti degli utenti su php.org sono riuscito a ottenere il codice funzionante. Dovrai iniziare quella discussione con le persone responsabili del comportamento di php.
Chris K,

Per il secondo esempio che hai letto male, stavi cercando call_user_func_array
Tofandel

12

Qualche anno in ritardo, ma questo è il modo migliore ora imho:

$x = (new ReflectionFunction("foo"))->getClosure();
$x();

3
Modo per OOP per me, ma ho dato un +1, dal momento che nessuno ha menzionato ancora le lezioni di Reflection.
Lajos Meszaros,

Non capisco questa risposta. Che problema sta risolvendo, esattamente?
David Spector, il

9

Per completezza, puoi anche usare eval () :

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

Tuttavia, call_user_func()è il modo corretto.


8
Va notato che eval () consentirà lo stesso comportamento ma consentirebbe anche di eseguire qualsiasi codice PHP. Quindi la variabile potrebbe essere utilizzata per dirottare il sistema. Call_user_func () non porta tali problemi di sicurezza.
Giovanni,

4
evalnon è una soluzione a questa domanda.
aprile

1
Per favore, non farlo perché un utente malintenzionato potrebbe cancellare completamente il tuo server.
andreszs,

9

Soluzione: utilizzare PHP7

Nota: per una versione riepilogata, vedere TL; DR alla fine della risposta.

Metodi antichi

Aggiornamento : uno dei vecchi metodi spiegati qui è stato rimosso. Fare riferimento ad altre risposte per spiegazioni su altri metodi, non è trattato qui. A proposito, se questa risposta non ti aiuta, dovresti tornare ad aggiornare le tue cose. Il supporto di PHP 5.6 è terminato a gennaio 2019 (ora anche PHP 7.0 e 7.1 non sono supportati). Vedi le versioni supportate per ulteriori informazioni.

Come altri hanno già detto, in PHP5 (e anche nelle versioni più recenti come PHP7) potremmo usare le variabili come nomi di funzione, uso call_user_func()e call_user_func_array()(che, personalmente, odio quelle funzioni), ecc.

Nuovi metodi

A partire da PHP7, ci sono nuovi modi introdotti:

Nota: tutto tra <something>parentesi indica una o più espressioni per formare qualcosa, ad es. <function_name>Espressioni che formano un nome di funzione.

Chiamata dinamica delle funzioni: nome della funzione al volo

Possiamo usare una o più espressioni tra parentesi come nome della funzione in una sola volta, sotto forma di:

(<function_name>)(arguments);

Per esempio:

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: sebbene rimuovere le parentesi str_replace()non sia un errore, l'inserimento di parentesi rende il codice più leggibile. Tuttavia, a volte non è possibile farlo, ad esempio durante l'utilizzo .dell'operatore. Per essere coerenti, ti consiglio di mettere sempre le parentesi.

Chiamata dinamica al metodo: nome del metodo al volo

Proprio come le chiamate di funzione dinamiche, possiamo fare lo stesso con le chiamate di metodo, circondate da parentesi graffe anziché da parentesi (per i moduli extra, vai alla sezione TL; DR):

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

Guardalo in un esempio:

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

$bar = "another thing";

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

Chiamata a metodo dinamico: la sintassi dell'array

Un modo più elegante aggiunto in PHP7 è il seguente:

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

Come esempio:

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: il vantaggio di utilizzare questo metodo rispetto al precedente è che non ti interessa il tipo di chiamata (ovvero se è statico o meno).

Esempio aggiuntivo: utilizzo di classi anonime

Rendendo le cose un po 'complicate, potresti usare una combinazione di classi anonime e le funzionalità sopra:

$bar = "SomeThing";

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

TL; DR (conclusione)

Generalmente, in PHP7, sono possibili tutti i seguenti moduli:

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

Basato principalmente su questo discorso di PHP .


1
Bel elenco di espressioni, include anche:(expressions)->$bar()
Necro

1
@Necro Grazie, l'ho aggiunto!
MAChitgarha,

7

Nomi e spazi dei nomi delle funzioni dinamiche

Solo per aggiungere un punto sui nomi di funzioni dinamiche quando si usano gli spazi dei nomi.

Se si utilizzano spazi dei nomi, i seguenti non funzioneranno, tranne se la funzione è nello spazio dei nomi globale:

namespace greetings;

function hello()
{
    // do something
}

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

Cosa fare?

Devi usare call_user_func()invece:

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

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

3

Completando la risposta di @Chris K se vuoi chiamare il metodo di un oggetto, puoi chiamarlo usando una singola variabile con l'aiuto di una chiusura:

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"

Ho pubblicato un altro esempio qui


3

Cosa ho imparato da questa domanda e le risposte. Ringrazia tutti!

Supponiamo che io abbia queste variabili e funzioni:

$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. Per chiamare la funzione senza argomenti

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

    Ciao!

  2. Per chiamare la funzione con 1 argomento

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

    Caro Giovanni, ciao!

  3. Per chiamare la funzione con 1 o più argomenti Questo sarà utile se si chiamano dinamicamente le proprie funzioni e ciascuna funzione ha un numero diverso di argomenti. Questo è il mio caso che stavo cercando (e risolto). call_user_func_arrayè la chiave

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

    Cara Sarah, come stai?

Ciao ciao!



1

Il seguente codice può aiutare a scrivere la funzione dinamica in PHP. ora il nome della funzione può essere modificato dinamicamente dalla variabile '$ current_page'.

$current_page = 'home_page';
$function = @${$current_page . '_page_versions'};
$function = function() {
    echo 'current page';
};
$function();

Le funzioni anonime (es. Funzioni dinamiche) differiscono dalle funzioni variabili (es. Chiamate dinamiche). Inoltre, stai sostituendo la $functionvariabile senza alcun motivo.
MAChitgarha,

0

Il modo più semplice per chiamare una funzione in modo sicuro utilizzando il nome memorizzato in una variabile è,

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

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

Ho usato come di seguito per creare dinamicamente tabelle di migrazione in Laravel,

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

-5

Un approccio non convenzionale, che mi è venuto in mente è, a meno che tu non stia generando l'intero codice attraverso un'intelligenza artificiale ultra ultra autonoma che si scrive da solo , ci sono alte probabilità che le funzioni che vuoi chiamare "dinamicamente" siano già definite nel tuo codice base. Quindi perché non controllare solo la corda e fare la ifelsedanza infame per evocare il ... ottieni il mio punto.

per esempio.

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

Anche switch-casepuò essere utilizzato se non ti piace il gusto dolce della ifelsescala.

Capisco che ci sono casi in cui la " chiamata dinamica della funzione " sarebbe una necessità assoluta ( come una logica ricorsiva che si modifica da sola ). Ma la maggior parte dei banali casi d'uso quotidiani possono essere schivati.

Elimina molte incertezze dalla tua applicazione, dandoti la possibilità di eseguire una funzione di fallback se la stringa non corrisponde a nessuna delle definizioni delle funzioni disponibili. A PARER MIO.


Se il valore della variabile è già il nome della funzione, perché il lavoro extra? inoltre sarebbe bene avere un feedback solido per vedere se la funzione esiste da php prima di eseguirla: if (method_exists($functionName)) $functionName(); else //exception or handle
Necro

2
Sì, il tuo punto è valido, ma poiché il nome della funzione è dinamico, nella tua logica qualsiasi stringa che viene deve essere eseguita se esiste una funzione con quel nome, indipendentemente dal fatto che quella funzione sia rilevante nella situazione attuale o meno. Il mio approccio si assicura che solo un certo gruppo di funzioni possa essere eseguito, altrimenti può essere generato un errore. Può essere semplificato da array e come tale.
Mohd Abdul Mujib il

-11

Non so perché devi usarlo, non suona affatto bene per me, ma se ci sono solo una piccola quantità di funzioni, potresti usare un costrutto if / elseif. Non so se sia possibile una soluzione diretta.

qualcosa come $ foo = "bar"; $ test = "pippo"; test echo $$;

dovrebbe tornare barra, puoi provare ma non credo che questo funzionerà per le funzioni

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.