将闭包绑定到类作为新方法

时间:2015-07-27 09:28:25

标签: php closures

我正在构建一个扩展供应商类功能的API类。期望扩展供应商类,并检查是否存在这样的方法:

if (method_exists($this, 'block'.$CurrentBlock['type']))
{
    $CurrentBlock = $this->{'block'.$CurrentBlock['type']}($CurrentBlock);
}

因为我的API也是供应商文件,所以我认为我会做一些聪明的事情,并试图让人们将闭包传递到我的API中,然后扩展这个类。

public function extendBlock($blockName, Closure $closure)
{
    $methodName = camel_case("block_{$blockName}");
    $this->{$methodName} = $closure;

    return method_exists($this, $methodName);
}

这理论上会绑定闭包,这样我的第一个代码块中的调用就会成功......但这并不会发生。它不被视为一种方法,而是一种包含闭包的属性。 method_exist不仅失败,而且尝试调用该方法失败。

这是一个修改过的版本,我试图弄清楚出了什么问题。

public function extendBlock($blockName, Closure $closure)
{
    $methodName = camel_case("block_{$blockName}");
    $newClosure = clone $closure;
    $newClosure = $newClosure->bindTo($this);

    $this->{$methodName} = $newClosure;
    $this->{$methodName}();

    return method_exists($this, $methodName);
}

这些都不起作用。该属性已明确设置,$this$closure的范围当前指向该方法的$this

如果我改为运行它,闭包执行正确。

    $this->{$methodName} = $newClosure;
    //$this->{$methodName}();

    $foobar = $this->{$methodName};
    $foobar();

所以是的。我真的希望有一种漂亮,整洁的方式来满足我的第一个代码块中的检查而不需要用户继承我的课程并直接写它们,但我认为这不可能。

修改:这与Storing a Closure Function in a Class Property in PHP略有不同 - 虽然提供的__call解决方案是优秀的,值得研究一下你是不是关于将闭包绑定到类的好奇心,这个方法不会欺骗method_exists检查。

2 个答案:

答案 0 :(得分:1)

它不适用于method_exists(),因为该函数根据在类范围中显式声明的方法提供信息。但是,仍然有魔术方法的解决方法。确切地说__call()

class Caller
{
    public function bind($method, Closure $call)
    {
        $this->$method = $call;
    }

    public function __call($method, $args)
    {
        if (isset($this->$method) && $this->$method instanceof Closure) {
            return call_user_func_array($this->$method, $args);
        }
    }
}

允许您强制调用您的"属性可调用"。例如,

$c = function($x) {
    return $x*$x;
};

$obj = new Caller();
$obj->bind('foo', $c);
var_dump($obj->foo(4)); //16

请参阅示例 here

可能有一些方法可以动态地更改类本身(runkit和公司),但我强烈建议尽可能远离它。

答案 1 :(得分:0)

使用http://github.com/zenovich/runkit中的最新版Runkit,您只需编写runkit_method_add(get_class($this), $methodName, $newClosure);即可 这样做。