如何调用包装使用$ this的闭包的ReflectionFunction? [英] How can I invoke a ReflectionFunction wrapping a closure that utilizes $this?

查看:163
本文介绍了如何调用包装使用$ this的闭包的ReflectionFunction?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个最容易解释的例子:

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).

由于PHP不允许您明确显示$this的绑定(

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.45.55.67上进行了测试.

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.45.55.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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆