老生常谈ThinkPHP中的行为扩展和插件_php实例_脚本之家

原理分析

之前写到TP3.1的行为扩展是tag();在TP3.2中引入了另一种说法—:钩子。

将标签与类之间的对应关系(如'app_init'=>array('Common\Behavior\InitHook')),通过Hook类中import或add方法,加载到Hook类中静态变量$tags中。当执行Hook中静态方法listen或者exec方法的时候,实例化标签对应的类,调用相应的方法(如果是插件,调用传递的方法,如果是行为,调用run方法)。

我们来看一下TP3.2中的钩子这个东西:

Hook中exec方法定义如下:

一:文件流程:

static public function exec($name, $tag,&$params=NULL) { if(false === strpos { // 插件 $class = "Addons\\{$name}\\{$name}Addon"; }else{ // 行为扩展 $class = $name.'Behavior'; $tag = 'run'; } $addon = new $class(); return $addon->$tag; }

1:/index.php ->require './ThinkPHP/ThinkPHP.php';

ThinkPHP中的行为

2:/ThinkPHP/ThinkPHP.php—->require CORE_PATH.'Think'.EXT;
Think\Think::start();

加载标签与类之间的对应关系

3:/ThinkPHP/Library/Think/Think.class.php—–>App::run();

// 加载模式行为定义if { Hook::import(is_array?$mode['tags']:include $mode['tags']);}// 加载应用行为定义if(is_file) // 允许应用增加开发模式配置定义 Hook::import(include CONF_PATH.'tags.php'); 

4:/ThinkPHP/Library/Think/App.class.php
。到这里基本流程就走完了,(这里不说细节);

模式行为对应关系

首先,我们要明确一些说法。在TP中,设置陷阱的过程称为##绑定事件##,而某个事件触发的功能函数称为##行为##。

在ThinkPHP/Mode/common.php中tags标签中定义

钩子应该具有的基本方法应该有:

'tags' => array( 'app_begin' => array( 'Behavior\ReadHtmlCache', // 读取静态缓存 ), 'app_end' => array( 'Behavior\ShowPageTrace', // 页面Trace显示 ), 'view_parse' => array( 'Behavior\ParseTemplate', // 模板解析 支持PHP、内置模板引擎和第三方模板引擎 ), 'template_filter'=> array( 'Behavior\ContentReplace', // 模板输出替换 ), 'view_filter' => array( 'Behavior\WriteHtmlCache', // 写入静态缓存 ), )

设置钩子(导入钩子)
触发事件
执行行为
首先我们看看TP是怎么写的,源代码位于ThinkPHP/Library/Think/Hook.class.php,Hook类中全是静态方法,其中有唯一静态属性$tags,他是一个数组,键为绑定的事件,值为绑定的行为。

在Application/Common/Conf/tags.php中定义

其中有两个方法可以用于绑定,前者是单个,后者是是批量。

调用相应的行为

    static public function add($tag,$name) {
        echo $tag;
        echo "\n";
        if(!isset(self::$tags[$tag])){
            self::$tags[$tag]   =   array();
        }
        if(is_array($name)){
            self::$tags[$tag]   =  
array_merge(self::$tags[$tag],$name);
        }else{
            self::$tags[$tag][] =   $name;
        }
    }

如Hook::listen相当于调用Behavior\ReadHtmlCache中的run方法。找到该类,可以查看其对应的run方法如下

    static public function import($data,$recursive=true) {
        if(!$recursive){ // 覆盖导入
            self::$tags   =   array_merge(self::$tags,$data);
        }else{ // 合并导入
            foreach ($data as
$tag=>$val){
                if(!isset(self::$tags[$tag]))
                    self::$tags[$tag]   =   array();           
                if(!empty($val['_overlay'])){
                    // 可以针对某个标签指定覆盖模式
                    unset($val['_overlay']);
                    self::$tags[$tag]   =   $val;
                }else{
                    // 合并模式
                    self::$tags[$tag]   =  
array_merge(self::$tags[$tag],$val);
                }
            }           
        }
    }
当系统触发了某个事件,比如app_start事件,TP会找到Hook::listen方法,该方法会查找$tags中有没有绑定app_start事件的方法,然后用foreach遍历$tags属性,并执行Hook:exec方法。

自定义行为

    static public function listen($tag, &$params=NULL) {
        if(isset(self::$tags[$tag])) {
            if(APP_DEBUG) {
                G($tag.'Start');
                trace('[ '.$tag.' ] --START--','','INFO');
            }
            foreach (self::$tags[$tag] as $name) {
                APP_DEBUG && G($name.'_start');
                $result =   self::exec($name, $tag,$params);
                if(APP_DEBUG){
                    G($name.'_end');
                    trace('Run '.$name.' [
RunTime:'.G($name.'_start',$name.'_end',6).'s ]','','INFO');
                }
                if(false === $result) {
                    // 如果返回false 则中断插件执行
                    return ;
                }
            }
            if(APP_DEBUG) { // 记录行为的执行日志
                trace('[ '.$tag.' ] --END-- [
RunTime:'.G($tag.'Start',$tag.'End',6).'s ]','','INFO');
            }
        }
        return;
    }
Hook:exec方法会检查行为名称,如果包含Behavior关键字,那么入口方法必须为run方法,而执行run方法的参数在调用Hook::listen时指定。但如果不用##Behavior##关键字做配置,即将系统默认的ReadHtmlCacheBehavior改为ReadHtml,系统会报错吗?答案是会的!

1.在Application/Common/Conf/tags.php中添加对应关系

如果去掉Behavior,系统就会找该类中绑定事件名称的方法,即app_begin。这样的好处是,不会强制使用run方法,一个行为可以复用了。

'dqs_behavior'=>array('Common\Behavior\Dqs')

    static public function exec($name, $tag,&$params=NULL) {
        if('Behavior' ==
substr($name,-8) ){
            // 行为扩展必须用run入口方法
            $tag    =   'run';
        }
        $addon   = new $name();
        return $addon->$tag($params);
    }

将对应关系添加到tags.php,程序会自动将其加载到Hook的$tags变量中,当然也可通过使用Hook中的add方法手动加载。

补充:

相关文章

Comment ()
评论是一种美德,说点什么吧,否则我会恨你的。。。