I have been doing some tests (to replace old code) with the __invoke magic method and I'm not sure this is a bug or not:
Lets suppose we have a class:
class Calc {
function __invoke($a,$b){
return $a*$b;
}
}
The following is possible and works without any problem:
$c = new Calc;
$k = $c;
echo $k(4,5); //outputs 20
However if I want to have another class to store an instance of that object, this doesn't work:
class Test {
public $k;
function __construct() {
$c = new Calc;
$this->k = $c; //Just to show a similar situation than before
// $this-k = new Calc; produces the same error.
}
}
The error occurs when we try to call it like:
$t = new Test;
echo $t->k(4,5); //Error: Call to undefined method Test::k()
I know that a "solution" could be to have a function inside the class Test (named k) to "forward" the call using call_user_func_array but that is not elegant.
I need to keep that instance inside a common class (for design purposes) and be able to call it as function from other classes... any suggestion?
Update:
I found something interesting (at least for my purposes):
If we assign the "class variable" into a local variable it works:
$t = new Test;
$m = $t->k;
echo $m(4,5);
PHP thinks you want to call a method k on instance $t when you do:
$t->k(4, 5)
which is perfectly reasonable. You can use an intermediate variable to call the object:
$b = $t->k;
$b(4, 5);
See also bug #50029 , which describes your issue.
When you do $test->k()
, PHP thinks you are calling a method on the $test
instance. Since there is no method named k()
, PHP throws an exception. What you are trying to do is make PHP return the public property k
and invoke that, but to do so you have to assign k
to a variable first. It's a matter of dereferencing.
You could add the magic __call
method to your Test
class to check if there is a property with the called method name and invoke that instead though:
public function __call($method, $args) {
if(property_exists($this, $method)) {
$prop = $this->$method;
return $prop();
}
}
I leave adding the arguments to the invocation to you. You might also want to check if the property is_callable
.
But anyway, then you can do
$test->k();
You can not use method syntax (like $foo->bar() ) to call closures or objects with __invoke, since the engine always thinks this is a method call. You could simulate it through __call:
function __call($name, $params) {
if(is_callable($this->$name)) {
call_user_func_array($this->$name, $params);
}
}
but it would not work as-is.
If you call $test->k() PHP will search for a method called "k" on the $test instance and obviously it will throws an Exception.
To resolve this problem you can create a getter of the property "k"
class Test {
public $k;
function __construct() {
$c = new Calc;
$this->k = $c; //Just to show a similar situation than before
// $this-k = new Calc; produces the same error.
}
public function getK() {
return $this->k;
}
}
So now you can use the functor in this way:
$t = new Test();
echo $t->getK()(4,5);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.