我正在为一个类制作一个事件处理程序,但我想知道使用闭包而不是评估代码是否更好?
我使用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');
}
);
?>
它会执行时没有错误吗?我想自己测试一下,但目前我做不到。
如果使用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
好。
为什么不将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
。