Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call a variable function using a class property as function name

Tags:

php

The following code uses the string "rand" stored in the property $prop to call rand() as a variable function, by using $function as a temporary local variable.

class C
{
    private $prop = "rand";

    public function execute()
    {
        $function = $this->prop;
        echo $function();
    }
}

$c = new C();
$c->execute();

This works, but I need to call the variable function stored in $this->prop using only one statement and avoiding the temporary variable.

I had no luck with

echo $this->prop();

because it actually calls the method prop() which does not exist and in any case it is not what I want to do.

As $this->prop is actually a string, I tried the following, but it produces a syntax error:

echo ($this->prop)();

I also tried

echo call_user_func($this->prop);

Although it does the work, it is not an option for me because it is not a variable function.

It seems like variable functions only work using local variables as function name.

Does anybody know a way to call directly a variable function using a class property as function name, avoiding the local temporary variable and the usage of call_user_func()?

Edit: I understand your perplexity, therefore I'm going to explain what's wrong with using call_user_func.

I'm just exploring the opportunities offered by variable functions, which seems to be less then those offered by variable variables.

Let's try using variable variables feature it its simplest form.

Suppose we have a function f() which returns the string "something"

function f() {
  return "something";
}

then a class property containing the string "something"

$this->prop = "something";

$something is a local variable

$something = "I am a local variable";

Then all the following statements will work:

$r = ${"something"};
$r = ${$this->prop};
$r = ${f()};

My personal conclusion: No matter how the string "something" has been obtained; just surround it with braces {} and prepend a dollar symbol $ to consider it a variable. Pretty flessibe.

Let's try the same for variable functions

Now we have a function f() which returns the string "rand"

function f() {
  return "rand";
}

then a class property containing the string "rand"

$this->prop = "rand";

Variable functions on the other hand, does not allow a string followed by parenthesis () to be considered a function call.

$r = "rand"(); // Produces a syntax error, unexpected '()' after a string
$r = $this->prop(); // Calls the 'prop()' method, which does not exist
$r = f()(); // Again a syntax error, unexpected '()' after the function f()

I have to conclude that variable functions always require a local variable to be run :(

like image 778
Demis Palma ツ Avatar asked Jan 23 '15 16:01

Demis Palma ツ


2 Answers

You need to implement a magic __call method, like this:

class C
{
    private $prop = "execute";

    public function __call($method, $args)
    {
        if($method == "prop")  // just for this prop name
        {
            if(method_exists($this, $this->prop))
                return call_user_func_array([$this, $this->prop], $args);
        }
    }

    public function execute ($s){
        echo '>>'.$s.'<<';
    }        

}

$c = new C;
$c->prop(123);
like image 162
Oleg Dubas Avatar answered Sep 18 '22 23:09

Oleg Dubas


It certainly does feel like a glaring omission in PHP's syntax. (Although taken literally I guess they are variable functions, not property functions!?) I would have perhaps expected the following "curly brace" syntax to work, but it doesn't, Parse error: syntax error, unexpected '{' in ....

echo {$this->prop}();

However, there are significant benefits to using variable function syntax over other methods. Variable functions are quicker than call_user_func() / call_user_func_array() and natively support pass-by-reference, rather than the "special-case" call-time pass-by-reference with call_user_func_array() (which is deprecated in all other cases).

An alternative to the __call magic method (above), which is going to be relatively slow, is to simply use a wrapper method, to which you pass the function/method name and use variable functions inside that wrapper method.

In its most simplest form:

function callUserFunc($callable) {
    return $callable();
}

Because of the performance benefit (over using call_user_func_array()) several frameworks implement a similar "helper" method, allowing for a variable number of arguments. This other question/answer goes into more depth and covers some performance benchmarks: Calling a function with explicit parameters vs. call_user_func_array()

like image 26
MrWhite Avatar answered Sep 17 '22 23:09

MrWhite