钩子,即行为扩展,是一种较为抽象的概念。它可被视为应用程序执行过程中的广义上,无论是大型业务逻辑,还是小型浏览器检测、多语言检测等,均可被视为一种行为。甚至您希望为网站用户的首次访问弹出一个“Hello, World!”的提示,也可视为一种行为。行为的存在使得无需修改框架和应用,即可通过外围扩展或配置来更改或添加功能。不同的行为之间还可能具有相同的位置属性,例如,某些行为作用于应用执行前,而某些行为作用于模板输出后。我们将这些行为发生作用的位置称为标签(位)(tag)。当应用程序运行至该标签时,将被拦截并执行相关行为。为便于开发和拓展,我们在DzzOffice 2.0中增加了相应的处理逻辑。
core\class\dzz\Hook.php 提供了以下处理方法:
/**
* 动态添加行为扩展到某个标签
* @param string $tag 标签名称
* @param string $behavior 行为名称(即对于钩子处理程序)
* @param bool $first 是否放到开头执行
* @return void
*/
Hook::add($tag, $behavior, $first = false)
/**
* 批量导入
* @param array $tags 插件信息
* @param boolean $recursive 是否递归合并
*/
Hook::import(array $tags, $recursive = true)
/**
*
* @param string $tag 留空获取全部
* @return array
*/
Hook::get($tag = '')
/**
* @param string $tag 标签名称
* @param mixed $params 传入参数
* @param mixed $extra 额外参数
* @param bool $once 只获取一个有效返回值
* @return mixed
*/
Hook::listen($tag, &$params = null, $extra = null,$once = false)
在 core\class\dzz\dzz_app.php 中,有以下代码:
//初始化之前导入数据库钩子
private function _init_hook(){
$tagfile = CACHE_DIR . BS . 'tags' . EXT;
$data = array();
if (file_exists($tagfile)) {//文件存在则导入文件
$data=include $tagfile;
//if(is_array($data)) $data=array_unique($data);
}
if($data){
Hook::import($data);
}else{
foreach(DB::fetch_all("SELECT name,addons FROM %t where `status`='1' ORDER BY priority DESC",array('hooks')) as $value) {
$addons = $value['addons'];//同一个挂载点下多个钩子改为多条记录
Hook::add($value['name'],$addons);
}
//写入缓存文件
$data = Hook::get();
@file_put_contents($tagfile,"
该处理将注册系统所有的钩子,默认生成 data/cache/tags.php 缓存文件,并在判断缓存文件存在时进行注册。
对应钩子数据库表前缀_hooks表存储示例如下:
说明:上图列出了2个挂载点,分别对应部门向上同步和用户向上同步的两个挂载点。这两个挂载点下分别挂载了两个钩子程序(1. 钉钉的用户同步;2. 企业微信的用户同步)。addons为钩子程序对应类库文件路径。name即为标签位(挂载点)。
调用 Hook::listen('syntoline_user', $param); 将执行该挂载点下的两个程序,即钉钉应用的用户同步和企业微信应用的用户同步。
定义(添加)
应用的钩子程序目录位于应用下的 classes 目录(即命名空间类库目录,可使用命名空间定义),例如:
dzz/dingtalk/classes/dingtalk.php。
应用的钩子程序配置在对应应用目录下的dzz_app_应用名.xml文件中配置(如dzz/dingtalk/dzz_app_dingtalk.xml),格式如下:
xml书写规范:
1.首先添加 item 对应的 hooks 模块,如上所示,id='hooks' 的 item 即为;
2.每条钩子对应一个 item,id 对应标签位(挂载点);
3.priority 对应优先级(可忽略),若有与系统默认钩子同名或同一钩子需执行多个处理程序,可定义优先级以保证执行顺序,优先级以数值大小来区分,数值越大优先级越高;
4.description 对应描述(可忽略);
5.cdata 对应钩子程序路径,如需定义钩子执行作用域,可在路径添加 “|作用域(命名空间方式指定,中间分割符用/)”,如上定义标签位 test,在 test 应用下运行时(即用 Hook::listen('test') 调用),将先执行 dzz\test\classes\test 对应处理程序,后执行 dzz\test\classes\testa 对应处理程序。若非 test 应用下,将只执行 dzz\test\classes\test。
钩子类文件:
钩子类文件以“.php”为后缀,要求类名和文件名一致,类名称首字母大写。钩子方法定义时,建议与标签位名称一致。系统执行时,将自动在对应类文件中寻找该标签位名称方法。如果没有,则默认执行 run 方法(即钩子方法需与标签位名称一致或用 run 来命名)。
系统默认钩子,均使用命名空间定义,根据需要可不用亦可。
在钩子方法中,一旦执行 return false;,则该标签位(挂载点)下无论是否还有其它钩子程序,都将不再执行。
应用导入或安装时将根据应用文件 xml 中的 item 里的 hooks 模块配置。安装后更新系统缓存以验证钩子程序是否正确调用。