PHP钩子机制原理及详解-php教程

资源魔 37 0

甚么是钩子?

各人想必听过插件,wordpress插件特地多,这个就是用钩子机制完成的。

今世码正在运转的进程中,咱们事后正在运转的几个非凡点里执行一些非凡办法:例如正在运转办法(例如Blog::add的add办法)以前记载输出参数、运转办法之跋文录解决后果,这个运转办法以前、运转办法之后就是简略的钩子(挂载点),咱们正在这个钩子上搁置钩子函数(记载输出参数、记载解决后果),执行一些以及顺序运转没有相干的义务。

<?php
class Blog extends Controller{
    
    public function add(){
        
        //some code
        $res = $data;
        
        return $res;
    }
}
$obj = new Blog();
Log::write($_REQUEST);
$res =  $obj->add();
Log::write(json_encode($res));

假如正在运转办法以前搁置的是一个OnBeforeRunActionCallback()的办法,这个办法可能最开端的时分是空的,但咱们当前就能够没有去修正原有代码,间接正在OnBeforeRunActionCallback()外面加代码逻辑就能够了,例如记载日记、参数过滤等等。

<?php
class Blog extends Controller{
    
    public function add(){
        
        //some code
        $res = $data;
        
        return $res;
    }
}
$obj = new Blog();
OnBeforeRunActionCallback($_REQUEST);
$obj->add();
OnAfterRunActionCallback($res);
function OnBeforeRunActionCallback($param){
    Log::write($param);
        FilterParams($param);
}
function OnAfterRunActionCallback($res){
    Log::write(json_encode($res));
}

正在名目代码中,你以为要扩大(临时没有扩大)之处搁置一个钩子函数,等需求扩大的时分,把需求完成的类以及函数挂载到这个钩子上,就能够完成扩大了。

原理

实际的钩子普通设计为一个类Hook,该类提供注册插件到钩子(add_hook)、触发钩子办法(trigger_hook)。注册插件的时分将插件所要运转的可执行办法存储到钩子对应的数组外面。

$_listeners = array(
    'OnBeforeRunAction' => array(
        'callback1',
        'callback2',
        'callback3',
    ),
);
//提前注册插件到钩子
add_hook('OnBeforeRunAction', 'callback4');
//特定中央执行钩子
trigger_hook('OnBeforeRunAction');

当触发钩子的时分,将遍历OnBeforeRunAction里注册的回调办法,执行对应的回调办法,完成静态扩大性能。注册的钩子办法普通是匿名函数:

function trigger_hook($hook, $data=''){
    //查看要完成的钩子,能否正在监听数组之中
    if (isset($this->_listeners[$hook]) && is_array($this->_listeners[$hook]) && count($this->_listeners[$hook]) > 0)
    {
        // 轮回挪用开端
        foreach ($this->_listeners[$hook] as $listener)
        {
            if(is_callable()){
                call_user_func($listener, $data);
            }elseif(is_array($listener)){
                // 掏出插件工具的援用以及办法
                $class =& $listener[0];
                $method = $listener[1];
                if(method_exists($class,$method))
                {
                    // 静态挪用插件的办法
                    $class->$method($data);
                }
            }
        }
    }
}

若何完成?

简略的

一、插件类Hook:提供注册插件以及执行插件的办法,实际是往一个数组里存挂载点对应的可执行办法。

二、正在某个设置装备摆设文件或许函数里对立注册插件。

class Hook
{
    //action hooks array  
    private static $actions = array();
    /**
     * ads a function to an action hook
     * @param $hook
     * @param $function
     */
    public static function add_action($hook,$function)
    {   
        $hook=mb_strtolower($hook,CHARSET);
        // create an array of function handlers if it doesn't already exist
        if(!self::exists_action($hook))
        {
            self::$actions[$hook] = array();
        }
        // append the current function to the list of function handlers
        if (is_callable($function))
        {
            self::$actions[$hook][] = $function;
            return TRUE;
        }
        return FALSE ;
    }
    /**
     * executes the functions for the given hook
     * @param string $hook
     * @param array $params
     * @return boolean true if a hook was setted
     */
    public static function do_action($hook,$params=NULL)
    {
        $hook=mb_strtolower($hook,CHARSET);
        if(isset(self::$actions[$hook]))
        {
            // call each function handler associated with this hook
            foreach(self::$actions[$hook] as $function)
            {
                if (is_array($params))
                {
                    call_user_func_array($function,$params);
                }
                else
                {
                    call_user_func($function);
                }
                //cant return anything since we are in a loop! dude!
            }
            return TRUE;
        }
        return FALSE;
    }
    /**
     * gets the functions for the given hook
     * @param string $hook
     * @return mixed
     */
    public static function get_action($hook)
    {
        $hook=mb_strtolower($hook,CHARSET);
        return (isset(self::$actions[$hook]))? self::$actions[$hook]:FALSE;
    }
    /**
     * check exists the functions for the given hook
     * @param string $hook
     * @return boolean
     */
    public static function exists_action($hook)
    {
        $hook=mb_strtolower($hook,CHARSET);
        return (isset(self::$actions[$hook]))? TRUE:FALSE;
    }
}
 
    /**
     * Hooks Shortcuts not in class
     */
    function add_action($hook,$function)
    {
        return Hook::add_action($hook,$function);
    }
 
    function do_action($hook)
    {
        return Hook::do_action($hook);
    }

用法举例:

//增加钩子
Hook::add_action('unique_name_hook','some_class::hook_test');
//或应用快捷函数增加钩子:
add_action('unique_name_hook','other_class::hello');
add_action('unique_name_hook','some_public_function');
//执行钩子
do_action('unique_name_hook');//也能够应用 Hook::do_action();

带装置/卸载的

钩子类初始化的时分去注册曾经开启的插件(如数据库记载);全局合适的时分设置挂载点;运转到合适的时分触发挂载点注册的事情。

一、插件类Hook:提供注册插件以及执行插件的办法,实际是往一个数组里存挂载点对应的可执行办法。

二、插件类添加一个初始化的办法,去数据查找曾经装置的插件,运转插件必需执行的注册办法(reg),注册插件办法注册钩子到挂载点。

三、固定把插件放正在某个目次,并安照肯定标准写设置装备摆设文件。后盾有插件列表页面,遍历指定目次下的插件。当装置的时分,将插件信息记载到数据库,卸载的时分删除了数据库记载信息。

<?php
/**
 * @file plugin.php
 * @brief 插件外围类
 * @note 察看者模式,注册事情,触发事情
 */
class plugin extends IInterceptorBase
{
    //默许开启的插件列表
    private static $defaultList = array("_verification","_goodsCategoryWidget","_authorization","_userInfo","_initData");
    //曾经注册监听
    private static $_listen = array();
    //加载插件
    public static function init()
    {
        $pluginDB    = new IModel('plugin');
        $pluginList  = $pluginDB->query("is_open = 1","class_name","sort asc");
        //加载默许插件
        foreach(self::$defaultList as $val)
        {
            $pluginList[]= array('class_name' => $val);
        }
        foreach($pluginList as $key => $val)
        {
            $className = $val['class_name'];
            $classFile = self::path().$className."/".$className.".php";
            if(is_file($classFile))
            {
                include_once($classFile);
                $pluginObj = new $className();
                $pluginObj->reg();
            }
        }
    }
    /**
     * @brief 注册事情
     * @param string $event 事情
     * @param object ro function $classObj 类实例 或许 匿名函数
     * @param string $method 办法名字
     */
    public static function reg($event,$classObj,$method = "")
    {
        if(!isset(self::$_listen[$event]))
        {
            self::$_listen[$event] = array();
        }
        self::$_listen[$event][] = array($classObj,$method);
    }
    /**
     * @brief 显示已注册事情
     * @param string $event 事情称号
     * @return array
     */
    public static function get($event = '')
    {
        if($event)
        {
            if( isset(self::$_listen[$event]) )
            {
                return self::$_listen[$event];
            }
            return null;
        }
        return self::$_listen;
    }
    /**
     * @brief 触发事情
     * @param string $event 事情
     * @param mixed  $data  数据
     * @notice 能够挪用匿名函数以及办法
     */
    public static function trigger($event,$data = null)
    {
        $result = array();
        if(isset(self::$_listen[$event]))
        {
            foreach(self::$_listen[$event] as $key => $val)
            {
                list($pluginObj,$pluginMethod) = $val;
                $result[$key] = is_callable($pluginObj) ? call_user_func($pluginObj,$data):call_user_func(array($pluginObj,$pluginMethod),$data);
            }
        }
        return isset($result[1]) ? $result : current($result);
    }
    /**
     * @brief 插件物理门路
     * @return string 门路字符串
     */
    public static function path()
    {
        return IWeb::$app->getBasePath()."plugins/";
    }
    /**
     * @brief 插件WEB门路
     * @return string 门路字符串
     */
    public static function webPath()
    {
        return IUrl::creatUrl('')."plugins/";
    }
    /**
     * @brief 猎取全副插件
     * @param string $name 插件名字,假如为空则猎取全副插件信息
     * @return array 插件信息 array(
        "name"        => 插件名字,
        "description" => 插件形容,
        "explain"     => 应用阐明,
        "class_name"  => 插件ID,
        "is_open"     => 能否开启,
        "is_install"  => 能否装置,
        "config_name" => 默许插件参数构造,
        "config_param"=> 曾经保留的插件参数,
        "sort"        => 排序,
     )
     */
    public static function getItems($name = '')
    {
        $result = array();
        $dirRes = opendir(self::path());
        //遍历目次读取设置装备摆设文件
        $pluginDB = new IModel('plugin');
        while($dir = readdir($dirRes))
        {
            if($dir[0] == "." || $dir[0] == "_")
            {
                continue;
            }
            if($name && $result)
            {
                break;
            }
            if($name && $dir != $name)
            {
                continue;
            }
            $pluginIndex = self::path().$dir."/".$dir.".php";
            if(is_file($pluginIndex))
            {
                include_once($pluginIndex);
                if(get_parent_class($dir) == "pluginBase")
                {
                    $class_name   = $dir;
                    $pluginRow    = $pluginDB->getObj('class_name = "'.$class_name.'"');
                    $is_open      = $pluginRow ? $pluginRow['is_open'] : 0;
                    $is_install   = $pluginRow ? 1                     : 0;
                    $sort         = $pluginRow ? $pluginRow['sort']    : 99;
                    $config_param = array();
                    if($pluginRow && $pluginRow['config_param'])
                    {
                        $config_param = JSON::decode($pluginRow['config_param']);
                    }
                    $result[$dir] = array(
                        "name"        => $class_name::name(),
                        "description" => $class_name::description(),
                        "explain"     => $class_name::explain(),
                        "class_name"  => $class_name,
                        "is_open"     => $is_open,
                        "is_install"  => $is_install,
                        "config_name" => $class_name::configName(),
                        "config_param"=> $config_param,
                        "sort"        => $sort,
                    );
                }
            }
        }
        if(!$name)
        {
            return $result;
        }
        return isset($result[$name]) ? $result[$name] : array();
    }
    /**
     * @brief 零碎内置的一切事情触发
     */
    public static function onCreateApp(){plugin::init();plugin::trigger("onCreateApp");}
    public static function onFinishApp(){plugin::trigger("onFinishApp");}
    public static function onBeforeCreateController($ctrlId){plugin::trigger("onBeforeCreateController",$ctrlId);plugin::trigger("onBeforeCreateController@".$ctrlId);}
    public static function onCreateController($ctrlObj){plugin::trigger("onCreateController");plugin::trigger("onCreateController@".$ctrlObj->getId());}
    public static function onFinishController($ctrlObj){plugin::trigger("onFinishController");plugin::trigger("onFinishController@".$ctrlObj->getId());}
    public static function onBeforeCreateAction($ctrlObj,$actionId){plugin::trigger("onBeforeCreateAction",$actionId);plugin::trigger("onBeforeCreateAction@".$ctrlObj->getId());plugin::trigger("onBeforeCreateAction@".$ctrlObj->getId()."@".$actionId);}
    public static function onCreateAction($ctrlObj,$actionObj){plugin::trigger("onCreateAction");plugin::trigger("onCreateAction@".$ctrlObj->getId());plugin::trigger("onCreateAction@".$ctrlObj->getId()."@".$actionObj->getId());}
    public static function onFinishAction($ctrlObj,$actionObj){plugin::trigger("onFinishAction");plugin::trigger("onFinishAction@".$ctrlObj->getId());plugin::trigger("onFinishAction@".$ctrlObj->getId()."@".$actionObj->getId());}
    public static function onCreateView($ctrlObj,$actionObj){plugin::trigger("onCreateView");plugin::trigger("onCreateView@".$ctrlObj->getId());plugin::trigger("onCreateView@".$ctrlObj->getId()."@".$actionObj->getId());}
    public static function onFinishView($ctrlObj,$actionObj){plugin::trigger("onFinishView");plugin::trigger("onFinishView@".$ctrlObj->getId());plugin::trigger("onFinishView@".$ctrlObj->getId()."@".$actionObj->getId());}
    public static function onPhpShutDown(){plugin::trigger("onPhpShutDown");}
}
/**
 * @brief 插件基类,一切插件必需承继此类
 * @notice 必需完成3个形象办法: reg(),name(),description()
 */
abstract class pluginBase extends IInterceptorBase
{
    //谬误信息
    protected $error = array();
    //注册事情接口,外部经过挪用payment::reg(事情,工具实例,办法);
    public function reg(){}
    /**
     * @brief 默许插件参数信息,写入到plugin表config_param字段
     * @return array("字段名" => array(
         "name"    => "文字显示",
         "type"    => "数据类型【text,radio,checkbox,select】",
         "pattern" => "数据校验【int,float,date,datetime,require,正则表白式】",
         "value"   => "1,数组:枚举数据【radio,checkbox,select】的预设值,array(名字=>数据); 2,字符串:【text】默许数据",
        ))
     */
    public static function configName()
    {
        return array();
    }
    /**
     * @brief 插件装置
     * @return boolean
     */
    public static function install()
    {
        return true;
    }
    /**
     * @brief 插件卸载
     * @return boolean
     */
    public static function uninstall()
    {
        return true;
    }
    /**
     * @brief 插件名字
     * @return string
     */
    public static function name()
    {
        return "插件称号";
    }
    /**
     * @brief 插件性能形容
     * @return string
     */
    public static function description()
    {
        return "插件形容";
    }
    /**
     * @brief 插件应用阐明
     * @return string
     */
    public static function explain()
    {
        return "";
    }
    /**
     * @brief 猎取DB中录入的设置装备摆设参数
     * @return array
     */
    public function config()
    {
        $className= get_class($this);
        $pluginDB = new IModel('plugin');
        $dataRow  = $pluginDB->getObj('class_name = "'.$className.'"');
        if($dataRow && $dataRow['config_param'])
        {
            return JSON::decode($dataRow['config_param']);
        }
        return array();
    }
    /**
     * @brief 前往谬误信息
     * @return array
     */
    public function getError()
    {
        return $this->error ? join("\r\n",$this->error) : "";
    }
    /**
     * @brief 写入谬误信息
     * @return array
     */
    public function setError($error)
    {
        $this->error[] = $error;
    }
    /**
     * @brief 插件视图衬着有规划
     * @param string $view 视图名字
     * @param array  $data 视图外面的数据
     */
    public function redirect($view,$data = array())
    {
        if($data === true)
        {
            $this->controller()->redirect($view);
        }
        else
        {
            $__className      = get_class($this);
            $__pluginViewPath = plugin::path().$__className."/".$view;
            $result = self::controller()->render($__pluginViewPath,$data);
            if($result === false)
            {
                IError::show($__className."/".$view."插件视图没有存正在");
            }
        }
    }
    /**
     * @brief 插件视图衬着去掉规划
     * @param string $view 视图名字
     * @param array  $data 视图外面的数据
     */
    public function view($view,$data = array())
    {
        self::controller()->layout = "";
        $this->redirect($view,$data);
    }
    /**
     * @brief 插件物理目次
     * @param string 插件门路地点
     */
    public function path()
    {
        return plugin::path().get_class($this)."/";
    }
    /**
     * @brief 插件WEB目次
     * @param string 插件门路地点
     */
    public function webPath()
    {
        return plugin::webPath().get_class($this)."/";
    }
}

更多相干php常识,请拜访php教程!

以上就是PHP钩子机制原理及详解的具体内容,更多请存眷资源魔其它相干文章!

标签: php php开发教程 php开发资料 php开发自学

抱歉,评论功能暂时关闭!