如何调用包装使用$ this的闭包的ReflectionFunction? [英] How can I invoke a ReflectionFunction wrapping a closure that utilizes $this?
问题描述
这是一个最容易解释的例子:
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 results in an error:
PHP致命错误:未捕获错误:不在对象上下文中时使用$ this
PHP Fatal error: Uncaught Error: Using $this when not in object context
那是因为我在闭包中使用了$this
,所以它实际上更像是ReflectionMethod
,但是ReflectionMethod
似乎不想接受closure
作为参数,所以我不确定我能做什么.
That's because I've used $this
in the closure, so it's really more like a ReflectionMethod
, but ReflectionMethod
doesn't seem to want to take a closure
as an argument, so I'm not really sure what I can do.
如何使用反射调用$ex->f
?
推荐答案
好吧,我真的不知道为什么会发生这种情况.但是有一种解决方法(嗯,我经过几次测试就发现了它.)
Well, I really don't know why that behavior is happening. But there's a workaround (well, I found it after a few tests).
As PHP
doesn't let you explicit the bind of $this
(it's bound automatically), you have to use an alternative variable:
$t = $this;
$this->f = function() use ($t) {
return $t->x;
};
整个代码:
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;
结果想要
10
在PHP 5.4
,5.5
,5.6
和7
上进行了测试.
Tested on PHP 5.4
, 5.5
, 5.6
and 7
.
更新
在@mpen回答之后,我已经意识到他的限制以及对Reflection的使用.
After @mpen answer, I've realized about his restrictions and the use of Reflection.
当您使用 ReflectionFunction
调用function
,至少是closure
,您应该将其视为closure
. ReflectionFunction
具有一种称为
When you use a ReflectionFunction
to invoke a function
, which is a closure
at least, you should treat that as a closure
. ReflectionFunction
has a method called ReflectionFunction::getClosure()
.
该类将保持为@mpen创建,其用法将为:
The Class remains as @mpen created and the use will be as:
$ex = new Example();
$f = new ReflectionFunction($ex->f);
$closure = $f->getClosure();
echo $closure().PHP_EOL;
但仅适用于PHP 7
.
对于PHP 5.4
,5.5
和5.6
,您必须绑定类和范围.很奇怪,但这是我使用 Closure :: bindTo()的唯一方法>或 Closure :: bind():
For PHP 5.4
, 5.5
and 5.6
you'll have to bind the class and scope. Weird, but it's the only way that 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;
或者只是:
$ex = new Example();
$f = new ReflectionFunction($ex->f);
$class = $f->getClosureThis();
$closure = Closure::bind($f->getClosure() , $class , $class);
echo $closure().PHP_EOL;
将类作为范围(第二个参数)传递非常重要,这将确定您是否可以访问private
/protected
变量.
It's very important to pass the class as scope (second parameter) which will determine whether you can access private
/protected
variable or not.
第二个参数也可以是类名:
The second paramater also could be the class name as:
$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 didn't concerned about performance, so the class passed twice is just fine to me.
还有方法 Closure :: call()可以是用于更改范围,但也仅用于PHP >= 7
.
There's also the method Closure::call() which can be used to change the scope, but also just for PHP >= 7
.
这篇关于如何调用包装使用$ this的闭包的ReflectionFunction?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!