我想到了观察者模式。插件会自行注册并在调用钩子时收到通知。
想到的另一件事是 PHP 中的回调。我想到了similar question already 和an answer。它显示基于回调的钩子。
观察者模式 运行起来有点短,因为使用钩子时,您经常希望提供参数和返回值之类的东西。使用回调的链接答案也没有这个,所以我写了一个小Hooks 示例类,为注册的回调提供命名的钩子/事件,以及注册你自己的类的方法,例如一个插件。
这个想法很基本:
- 挂钩有一个名称和零个或多个附加回调。
- 所有挂钩都在
Hooks 类中进行管理。
- 主代码通过调用
Hooks 对象上的函数来调用挂钩。
- 插件(和其他类)可以注册自己的回调,这是在
Registerable 接口的帮助下完成的。
一个插件和两个钩子的一些示例代码:
<?php
Namespace Addon;
class Hooks
{
private $hooks = array();
private $arguments;
private $name;
private $return;
public function __call($name, array $arguments)
{
$name = (string) $name;
$this->name = $name;
$this->arguments = $arguments;
$this->return = NULL;
foreach($this->getHooks($name) as $hook)
{
$this->return = call_user_func($hook, $this);
}
return $this->return;
}
public function getHooks($name)
{
return isset($this->hooks[$name]) ? $this->hooks[$name] : array();
}
public function getArguments()
{
return $this->arguments;
}
public function getName()
{
return $this->name;
}
public function getReturn()
{
return $this->return;
}
public function setReturn($return)
{
$this->return = $return;
}
public function attach($name, $callback)
{
$this->hooks[(string) $name][] = $callback;
}
public function register(Registerable $plugin)
{
$plugin->register($this);
}
}
interface Registerable
{
public function register(Hooks $hooks);
}
class MyPlugin implements Registerable
{
public function register(Hooks $hooks)
{
$hooks->attach('postPublished', array($this, 'postPublished'));
$hooks->attach('postDisplayFilter', array($this, 'filterToUpper'));
}
public function postPublished()
{
echo "MyPlugin: postPublished.\n";
}
public function filterToUpper(Hooks $context)
{
list($post) = $context->getArguments();
return strtoupper($post);
}
}
$hooks = new Hooks();
$plugin = new MyPlugin();
$hooks->register($plugin);
$hooks->postPublished();
echo $hooks->postDisplayFilter("Some post text\n");
我这样做是为了防止每个插件必须只有一个具体的基类,因为它想使用钩子。此外,一切都可以注册钩子,唯一需要的是回调。例如一个匿名函数:
$hooks->attach('hookName', function() {echo "Hook was called\n";});
您可以自己创建一个插件基类,例如实现register 函数并自动注册具有特定文档块标签或函数名称的函数
class MyNewPlugin extends PluginSuper
{
/**
* @hook postPublished
*/
public function justAnotherFunction() {}
public hookPostPublished() {}
}
超类可以利用Reflection 在运行时添加挂钩。然而,反射会减慢速度,并可能使调试变得更加困难。