【问题标题】:How can I invoke a ReflectionFunction wrapping a closure that utilizes $this?如何调用包含使用 $this 的闭包的 ReflectionFunction?
【发布时间】:2016-11-23 19:07:42
【问题描述】:

用一个例子最容易解释:

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;

运行会导致错误:

PHP 致命错误:未捕获的错误:不在对象上下文中使用 $this

那是因为我在闭包中使用了$this,所以它真的更像ReflectionMethod,但ReflectionMethod 似乎不想将closure 作为参数,所以我不太确定我能做什么。

如何使用反射调用$ex->f

【问题讨论】:

    标签: php reflection


    【解决方案1】:

    嗯,我真的不知道为什么会发生这种行为。但是有一个解决方法(好吧,我在几次测试后找到了它)。

    由于PHP 不允许您显式绑定$this (it's bound automatically),您必须使用替代变量:

    $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.45.55.67 上测试。

    更新

    @mpen 回答后,我意识到他的限制和反射的使用。

    当您使用ReflectionFunction 调用function(至少是closure)时,您应该将其视为closureReflectionFunction 有一个名为 ReflectionFunction::getClosure() 的方法。

    类保持为 @mpen 创建的,用途如下:

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

    但仅适用于PHP 7

    对于PHP 5.45.55.6,您必须绑定类和范围。很奇怪,但这是我发现使用Closure::bindTo()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 变量。

    第二个参数也可以是类名:

    $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
    

    但我不关心性能,所以两次通过的课程对我来说很好。

    还有Closure::call()方法可以用来改变作用域,但也只能用于PHP >= 7

    【讨论】:

    • 谢谢,但这对我来说并不是真正的解决方案。我需要能够调用任意闭包。
    • @mpen 我已经更新了我测试过的所有东西的问题
    猜你喜欢
    • 2012-04-08
    • 2017-11-12
    • 2010-10-23
    • 1970-01-01
    • 2014-12-21
    • 2017-01-04
    • 1970-01-01
    • 2013-01-08
    • 2014-04-02
    相关资源
    最近更新 更多