How to call a function from a string stored in a variable?

后端 未结 16 2300
予麋鹿
予麋鹿 2020-11-22 10:16

I need to be able to call a function, but the function name is stored in a variable, is this possible? e.g:

function foo ()
{
  //code here
}

function bar ()
{
          


        
相关标签:
16条回答
  • 2020-11-22 11:01

    What I learnt from this question and the answers. Thanks all!

    Let say I have these variables and functions:

    $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. To call function without arguments

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

      Hello!

    2. To call function with 1 argument

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

      Dear John, hello!

    3. To call function with 1 or more arguments This will be useful if you are dynamically calling your functions and each function have different number of arguments. This is my case that I have been looking for (and solved). call_user_func_array is the key

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

      Dear Sarah, how are you?

    Yay bye!

    0 讨论(0)
  • 2020-11-22 11:02

    The easiest way to call a function safely using the name stored in a variable is,

    //I want to call method deploy that is stored in functionname 
    $functionname = 'deploy';
    
    $retVal = {$functionname}('parameters');
    

    I have used like below to create migration tables in Laravel dynamically,

    foreach(App\Test::$columns as $name => $column){
            $table->{$column[0]}($name);
    }
    
    0 讨论(0)
  • 2020-11-22 11:03

    One unconventional approach, that came to my mind is, unless you are generating the whole code through some super ultra autonomous AI which writes itself, there are high chances that the functions which you want to "dynamically" call, are already defined in your code base. So why not just check for the string and do the infamous ifelse dance to summon the ...you get my point.

    eg.

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

    Even switch-case can be used if you don't like the bland taste of ifelse ladder.

    I understand that there are cases where the "dynamically calling the function" would be an absolute necessity (Like some recursive logic which modifies itself). But most of the everyday trivial use-cases can just be dodged.

    It weeds out a lot of uncertainty from your application, while giving you a chance to execute a fallback function if the string doesn't match any of the available functions' definition. IMHO.

    0 讨论(0)
  • 2020-11-22 11:05

    Dynamic function names and namespaces

    Just to add a point about dynamic function names when using namespaces.

    If you're using namespaces, the following won't work except if your function is in the global namespace:

    namespace greetings;
    
    function hello()
    {
        // do something
    }
    
    $myvar = "hello";
    $myvar(); // interpreted as "\hello();"
    

    What to do?

    You have to use call_user_func() instead:

    // if hello() is in the current namespace
    call_user_func(__NAMESPACE__.'\\'.$myvar);
    
    // if hello() is in another namespace
    call_user_func('mynamespace\\'.$myvar);
    
    0 讨论(0)
  • 2020-11-22 11:05

    I dont know why u have to use that, doesnt sound so good to me at all, but if there are only a small amount of functions, you could use a if/elseif construct. I dont know if a direct solution is possible.

    something like $foo = "bar"; $test = "foo"; echo $$test;

    should return bar, you can try around but i dont think this will work for functions

    0 讨论(0)
  • 2020-11-22 11:11

    Solution: Use PHP7

    Note: For a summarized version, see TL;DR at the end of the answer.

    Old Methods

    Update: One of the old methods explained here has been removed. Refer to other answers for explanation on other methods, it is not covered here. By the way, if this answer doesn't help you, you should return upgrading your stuff. PHP 5.6 support has ended in January 2019 (now even PHP 7.0 and 7.1 are not being supported). See supported versions for more information.

    As others mentioned, in PHP5 (and also in newer versions like PHP7) we could use variables as function names, use call_user_func() and call_user_func_array() (which, personally, I hate those functions), etc.

    New Methods

    As of PHP7, there are new ways introduced:

    Note: Everything inside <something> brackets means one or more expressions to form something, e.g. <function_name> means expressions forming a function name.

    Dynamic Function Call: Function Name On-the-fly

    We can use one or more expressions inside parentheses as the function name in just one go, in the form of:

    (<function_name>)(arguments);
    

    For example:

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

    Note: Although removing the parentheses around str_replace() is not an error, putting parentheses makes code more readable. However, you cannot do that sometimes, e.g. while using . operator. To be consistent, I recommend you to put the parentheses always.

    Dynamic Method Call: Method Name On-the-fly

    Just like dynamic function calls, we can do the same way with method calls, surrounded by curly braces instead of parentheses (for extra forms, navigate to TL;DR section):

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

    See it in an example:

    class Foo
    {
        public function another(): string
        {
            return "something";
        }
    }
    
    $bar = "another thing";
    
    (new Something())->{explode(" ", $bar)[0]}(); // something
    

    Dynamic Method Call: The Array Syntax

    A more elegant way added in PHP7 is the following:

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

    As an example:

    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
    

    Note: The benefit of using this method over the previous one is that, you don't care about the call type (i.e. whether it's static or not).

    Note: If you care about performance and micro-optimizations, don't use this method. As I tested, this method is really slower than other methods (more than 10 times).

    Extra Example: Using Anonymous Classes

    Making things a bit complicated, you could use a combination of anonymous classes and the features above:

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

    TL;DR (Conclusion)

    Generally, in PHP7, using the following forms are all 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)
    

    Special thanks to this PHP talk.

    0 讨论(0)
提交回复
热议问题