这个话题源自最近工作中一个新项目的需求,该项目需要和数量不定的第三方平台进行对接,而这里提到的第三方平台会随着项目进度逐渐增加
站在程序构架的角度,对于这种类型需求如果可以符合“对增加开放,对修改封闭”的设计标准,对于日后的运行和维护无疑都是一件好事。
当然,第一步我们首先需要对第三方平台的对接接口和操作过程进行分析,并分解出统一调用时必要的接口。在php中我们即可以使用类的继承,也可以进行独立的interface设计,这里仅作示例:
[php] view plaincopy
- // 用作实现统一接口的父类,所以插件都需要继承自该类
- // 在调用时可用is_a,is_subclass_of,reflaction等进行检测
- class Plugin{
- protected function init(){
- }
- protected function run(){
- }
- }
- // 示例用的自建插件
- class TestPlugin1 extends Plugin{
- }
- class TestPlugin2 extends Plugin{
- }
当我们完成整个单个插件的接口设计后,下一步就需要一个对插件进行统一调用管理模块
先来考虑一下实现管理模块所面临的3个基本需求:如何检测到插件、如何验证插件有效性、如何加载插件、如何调用插件中的功能
1、如何检测到插件
通常情况下,在我们导入一个新的class时,我们就可以通过其类名对其进行实例化后的使用。但是在插件式的系统构架中,管理类所需要面对的是设计编码时各种未知的插件类名称,唯一的共同点只是这些类拥有共同的调用接口。于是,管理类如何检测插件就是我们需要面对的第一个问题。
其实,虽然我们在编码时并不知道插件的类名,但从根本上来说在php运行过程中,php解释器是必须实际知道每个插件的类名才能对其进行实例化和调用的。这就意味着,我们必须通过某种手段将需要调用的插件类引入,并获得其实际类名称。在这个配置过程中,我个人将其分为动态配置和静态配置2大类。
以静态配置为例,我们可以直接将所需引入的插件名称和其文件路径写入配置文件:
[php] view plaincopy
- // key为引入类的类名,value为该类所需include的文件路径
- $plugin_list = array(
- ‘TestPlugin1‘ => ‘inc/a.plugin.php‘,
- ‘TestPlugin2‘ => ‘inc/a.plugin.php‘
- )
如果觉得静态配置过于繁琐,也可以通过指定插件文件夹,自动载入插件,在此不再赘述。
2、验证插件有效性 以及 加载插件
当我们完成插件的检测后,就可以通过依次引入插件文件、实例化插件类来进行插件加载了。如果考虑到系统稳定性和安全性,也可以在加载前适当的加入插件有效性验证,示例代码如下:
[php] view plaincopy
- foreach($plugin_list as $name => $path){
- @include_once(PATH . $path);
- // 确保类的引入无误
- if(!class_exists($name)){
- continue;
- }
- // 实例化插件
- $plugin = new $name();
- // 检测插件是否继承自指定接口类
- // 也可以通过反射机制的has_Methods对指定接口进行验证
- if(!is_a($plugin, ‘Plugin‘){
- continue;
- }
- // 验证通过,存储实例化后的插件对象
- $my_plugins[$name] = $plugin;
- }
3、调用插件接口
在完成以上2步之后,调用插件接口已经是易如反掌了:
[php] view plaincopy
- foreach($my_plugins as $name=>$plugin){
- $plugin->init();
- $plugin->run();
- }
利用php 实现插件式构架