栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > PHP

PHP钩子机制原理及详解

PHP 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力


PHP钩子机制原理及详解

什么是钩子?

大家想必听过插件,wordpress插件特别多,这个就是用钩子机制实现的。

当代码在运行的过程中,我们预先在运行的几个特殊点里执行一些特殊方法:例如在运行方法(例如Blog::add的add方法)之前记录输入参数、运行方法之后记录处理结果,这个运行方法之前、运行方法之后就是简单的钩子(挂载点),我们在这个钩子上放置钩子函数(记录输入参数、记录处理结果),执行一些和程序运行不相关的任务。

add();
Log::write(json_encode($res));

如果在运行方法之前放置的是一个onBeforeRunActionCallback()的方法,这个方法可能最开始的时候是空的,但我们以后就可以不去修改原有代码,直接在onBeforeRunActionCallback()里面加代码逻辑就可以了,例如记录日志、参数过滤等等。

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);
  }
     }
 }
    }
}

如何实现?

简单的

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

2、在某个配置文件或者函数里统一注册插件。

class Hook
{
    //action hooks array  
    private static $actions = array();
    
    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 ;
    }
    
    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;
    }
    
    public static function get_action($hook)
    {
 $hook=mb_strtolower($hook,CHARSET);
 return (isset(self::$actions[$hook]))? self::$actions[$hook]:FALSE;
    }
    
    public static function exists_action($hook)
    {
 $hook=mb_strtolower($hook,CHARSET);
 return (isset(self::$actions[$hook]))? TRUE:FALSE;
    }
}
 
    
    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();

带安装/卸载的

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

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

2、插件类增加一个初始化的方法,去数据查找已经安装的插件,运行插件必须执行的注册方法(reg),注册插件方法注册钩子到挂载点。

3、固定把插件放在某个目录,并安照一定规范写配置文件。后台有插件列表页面,遍历指定目录下的插件。当安装的时候,将插件信息记录到数据库,卸载的时候删除数据库记录信息。

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();
     }
 }
    }
    
    public static function reg($event,$classObj,$method = "")
    {
 if(!isset(self::$_listen[$event]))
 {
     self::$_listen[$event] = array();
 }
 self::$_listen[$event][] = array($classObj,$method);
    }
    
    public static function get($event = '')
    {
 if($event)
 {
     if( isset(self::$_listen[$event]) )
     {
  return self::$_listen[$event];
     }
     return null;
 }
 return self::$_listen;
    }
    
    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);
    }
    
    public static function path()
    {
 return IWeb::$app->getbasePath()."plugins/";
    }
    
    public static function webPath()
    {
 return IUrl::creatUrl('')."plugins/";
    }
    
    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();
    }
    
    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");}
}

abstract class pluginbase extends IInterceptorbase
{
    //错误信息
    protected $error = array();
    //注册事件接口,内部通过调用payment::reg(事件,对象实例,方法);
    public function reg(){}
    
    public static function configName()
    {
 return array();
    }
    
    public static function install()
    {
 return true;
    }
    
    public static function uninstall()
    {
 return true;
    }
    
    public static function name()
    {
 return "插件名称";
    }
    
    public static function description()
    {
 return "插件描述";
    }
    
    public static function explain()
    {
 return "";
    }
    
    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();
    }
    
    public function getError()
    {
 return $this->error ? join("rn",$this->error) : "";
    }
    
    public function setError($error)
    {
 $this->error[] = $error;
    }
    
    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."插件视图不存在");
     }
 }
    }
    
    public function view($view,$data = array())
    {
 self::controller()->layout = "";
 $this->redirect($view,$data);
    }
    
    public function path()
    {
 return plugin::path().get_class($this)."/";
    }
    
    public function webPath()
    {
 return plugin::webPath().get_class($this)."/";
    }
}

更多相关php知识,请访问php教程!

以上就是PHP钩子机制原理及详解的详细内容,更多请关注考高分网其它相关文章!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/263349.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号