最近在使用ThinkPHP5开发应用,之前习惯了一些插件开发,感觉很好且,但基于现在文档不是很完善,所以自己研究了一下基础功能。将TP3.2中的插件功能迁移了过来。
加入插件的方法:
1、在系统的index.php入口中加入如下配置
1 2 3 4 |
// 插件目录 define('ADDON_PATH', __DIR__ . '/../addons/'); // 开启系统行为 define('APP_HOOK', true); |
2、在数据库中加入addons插件表和hooks钩子表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
CREATE TABLE `addons` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `name` varchar(40) NOT NULL COMMENT '插件名或标识', `title` varchar(20) NOT NULL DEFAULT '' COMMENT '中文名', `description` text COMMENT '插件描述', `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态', `config` text COMMENT '配置', `author` varchar(40) DEFAULT '' COMMENT '作者', `version` varchar(20) DEFAULT '' COMMENT '版本号', `create_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '安装时间', `has_adminlist` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '是否有后台列表', PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='插件表'; CREATE TABLE `hooks` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `name` varchar(40) NOT NULL DEFAULT '' COMMENT '钩子名称', `description` text NOT NULL COMMENT '描述', `type` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT '类型', `update_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', `addons` varchar(255) NOT NULL DEFAULT '' COMMENT '钩子挂载的插件 '',''分割', `status` tinyint(2) DEFAULT '1', PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) USING BTREE ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; |
3、在ThinkPHP5的根目录中创建addons目录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
www WEB项目目录 ├─composer.json composer定义文件 ├─README.md README文件 ├─LICENSE.txt 授权说明文件 ├─<strong style="color: red;">addons 插件目录(新增)</strong> ├─application 应用目录 │ ├─common 公共模块目录(可以更改) │ ├─runtime 应用的运行时目录(可写,可定制) │ ├─module 模块目录 │ │ └─ ... 更多类库目录 │ ├─common.php 公共函数文件 │ ├─config.php 公共配置文件 │ ├─route.php 路由配置文件 │ └─database.php 数据库配置文件 │ ├─public WEB目录(对外访问目录) │ ├─index.php 入口文件 │ ├─.htaccess 用于apache的重写 │ └─router.php 快速测试文件(用于PHP内置webserver) │ ├─thinkphp 框架系统目录 |
4、在application中的common.php公共函数里添加两个公共方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/** * 获取插件类的类名 * @param strng $name 插件名 * @param string $ext 扩展名 */ function get_addon_class($name, $ext = EXT) { // 初始化命名空间及类名 $class = "addons\\{$name}\\" . ucfirst($name); return $class; } /** * 处理插件钩子 * @param string $hook 钩子名称 * @param mixed $params 传入参数 * @return void */ function hook($hook, $params = []) { // 钩子调用 \think\Hook::listen($hook, $params); } |
5、在application目录下创建common模块,在common模块中创建behavior行为目录,在公共行为目录中创建Hooks.php钩子行为插件,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<?php // +---------------------------------------------------------------------- // | zzstudio [ WE CAN DO IT JUST THINK IT ] // +---------------------------------------------------------------------- // | Copyright (c) 2016 http://www.zzstudio.net All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: Byron Sampson <[email protected]> // +---------------------------------------------------------------------- namespace app\common\behavior; use think\Hook; class Hooks { public function run(&$param = []) { if(defined('BIND_MODULE') && BIND_MODULE === 'Install') return; // 动态加入命名空间 \think\Loader::addNamespace('addons', ADDON_PATH); // 获取钩子数据 $data = S('hooks'); if(!$data){ $hooks = M('Hooks')->getField('name,addons'); // 获取钩子的实现插件信息 foreach ($hooks as $key => $value) { if($value){ $map['status'] = 1; $names = explode(',',$value); $map['name'] = ['IN', $names]; $data = M('Addons')->where($map)->getField('id,name'); if($data){ $addons = array_intersect($names, $data); Hook::add($key, array_map('get_addon_class', $addons)); } } } S('hooks', Hook::get()); }else{ Hook::import($data, false); } } } |
6、在application目录中创建tags.php行为配置文件,并且加入内容
1 2 3 4 5 6 7 8 9 10 11 |
<?php // 系统行为定义 return [ 'app_init' => [ 'app\\common\\behavior\\Hooks' ], 'action_begin'=> [ ], 'app_end'=> [ ] ]; |
到此为止,插件功能就集成完毕了。
接下来我们做个简单的插件试一下:
1、在addons目录中创建一个Base.php 插件的基类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
<?php // +---------------------------------------------------------------------- // | addons [ WE CAN DO IT JUST ZZSTUDIO IT ] // +---------------------------------------------------------------------- // | Copyright (c) 2015 http://www.zzstudio.net All rights reserved. // +---------------------------------------------------------------------- // | Author: byron sampson <[email protected]> // +---------------------------------------------------------------------- namespace addons; /** * 插件类 * @author byron sampson <[email protected]> */ abstract class Base{ /** * 视图实例对象 * @var view * @access protected */ protected $view = null; /** * $info = array( * 'name'=>'Editor', * 'title'=>'编辑器', * 'description'=>'用于增强整站长文本的输入和显示', * 'status'=>1, * 'author'=>'thinkphp', * 'version'=>'0.1' * ) */ public $info = array(); public $addon_path = ''; public $config_file = ''; public $custom_config = ''; public $admin_list = array(); public $custom_adminlist = ''; public $access_url = array(); /** * 基类构造函数 * Base constructor. */ public function __construct() { $this->view = new \think\View(); $this->addon_path = ADDON_PATH . $this->getName() . '/'; $TMPL_PARSE_STRING = C('parse_str'); $TMPL_PARSE_STRING['__ADDONROOT__'] = $TMPL_PARSE_STRING['__ADDONS__'] . '/' . $this->getName(); C('parse_str', $TMPL_PARSE_STRING); if(is_file($this->addon_path . 'config.php')){ $this->config_file = $this->addon_path . 'config.php'; } } /** * 模板主题设置 * @access protected * @param string $theme 模版主题 * @return Action */ final protected function theme($theme) { $this->view->theme($theme); return $this; } /** * 模板变量赋值 * @access protected * @param mixed $name 要显示的模板变量 * @param mixed $value 变量的值 * @return Action */ final protected function assign($name, $value='') { $this->view->assign($name,$value); return $this; } //用于显示模板的方法 final protected function fetch($templateFile = CONTROLLER_NAME) { if(!is_file($templateFile)){ $templateFile = $this->addon_path . $templateFile . ".html"; if(!is_file($templateFile)){ E(L('_TEMPLATE_NOT_EXIST_') . ":$templateFile"); } } return $this->view->fetch($templateFile); } /** * 获取插件名 * @return string */ final public function getName() { $class = get_class($this); list($space, $name, $class) = explode('\\', $class); return $name; } /** * 检查配置信息是否完整 * @return bool */ final public function checkInfo() { $info_check_keys = array('name','title','description','status','author','version'); foreach ($info_check_keys as $value) { if(!array_key_exists($value, $this->info)) return false; } return true; } /** * 获取插件的配置数组 */ final public function getConfig($name = '') { static $_config = []; if(empty($name)){ $name = $this->getName(); } if(isset($_config[$name])){ return $_config[$name]; } $map['name'] = $name; $map['status'] = 1; $config = M('Addons')->where($map)->getField('config'); if($config){ $config = json_decode($config, true); }else{ $config = []; $temp_arr = include $this->config_file; foreach ($temp_arr as $key => $value) { if($value['type'] == 'group'){ foreach ($value['options'] as $gkey => $gvalue) { foreach ($gvalue['options'] as $ikey => $ivalue) { $config[$ikey] = $ivalue['value']; } } }else{ $config[$key] = $temp_arr[$key]['value']; } } } $_config[$name] = $config; return $config; } /** * 必须实现安装 * @return mixed */ abstract public function install(); /** * 必须卸载插件方法 * @return mixed */ abstract public function uninstall(); } |
2、在插件表(addons),钩子表(hooks)中加入相应的数据
1 2 3 |
INSERT INTO `addons` (`id`, `name`, `title`, `description`, `status`, `config`, `author`, `version`, `create_time`, `has_adminlist`) VALUES ('2', 'test', 'test插件', 'test插件简介', '1', NULL, 'byron sampson', '0.1', '1438154545', '0'); INSERT INTO .`hooks` (`id`, `name`, `description`, `type`, `update_time`, `addons`, `status`) VALUES ('21', 'demo', 'demo钩子', '1', '1384481614', 'test', '1'); |
3、在addons目录中创建一个test目录,且内部创建一个Test.php插件类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<?php // +---------------------------------------------------------------------- // | test [ WE CAN DO IT JUST THINK IT ] // +---------------------------------------------------------------------- // | Copyright (c) 2016 http://www.zzstudio.net All rights reserved. // +---------------------------------------------------------------------- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // +---------------------------------------------------------------------- // | Author: Byron Sampson <[email protected]> // +---------------------------------------------------------------------- namespace addons\test; class Test extends \addons\Base { /** * 实现demo钩子 * @param array $params */ public function demo($params = []) { echo 'demo hook!page is ' . $params['p']; } /** * 安装方法 */ public function install() { // TODO: Implement install() method. } /** * 卸载方法 */ public function uninstall() { // TODO: Implement uninstall() method. } } |
4、在控制器中使用hook方法来调用钩子
1 |
hook('demo', ['p'=>'page']); |
5、ok,大功告成
Fatal error: Call to undefined function app\common\behavior\S()
tp5.0不支持呀?报这个请问这个怎么解决?
这是针对beta版的方案,在正式版中可以用 composer require zzstudio/think-addons:1.0.4.2 来替代
thinkphp5 版本中有M、C这些方法?
这是beta版时加的功能,在正式版中有新的方案或自行调整一下
很棒