Call closure assigned to an object directly

I would like to be able to call the closure that I assign to the direct object without reassigning the closure to the variable, and then call it. Is it possible?

The code below does not work and causes Fatal error: Call to undefined method stdClass::callback() .

 $obj = new stdClass(); $obj->callback = function() { print "HelloWorld!"; }; $obj->callback(); 
+99
closures object properties php
Dec 26 '10 at 20:40
source share
11 answers

As with PHP7, you can do

 $obj = new StdClass; $obj->fn = function($arg) { return "Hello $arg"; }; echo ($obj->fn)('World'); 

or use Closure :: call () , although this does not work on StdClass .




Before PHP7, you need to implement the magic __call method to intercept the call and call the callback (which is not possible for StdClass , of course, because you cannot add the __call method)

 class Foo { public function __call($method, $args) { if(is_callable(array($this, $method))) { return call_user_func_array($this->$method, $args); } // else throw exception } } $foo = new Foo; $foo->cb = function($who) { return "Hello $who"; }; echo $foo->cb('World'); 

Note that you cannot do

 return call_user_func_array(array($this, $method), $args); 

in the body of __call , because it will call __call in an infinite loop.

+94
Dec 26 '10 at 20:55
source share

You can do this by calling __invoke when closing, since that is the magic method that objects use to behave like functions:

 $obj = new stdClass(); $obj->callback = function() { print "HelloWorld!"; }; $obj->callback->__invoke(); 

Of course, this will not work if the callback is an array or string (which can also be valid callbacks in PHP) - only for closures and other objects with __invoke behavior.

+100
Dec 28 2018-12-12T00:
source share

In PHP 7, you can do the following:

 ($obj->callback)(); 
+21
Jul 29 '15 at 19:34
source share

It seems possible to use call_user_func() .

 call_user_func($obj->callback); 

not elegant though .... What @Gordon says is perhaps the only way to go.

+7
Dec 26 '10 at 20:57
source share

Well, if you really insist. Another workaround would be the following:

 $obj = new ArrayObject(array(),2); $obj->callback = function() { print "HelloWorld!"; }; $obj['callback'](); 

But this is not the most beautiful syntax.

However, the PHP parser always considers T_OBJECT_OPERATOR , IDENTIFIER , ( as a method call. There seems to be no workaround for creating -> traversing the method table and accessing attributes.

+7
Dec 26 '10 at 22:21
source share

I know this is old, but I think the traits do a great job of this problem if you are using PHP 5.4 +

First create a property that makes the properties callable:

 trait CallableProperty { public function __call($method, $args) { if (property_exists($this, $method) && is_callable($this->$method)) { return call_user_func_array($this->$method, $args); } } } 

Then you can use this trait in your classes:

 class CallableStdClass extends stdClass { use CallableProperty; } 

Now you can define properties through anonymous functions and call them directly:

 $foo = new CallableStdClass(); $foo->add = function ($a, $b) { return $a + $b; }; $foo->add(2, 2); // 4 
+7
Aug 28 '14 at 20:22
source share

Since PHP 7 closure can be called using call() :

 $obj->callback->call($obj); 

Since PHP 7 you can perform operations with arbitrary expressions (...) (as explained by Korikulum ):

 ($obj->callback)(); 

Other common PHP 5 approaches are:

  • using the __invoke() magic method (as explained by Brilliand )

     $obj->callback->__invoke(); 
  • using the call_user_func() function

     call_user_func($obj->callback); 
  • using an intermediate variable in an expression

     ($_ = $obj->callback) && $_(); 

Each method has its pros and cons, but the most radical and final solution is still the one presented by Gordon .

 class stdKlass { public function __call($method, $arguments) { // is_callable([$this, $method]) // returns always true when __call() is defined. // is_callable($this->$method) // triggers a "PHP Notice: Undefined property" in case of missing property. if (isset($this->$method) && is_callable($this->$method)) { return call_user_func($this->$method, ...$arguments); } // throw exception } } $obj = new stdKlass(); $obj->callback = function() { print "HelloWorld!"; }; $obj->callback(); 
+7
Dec 09 '15 at 1:21
source share

It should be emphasized that storing the closure in a variable and calling varible actually (faster) faster, depending on the sum of the call, becomes quite a lot, with xdebug (so a very accurate measurement), we talk about 1.5 (coefficient using varible, instead of calling __invoke directly, so instead just save the closure to varible and name it.

+2
Apr 21 '13 at 15:28
source share

Here is another way to successfully call object properties as closures.
If you do not want to change the main object, use this:

 $obj = new AnyObject(); // with or without __invoke() method $obj->callback = function() { return function () { print "HelloWorld!"; }; }; $obj->callback(); 

UPDATE:

 $obj = new AnyObject(); // with or without __invoke() method $obj->callback = function() { print "HelloWorld!"; }; $callback = $obj->callback; $callback(); 
+2
Aug 24 '14 at 2:59
source share

Here's another alternative based on the accepted answer, but directly spreading stdClass:

 class stdClassExt extends stdClass { public function __call($method, $args) { if (isset($this->$method)) { $func = $this->$method; return call_user_func_array($func, $args); } } } 

Usage example:

 $foo = new stdClassExt; $foo->blub = 42; $foo->whooho = function () { return 1; }; echo $foo->whooho(); 

You should call_user_func use call_user_func or __invoke .

+1
Jul 08 '14 at 15:18
source share

If you are using PHP 5.4 or higher, you can bind a call to the called user behavior that is called to the scope of your object. So, for example, if you had the following setting ..

 function run_method($object, Closure $method) { $prop = uniqid(); $object->$prop = \Closure::bind($method, $object, $object); $object->$prop->__invoke(); unset($object->$prop); } 

And you worked in that class.

 class Foo { private $value; public function getValue() { return $this->value; } } 

You can run your own logic as if you were working from an area of ​​your object

 $foo = new Foo(); run_method($foo, function(){ $this->value = 'something else'; }); echo $foo->getValue(); // prints "something else" 
0
Aug 05 '13 at 15:53
source share



All Articles