PHP中插件机制的一种实现方案

插件,亦即Plug-in,是指一类特定的功能模块(通常由第三方开发者实现),它的特点是:当你需要它的时候激活它,不需要它的时候禁用/删除它;且无 论是激活还是禁用都不影响系统核心模块的运行,也就是说插件是一种非侵入式的模块化设计,实现了核心程序与插件程序的松散耦合。一个典型的例子就是 Wordpress中众多的第三方插件,比如Akimet插件用于对用户的评论进行Spam过滤。

一个健壮的插件机制,我认为必须具备以下特点:

  • 插件的动态监听和加载(Lookup) 
  • 插件的动态触发 
  • 以上两点的实现均不影响核心程序的运行

 

要在程序中实现插件,我们首先应该想到的就是定义不同的钩子(Hooks);“钩子”是一个很形象的逻辑概念,你可以认为它是系统预留的插件触发条件。它 的逻辑原理如下:当系统执行到某个钩子时,会判断这个钩子的条件是否满足;如果满足,会转而先去调用钩子所制定的功能,然后返回继续执行余下的程序;如果 不满足,跳过即可。这有点像汇编中的“中断保护”逻辑。

某些钩子可能是系统事先就设计好的,比如之前我举的关于评论Spam过滤的钩子,通常它已经由核心系统开发人员设计进了评论的处理逻辑中;另外一类钩子则 可能是由用户自行定制的(由第三方开发人员制定),通常存在于表现层,比如一个普通的PHP表单显示页面中。

可能你感觉上面的话比较无聊,让人昏昏欲睡;但是要看懂下面我写的代码,理解以上的原理是必不可少的。

下面进行PHP中插件机制的核心实现,整个机制核心分为三大块:

  • 一个插件经理类:这是核心之核心。它是一个应用程序全局Global对象。它主要有三个职责: 
    • 负责监听已经注册了的所有插件,并实例化这些插件对象。 
    • 负责注册所有插件。 
    • 当钩子条件满足时,触发对应的对象方法。

         

  • 插件的功能实现:这大多由第三方开发人员完成,但需要遵循一定的规则,这个规则是插件机制所规定的,因插件机制的不同而不同,下面的显 示代码你会看到这个规则。 
  • 插件的触发:也就是钩子的触发条件。具体来说这是一小段代码,放置在你需要插件实现的地方,用于触发这个钩子。

 

原理讲了一大堆,下面看看我的实现方案:

插件经理PluginManager类:

  1 <?
  2 /**
  3 * STBLOG PluginManager Class
  4 *
  5 * 插件机制的实现核心类
  6 *
  7 * @package        STBLOG
  8 * @subpackage    Libraries
  9 * @category    Libraries
 10 * @author        Saturn
 11 */
 12 class PluginManager
 13 {
 14     /**
 15      * 监听已注册的插件
 16      *
 17      * @access private
 18      * @var array
 19      */
 20     private $_listeners = array();
 21      /**
 22      * 构造函数
 23      *  
 24      * @access public
 25      * @return void
 26      */
 27     public function __construct()
 28     {
 29         #这里$plugin数组包含我们获取已经由用户激活的插件信息
 30      #为演示方便,我们假定$plugin中至少包含
 31      #$plugin = array(
 32         #    'name' => '插件名称',
 33         #    'directory'=>'插件安装目录'
 34         #);
 35         $plugins = get_active_plugins();#这个函数请自行实现
 36         if($plugins)
 37         {
 38             foreach($plugins as $plugin)
 39             {//假定每个插件文件夹中包含一个actions.php文件,它是插件的具体实现
 40                 if (@file_exists(STPATH .'plugins/'.$plugin['directory'].'/actions.php'))
 41                 {
 42                     include_once(STPATH .'plugins/'.$plugin['directory'].'/actions.php');
 43                     $class = $plugin['name'].'_actions';
 44                     if (class_exists($class))  
 45                     {
 46                         //初始化所有插件
 47                         new $class($this);
 48                     }
 49                 }
 50             }
 51         }
 52         #此处做些日志记录方面的东西
 53     }
 54      
 55     /**
 56      * 注册需要监听的插件方法(钩子)
 57      *
 58      * @param string $hook
 59      * @param object $reference
 60      * @param string $method
 61      */
 62     function register($hook, &$reference, $method)
 63     {
 64         //获取插件要实现的方法
 65         $key = get_class($reference).'->'.$method;
 66         //将插件的引用连同方法push进监听数组中
 67         $this->_listeners[$hook][$key] = array(&$reference, $method);
 68         #此处做些日志记录方面的东西
 69     }
 70     /**
 71      * 触发一个钩子
 72      *
 73      * @param string $hook 钩子的名称
 74      * @param mixed $data 钩子的入参
 75      *    @return mixed
 76      */
 77     function trigger($hook, $data='')
 78     {
 79         $result = '';
 80         //查看要实现的钩子,是否在监听数组之中
 81         if (isset($this->_listeners[$hook]) && is_array($this->_listeners[$hook]) && count($this->_listeners[$hook]) > 0)
 82         {
 83             // 循环调用开始
 84             foreach ($this->_listeners[$hook] as $listener)
 85             {
 86                 // 取出插件对象的引用和方法
 87                 $class =& $listener[0];
 88                 $method = $listener[1];
 89                 if(method_exists($class,$method))
 90                 {
 91                     // 动态调用插件的方法
 92                     $result .= $class->$method($data);
 93                 }
 94             }
 95         }
 96         #此处做些日志记录方面的东西
 97         return $result;
 98     }
 99 }
100 ?>

 

以上代码加上注释不超过100行,就完成了整个插件机制的核心。需要再次说明的是,你必须将它设置成全局类,在所有 需要用到插件的地方,优先加载。用#注释的地方是你需要自行完成的部分,包括插件的获取和日志记录等等。

下面是一个简单插件的实现。    

 1 <?
 2 /**
 3 * 这是一个Hello World简单插件的实现
 4 *
 5 * @package        DEMO
 6 * @subpackage    DEMO
 7 * @category    Plugins
 8 * @author        Saturn
 9 */
10 /**
11 *需要注意的几个默认规则:
12 *    1. 本插件类的文件名必须是action
13 *    2. 插件类的名称必须是{插件名_actions}
14 */
15 class DEMO_actions
16 {
17     //解析函数的参数是pluginManager的引用
18     function __construct(&$pluginManager)
19     {
20         //注册这个插件
21         //第一个参数是钩子的名称
22         //第二个参数是pluginManager的引用
23         //第三个是插件所执行的方法
24         $pluginManager->register('demo', $this, 'say_hello');
25     }
26      
27     function say_hello()
28     {
29         echo 'Hello World';
30     }
31 }
32 ?>  

 

这是一个简单的Hello World插件,用于输出一句话。在实际情况中,say_hello可能包括对数据库的操作,或者是其他一些特定的逻辑,比如调用Akimet API。

插件实现的默认规则由核心系统开发者自行确定。比如本例的一些默认规则我在注释中已经写的很清楚,在此不在赘述。需要特别注意的是钩子名称不要重复。

最后一步,就是定义钩子的触发,你将钩子放在哪里,上面这个插件的方法就会在哪里出发。比如我要将say_hello放到我博客首页Index.php, 那么你在index.php中的某个位置写下:

$pluginManager->trigger('demo','');


第一个参数表示钩子的名字,在本例中它是demo;第二个参数是插件对应方法的入口参数,由于这个例子中没有输入参数,所以为空。

总结 

本篇文章介绍了插件机制在PHP中实现的一种方法和思路,以及我本人对插件机制的理解。初次接触这个东西,可能会比较生涩,难以理解。但是当你结合真实的 例子,再想想程序的运行流程,思路可能会更清晰一些。

转载于:https://www.cnblogs.com/tianakong/p/10760839.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/449140.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python模块学习之glob模块

glob模块 功能描述&#xff1a;glob模块可以使用Unix shell风格的通配符匹配符合特定格式的文件和文件夹&#xff0c;跟windows的文件搜索功能差不多。glob模块并非调用一个子shell实现搜索功能&#xff0c;而是在内部调用了os.listdir()和fnmatch.fnmatch()。查看我之前写的fn…

根据一个属性,剔除 Json 中重复元素(删除 JSON 中重复的部分)

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 [ {"data" : {"code" : "044","post" : "xxx领导", "email" : "…

给生命一个助跑的过程(图)

在高原的上空&#xff0c;常常可以见到秃鹫在翱翔。 秃鹫又叫座山雕&#xff0c;也被人誉为“神鹰”&#xff0c;是高原上体格最大的猛禽。它们往往栖息在海拔2000-5000多米的高山原上&#xff0c;体重达到7-11公斤。秃鹫张开翅膀后&#xff0c;整个身体有2米多长&#xff0c;…

ELK 构建 MySQL 慢日志收集平台详解

ELK 介绍 ELK 最早是 Elasticsearch&#xff08;以下简称ES&#xff09;、Logstash、Kibana 三款开源软件的简称&#xff0c;三款软件后来被同一公司收购&#xff0c;并加入了Xpark、Beats等组件&#xff0c;改名为Elastic Stack&#xff0c;成为现在最流行的开源日志解决方案&…

数据类型转换规则C语言

一、数据类型 为什么对数据进行分类&#xff1a;节约存储空间&#xff0c;提高运行速度。整型&#xff1a;signedsigned char 1 -128~127signed short 2 -32678~32767signed int 4 正负20亿signed long 4/8 正负20亿/9…

Visual Studio 的码云扩展 V1.0.85 发布

开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f; >>> Visual Studio 的码云扩展 V1.0.85 已发布&#xff1a; 针对扩展进行了SDK部分的重写修正克隆窗体和快捷方式的图标颜色随主题变化修正快捷方式 优化用户登录和token处理机制调整了…

用 FastJSON 将 JSON 字符串转换为 Map

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 本文是利用阿里巴巴封装的FastJSON来转换json字符串的。例子如下&#xff1a; import com.alibaba.fastjson.JSON; import com.aliba…

shark恒破解笔记4-API断点GetPrivateProfileStringA

这小节是通过断在GetPrivateProfileStringA&#xff0c;然后找到注册码的。 1.运行程序输入假码111111&#xff0c;提示重启。通过这判断这是一个重启来验证的&#xff0c;那么它是如何来验证的呢&#xff1f;观察程序目录下会发现有一个名为“config.ini"的文件 那么判断…

开关语句、循环语句、goto

复习&#xff1a; 1、数据类型&#xff1a;signed char 1 %hhd -128~127signed short 2 %hd -32768~32767signed int 4 %d 正负20亿signed long 4 %ldsigned long long 8 %lld 9天开头的19位数unsigned char …

成功男士的健康心理特征

自信&#xff1a;他们普遍都有很强的自信心&#xff0c;有时有咄咄逼人的感觉。 急迫感&#xff1a;他们通常很急地想见到事物的成果&#xff0c;因此会给别人带来许多的压力。他们信仰“时间就是金钱”&#xff0c;不喜欢也不会把宝贵的时间浪费在琐碎的无聊事情上。 脚踏实…

DBlink的创建与删除

创建方式一&#xff1a; create [public] database link link名称 connect to 对方数据库用户identified by 对方数据库用户密码 using 对方数据库ip:端口/服务名; 创建方式二&#xff1a; create [public] database link link名称 connect to 对方数据库用户identified by 对…

kong 启动报错 could not retrieve server_version: timeout

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 报错&#xff1a; nginx: [error] init_by_lua error: /usr/local/share/lua/5.1/kong/init.lua:169: [postgres error] could not ret…

2-STM32物联网开发WIFI(ESP8266)+GPRS(Air202)系统方案安全篇(监听Wi-Fi和APP的数据)

1-STM32物联网开发WIFI(ESP8266)GPRS(Air202)系统方案安全篇(来看一下怎么样监听网络数据,监听电脑上位机软件的数据) 因为那个软件只能监听咱自己电脑上的数据,所以咱就用电脑开个热点,然后让Wi-Fi和APP连接咱电脑的热点,这样就能监听数据了 电脑开个热点 手机APP连接这个热点…

数组越界的后果

数组&#xff1a; 什么是数组&#xff1a;变量的组合&#xff0c;是一种批量定义变量的方式。 定义&#xff1a;类型 数组名[数量];int arr[5]; 使用&#xff1a;数组名[下标];下标&#xff1a;从零开始&#xff0c;范围&#xff1a;0~数量-1。 遍历&#xff1a;与for循环配合…

职场心理:12个建议或许能改变你的一生(图)

心理引言&#xff1a;如何更好实现自我的职业目标&#xff1f;听起来很简单&#xff0c;回答也很简短&#xff0c;就是你必须清楚你最终想要实现什么&#xff1f;只要你这样做&#xff0c;你就已经开始从看的见和看不见的两条路径来实现你的目标。职场是一种特殊的环境&#xf…

python—包

1.模块 可以增加代码的重复利用的方式 避免命名冲突 如何定义模块 模块就是一个普通文件&#xff0c;所以任何代码可以直接书写规范 函数&#xff08;单一的功能&#xff09;类&#xff08;相似功能的组合&#xff0c;或者类似业务模块&#xff09;测试代码 如何使用模块 模块…

Docker Installation : Docker 中安装并启动 Kong

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 Details about how to use Kong in Docker can be found on the DockerHub repository hosting the image: kong. We also have a Docke…

tomcat是否有必要配置环境变量(摘)

之前发表了一篇关于如何安装和配置Tomcat的文章&#xff0c;而最近在开发项目的时候总是报错。后来被公司的大神问了一句&#xff1a;是谁告诉你Tomcat是需要配置环境变量的&#xff1f; 作为新手的我瞬间整个人都不好了&#xff01;于是偷偷百度了一下&#xff0c;终于找到了关…

走迷宫+推箱子

两个小游戏的大致逻辑 走迷宫: 1、定义二维字符数组作为迷宫 2、定义变量记录老鼠的位置 3、获取游戏开始时间 3、进入循环 1、清理屏幕&#xff0c;使用system调用系统命令。 2、显示迷宫(遍历二维字符数组) 3、检查是否到达出口 获取游戏结束时间&#xff0c;计算出走出迷宫用…

Facebook开源ptr:在Python环境中并行运行单元测试

Facebook开源了一个名为Python Test Runner&#xff08;ptr&#xff09;的新项目&#xff0c;允许开发人员运行Python单元测试套件。ptr和现有测试运行器之间的主要区别在于ptr通过爬取存储库查找一定的Python项目&#xff0c;这些项目带有的单元测试在安装文件中已经定义过。 …