返回介绍

插件开发指南

发布于 2025-02-20 12:50:25 字数 7881 浏览 0 评论 0 收藏 0

完整的插件运行流程

插件安装流程

首先 ,我们打开 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文