How can I call the ReflectionFunction function to close the closure that $ this uses?

This is easiest to explain with an example:

class Example { private $x; public $f; public function __construct() { $this->x = 10; $this->f = function() { return $this->x; }; } } $ex = new Example(); $f = new ReflectionFunction($ex->f); echo $f->invoke().PHP_EOL; 

Running this result results in an error:

PHP Fatal error: Crash error: using $ this if not in an object context

This is because I used $this in closure, so it looks more like ReflectionMethod , but ReflectionMethod doesn't seem to want to accept closure as an argument, so I'm not quite sure what I can do.

How can I call $ex->f using reflection?

0
source share
1 answer

Well, I really don't know why this behavior is happening. But there is a workaround (well, I found it after several tests).

Since PHP does not allow you to explicitly bind $ this ( it is automatically bound ), you need to use an alternative variable:

 $t = $this; $this->f = function() use ($t) { return $t->x; }; 

All code:

 class Example { private $x; public $f; public function __construct() { $this->x = 10; $t = $this; $this->f = function() use ($t) { return $t->x; }; } } $ex = new Example(); $f = new ReflectionFunction($ex->f); echo $f->invoke().PHP_EOL; 

And the result was like

 10 

Tested in PHP 5.4, 5.5, 5.6 and 7.

UPDATE

After mpen answered, I realized about its limitations and the actual use of Reflection.

When you use the ReflectionFunction function to call a function that is at least a closure, you should consider this as a closure. The ReflectionFunction function has a getClosure () method.

The class remains as mpen, and its use will be as follows:

 $ex = new Example(); $f = new ReflectionFunction($ex->f); $closure = $f->getClosure(); echo $closure().PHP_EOL; 

But it only works on PHP 7.

For PHP 5.4, 5.5 and 5.6 you will need to bind a class and scope. Strange, but this is the only way I found using Closure :: bindTo () or Closure :: bind () :

 $ex = new Example(); $f = new ReflectionFunction($ex->f); $closure = $f->getClosure(); $class = $f->getClosureThis(); $closure = $closure->bindTo($class , $class); echo $closure().PHP_EOL; 

Or simply:

 $ex = new Example(); $f = new ReflectionFunction($ex->f); $class = $f->getClosureThis(); $closure = Closure::bind($f->getClosure() , $class , $class); echo $closure().PHP_EOL; 

It is important to pass the class as a scope (second parameter), which will determine if you can access the private / protected variable or not.

The second parameter can also be a class name:

 $closure = $closure->bindTo($class , 'Example');//PHP >= 5.4 $closure = $closure->bindTo($class , get_class($class));//PHP >= 5.4 $closure = $closure->bindTo($class , Example::class);//PHP 5.5 

But I was not worried about performance, so the class went through twice, and I feel good too.

There is also a Closure :: call () method that can be used to change the scope, but also for PHP> = 7.

+1
source

Source: https://habr.com/ru/post/1262201/


All Articles