插件开发指南
完整的插件运行流程
插件安装流程
首先 ,我们打开 Editor 插件的定义类
<?php
// +----------------------------------------------------------------------
// | OneThink [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013 http://www.onethink.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: yangweijie <yangweijiester@gmail.com> <code-tech.diandian.com>
// +----------------------------------------------------------------------
namespace Addons\Editor;
use Common\Controller\Addon;
/**
* 编辑器插件
* @author yangweijie <yangweijiester@gmail.com>
*/
class EditorAddon extends Addon{
public $info = array(
'name'=>'Editor',
'title'=>'前台编辑器',
'description'=>'用于增强整站长文本的输入和显示',
'status'=>1,
'author'=>'thinkphp',
'version'=>'0.1'
);
public function install(){
return true;
}
public function uninstall(){
return true;
}
/**
* 编辑器挂载的文章内容钩子
* @param array('name'=>'表单 name','value'=>'表单对应的值')
*/
public function documentEditFormContent($data){
$this->assign('addons_data', $data);
$this->assign('addons_config', $this->getConfig());
$this->display('content');
}
/**
* 讨论提交的钩子使用编辑器插件扩展
* @param array('name'=>'表单 name','value'=>'表单对应的值')
*/
public function topicComment ($data){
$this->assign('addons_data', $data);
$this->assign('addons_config', $this->getConfig());
$this->display('content');
}
}
整个插件就是一个特殊的继承了 Addon 抽象类的子类。必须实现 install 和 uninstall 方法。 然后必须有一个自己的 info 属性,作为插件自己的信息。name、titile、description、status、author、version 这 6 个是必须的。到时候后台列表里在未安装时会读取插件信息,显示出来。status 为 1 或者 0,表示安装插件后是否立即启用。
install 和 uninstall 方法用于后台插件安装和卸载时候调用。返回 true 或者 false 用于告诉后台我安装卸载的准备工作是否做好了。比如我安装时候创建了某些表,创建成功可以安装,不成功提示错误。卸载前应该将安装时做的操作恢复到安装前状态。 其次,插件被安装后才能配置插件,卸载后会同时去除钩子处挂载的插件名,安装会添加钩子对应的插件名。
后台 AddonsController.class.php
/**
* 安装插件
*/
public function install(){
$addon_name = trim(I('addon_name'));
$class = get_addon_class($addon_name);
if(!class_exists($class))
$this->error('插件不存在');
$addons = new $class;
$info = $addons->info;
if(!$info || !$addons->checkInfo())//检测信息的正确性
$this->error('插件信息缺失');
session('addons_install_error',null);
$install_flag = $addons->install();
if(!$install_flag){
$this->error('执行插件预安装操作失败'.session('addons_install_error'));
}
$addonsModel = D('Addons');
$data = $addonsModel->create($info);
if(is_array($addons->admin_list) && $addons->admin_list !== array()){
$data['has_adminlist'] = 1;
}else{
$data['has_adminlist'] = 0;
}
if(!$data)
$this->error($addonsModel->getError());
if($addonsModel->add($data)){
$config = array('config'=>json_encode($addons->getConfig()));
$addonsModel->where("name='{$addon_name}'")->save($config);
$hooks_update = D('Hooks')->updateHooks($addon_name);
if($hooks_update){
S('hooks', null);
$this->success('安装成功');
}else{
$addonsModel->where("name='{$addon_name}'")->delete();
$this->error('更新钩子处插件失败,请卸载后尝试重新安装');
}
}else{
$this->error('写入插件数据失败');
}
}
实例化插件类->插件 info 是否正确->执行 install 方法,预安装操作->添加插件数据到数据库 Addons 表和 Hooks 表
每个步骤出错都会提示,install 方法中错误用 session('addons_install_error', 'error') 传递。
插件卸载流程
卸载流程和安装相反
后台 AddonsController.class.php
/**
* 卸载插件
*/
public function uninstall(){
$addonsModel = M('Addons');
$id = trim(I('id'));
$db_addons = $addonsModel->find($id);
$class = get_addon_class($db_addons['name']);
$this->assign('jumpUrl',U('index'));
if(!$db_addons || !class_exists($class))
$this->error('插件不存在');
session('addons_uninstall_error',null);
$addons = new $class;
$uninstall_flag = $addons->uninstall();
if(!$uninstall_flag)
$this->error('执行插件预卸载操作失败'.session('addons_uninstall_error'));
$hooks_update = D('Hooks')->removeHooks($db_addons['name']);
if($hooks_update === false){
$this->error('卸载插件所挂载的钩子数据失败');
}
S('hooks', null);
$delete = $addonsModel->where("name='{$db_addons['name']}'")->delete();
if($delete === false){
$this->error('卸载插件失败');
}else{
$this->success('卸载成功');
}
}
实例化插件类->执行预卸载->卸载插件所挂载过的钩子处信息->删除插件文件。 每个步骤出错都会提示,install 方法中错误用 session('addons_uninstall_error', 'error') 传递。
插件运行流程
hook 函数调用 Hook 类的 listen 静态方法触发钩子->获取钩子挂载的开启的插件->执行对应插件实现的钩子同名方法(这个在 init_hooks 函数会初始化)
代码上就是如下的过程:
hook('documentEditFormContent');
然后 hook 函数遍历 Hook 类里的$tag 属性,知道有哪些插件可被调用,接下来去读取配置和状态,启用就去执行钩子
/**
* 处理插件钩子
* @param string $hook 钩子名称
* @param mixed $params 传入参数
* @return void
*/
function hook($hook,$params=array()){
\Think\Hook::listen($hook,$params);
}
而 Hook::listen 方法里面
/**
* 监听标签的插件
* @param string $tag 标签名称
* @param mixed $params 传入参数
* @return void
*/
static public function listen($tag, &$params=NULL) {
if(isset(self::$tags[$tag])) {
if(APP_DEBUG) {
G($tag.'Start');
trace('[ '.$tag.' ] --START--','','INFO');
}
foreach (self::$tags[$tag] as $name) {
APP_DEBUG && G($name.'_start');
$result = self::exec($name, $tag,$params);
if(APP_DEBUG){
G($name.'_end');
trace('Run '.$name.' [ RunTime:'.G($name.'_start',$name.'_end',6).'s ]','','INFO');
}
if(false === $result) {
// 如果返回 false 则中断插件执行
return ;
}
}
if(APP_DEBUG) { // 记录行为的执行日志
trace('[ '.$tag.' ] --END-- [ RunTime:'.G($tag.'Start',$tag.'End',6).'s ]','','INFO');
}
}
return;
}
/**
* 执行某个插件
* @param string $name 插件名称
* @param Mixed $params 传入的参数
* @return void
*/
static public function exec($name, $tag,&$params=NULL) {
if(false === strpos($name,'\\')) {
// 插件(多个入口)
$class = "Addons\\{$name}\\{$name}Addon";
}else{
// 行为扩展(只有一个 run 入口方法)
$class = $name.'Behavior';
$tag = 'run';
}
$addon = new $class();
return $addon->$tag($params);
}
#实例化一个插件类,我们有 get_addon_classs() 函数,传入插件名即可或得插件类名,然后直接 new 就行了,区分大小写
$addons_class = new get_addon_classs($addon_name);
$addon->$tag() 这个方法就是去执行钩子处挂载的和钩子同名的插件方法。
这个方法里可以 display 渲染模板,默认插件的模板就在插件目录下,比如 Editor 插件类里用的$this->display('content');。
如果你想有目录层可以传入目录/模板这样的参数,这样是不支持主题的,如果要支持主题,也可以传入具体的模板路径,使用 T 函数如 T('Addons://Attachment@Article/edit') 这样,到时候想切换主题了,T 函数定位模板之前 C('DEFAULT_THEME','default') 这样就行了。
当然这个方法支持传参,只允许一个,为了能实现引用。所以多个参数,请封装成数组,传入 hook 函数的第二个参数。
执行完毕,这个钩子的某个插件前台功能方法就运行完了。
插件被禁用,钩子处的插件不会被执行该同名方法,并且插件后台列表里不会出现该插件的列表。
插件不光前台能用 ,后台有钩子,并且插件里实现了该钩子,也可以用。为了前后台编辑器插件可以配置不同的编辑器,我们复制了一份 Editor 插件,改名为 EditroForAdmin 了。
还有后台首页,其实是用 AdminIndex 钩子挂载了几个插件。
后台中,插件默认会在 插件列表里出现。默认没有安装过的会显示在前面。
基本的数据字段都是读取的插件类里的 info 属性数组。插件未安装的时候是不可以设置的。
假如插件需要 url 访问,就必须插件里有 Controller 目录和 tp 结构一样。 只不过生成这个方法的用 addons_url('插件名://控制器名/操作方法'),生成访问 url。并且访问权限由插件去做。具体写法,参照附件 Attachment 插件 里的控制器写法。和模板里 url 调用。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论