我正在为一个类创建一个事件处理程序,但我想知道使用闭包而不是代码评估会更好吗?
使用eval()的唯一原因仅仅是因为它能够访问类中的所有内容(而且它确实非常不安全:D),但我不知道闭包是否可以。
如果我做了这样的事情:
<?php
class SomethingCool {
protected $handlers;
public function addHandler($cmd, closure $func) {
$this->handlers[$cmd][] = $func;
}
public function handle($cmd) {
if(!isset($this->handlers[$cmd]))
return false;
foreach($this->handlers[$cmd] as $func)
$func();
}
}
?>
<?php
$wut = new SomethingCool();
$wut->addHandler('lol', function() use($wut) {
$wut->handle('lol');
}
);
?>
它会在没有错误的情况下执行吗? 我会亲自测试一下,但我现在还不能。
答案 0 :(得分:2)
如果您使用eval
编写处理程序,您最终将编写如下代码:
$wut->addHandler('lol', '$this->handle(\'lol\');');
除了在编辑器中转义引号和破坏语法突出显然存在明显可怕的问题之外,这还引入了模糊依赖关系的问题。您的代码中$this
引用了什么?它在字面上不像代码那样工作,它取决于在特定上下文中进行评估。这使代码变得一团糟。
另一种选择是依赖注入:
$wut->addHandler('lol', function (SomethingCool $sc) {
$sc->handle('lol');
});
调用此处理程序时,SomethingCool
会将自身注入函数参数。这更加强大。这意味着您可以将此回调传递给其他上下文并在幕后执行任何操作,回调不再依赖于在特定上下文中进行评估。
或者,使用闭包:
$wut->addHandler('lol', function () use ($wut) {
$wut->handle('lol');
});
这有一个好处,你可以确定你的依赖来自哪里,并且知道你可以依赖它。
是的,任何都优于eval
。
答案 1 :(得分:0)
为什么不将SomethingCool
的实例传递给每个处理程序?
public function handle($cmd)
{
if (!isset($this->handlers[$cmd])) {
return;
}
foreach ($this->handlers[$cmd] as $func)
$func($this); // pass ourself to each handler
}
}
$wut->addHandler('lol', function(SomethingCool $obj) {
// $obj refers to the SomethingCool instance
});
$wut->handle('lol');
顺便说一句,如果您还希望能够删除处理程序,也可以对每个命令类别使用SplObjectStorage
。