插件開發(fā)指南

2018-02-23 16:21 更新

完整的插件運行流程

插件安裝流程

首先 ,我們打開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'=>'用于增強(qiáng)整站長文本的輸入和顯示',
            'status'=>1,
            'author'=>'thinkphp',
            'version'=>'0.1'
        );
    public function install(){
        return true;
    }
    public function uninstall(){
        return true;
    }
    /**
     * 編輯器掛載的文章內(nèi)容鉤子
     * @param array('name'=>'表單name','value'=>'表單對應(yīng)的值')
     */
    public function documentEditFormContent($data){
        $this->assign('addons_data', $data);
        $this->assign('addons_config', $this->getConfig());
        $this->display('content');
    }
    /**
     * 討論提交的鉤子使用編輯器插件擴(kuò)展
     * @param array('name'=>'表單name','value'=>'表單對應(yīng)的值')
     */
    public function topicComment ($data){
        $this->assign('addons_data', $data);
        $this->assign('addons_config', $this->getConfig());
        $this->display('content');
    }
}

整個插件就是一個特殊的繼承了 Addon抽象類的子類。必須實現(xiàn) install和uninstall方法。 然后必須有一個自己的info屬性,作為插件自己的信息。name、titile、description、status、author、version這6個是必須的。到時候后臺列表里在未安裝時會讀取插件信息,顯示出來。status為1或者0,表示安裝插件后是否立即啟用。

install和uninstall方法用于后臺插件安裝和卸載時候調(diào)用。返回true或者false用于告訴后臺我安裝卸載的準(zhǔn)備工作是否做好了。比如我安裝時候創(chuàng)建了某些表,創(chuàng)建成功可以安裝,不成功提示錯誤。卸載前應(yīng)該將安裝時做的操作恢復(fù)到安裝前狀態(tài)。 其次,插件被安裝后才能配置插件,卸載后會同時去除鉤子處掛載的插件名,安裝會添加鉤子對應(yīng)的插件名。

后臺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('執(zhí)行插件預(yù)安裝操作失敗'.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('寫入插件數(shù)據(jù)失敗');
    }
}

實例化插件類->插件info是否正確->執(zhí)行install方法,預(yù)安裝操作->添加插件數(shù)據(jù)到數(shù)據(jù)庫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('執(zhí)行插件預(yù)卸載操作失敗'.session('addons_uninstall_error'));
    $hooks_update = D('Hooks')->removeHooks($db_addons['name']);
    if($hooks_update === false){
        $this->error('卸載插件所掛載的鉤子數(shù)據(jù)失敗');
    }
    S('hooks', null);
    $delete = $addonsModel->where("name='{$db_addons['name']}'")->delete();
    if($delete === false){
        $this->error('卸載插件失敗');
    }else{
        $this->success('卸載成功');
    }
}

實例化插件類->執(zhí)行預(yù)卸載->卸載插件所掛載過的鉤子處信息->刪除插件文件。 每個步驟出錯都會提示,install 方法中錯誤用 session('addons_uninstall_error', 'error')傳遞。

插件運行流程

hook函數(shù)調(diào)用Hook類的listen靜態(tài)方法觸發(fā)鉤子->獲取鉤子掛載的開啟的插件->執(zhí)行對應(yīng)插件實現(xiàn)的鉤子同名方法(這個在init_hooks函數(shù)會初始化)

代碼上就是如下的過程:

hook('documentEditFormContent');

然后hook函數(shù)遍歷 Hook類里的$tag屬性,知道有哪些插件可被調(diào)用,接下來去讀取配置和狀態(tài),啟用就去執(zhí)行鉤子

/**
 * 處理插件鉤子
 * @param string $hook   鉤子名稱
 * @param mixed $params 傳入?yún)?shù)
 * @return void
 */
function hook($hook,$params=array()){
    \Think\Hook::listen($hook,$params);
}
而Hook::listen方法里面
    /**
 * 監(jiān)聽標(biāo)簽的插件
 * @param string $tag 標(biāo)簽名稱
 * @param mixed $params 傳入?yún)?shù)
 * @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 則中斷插件執(zhí)行
                return ;
            }
        }
        if(APP_DEBUG) { // 記錄行為的執(zhí)行日志
            trace('[ '.$tag.' ] --END-- [ RunTime:'.G($tag.'Start',$tag.'End',6).'s ]','','INFO');
        }
    }
    return;
}
/**
 * 執(zhí)行某個插件
 * @param string $name 插件名稱
 * @param Mixed $params 傳入的參數(shù)
 * @return void
 */
static public function exec($name, $tag,&$params=NULL) {
    if(false === strpos($name,'\\')) {
        // 插件(多個入口)
        $class   =  "Addons\\{$name}\\{$name}Addon";
    }else{
        // 行為擴(kuò)展(只有一個run入口方法)
        $class   =  $name.'Behavior';
        $tag    =   'run';
    }
    $addon   = new $class();
    return $addon->$tag($params);
}
#實例化一個插件類,我們有g(shù)et_addon_classs()函數(shù),傳入插件名即可或得插件類名,然后直接new 就行了,區(qū)分大小寫 
$addons_class = new get_addon_classs($addon_name);

$addon->$tag() 這個方法就是去執(zhí)行鉤子處掛載的和鉤子同名的插件方法。

這個方法里可以display渲染模板,默認(rèn)插件的模板就在插件目錄下,比如Editor插件類里用的$this->display('content');。

如果你想有目錄層可以傳入目錄/模板這樣的參數(shù),這樣是不支持主題的,如果要支持主題,也可以傳入具體的模板路徑,使用T函數(shù)如T('Addons://Attachment@Article/edit')這樣,到時候想切換主題了,T函數(shù)定位模板之前C('DEFAULT_THEME','default')這樣就行了。

當(dāng)然這個方法支持傳參,只允許一個,為了能實現(xiàn)引用。所以多個參數(shù),請封裝成數(shù)組,傳入hook函數(shù)的第二個參數(shù)。

執(zhí)行完畢,這個鉤子的某個插件前臺功能方法就運行完了。

插件被禁用,鉤子處的插件不會被執(zhí)行該同名方法,并且插件后臺列表里不會出現(xiàn)該插件的列表。

插件不光前臺能用 ,后臺有鉤子,并且插件里實現(xiàn)了該鉤子,也可以用。為了前后臺編輯器插件可以配置不同的編輯器,我們復(fù)制了一份Editor插件,改名為EditroForAdmin了。

還有后臺首頁,其實是用AdminIndex 鉤子掛載了幾個插件。

后臺中,插件默認(rèn)會在 插件列表里出現(xiàn)。默認(rèn)沒有安裝過的會顯示在前面。

基本的數(shù)據(jù)字段都是讀取的插件類里的info屬性數(shù)組。插件未安裝的時候是不可以設(shè)置的。

假如插件需要url訪問,就必須插件里有Controller目錄和tp結(jié)構(gòu)一樣。 只不過生成這個方法的用addons_url('插件名://控制器名/操作方法'),生成訪問url。并且訪問權(quán)限由插件去做。具體寫法,參照附件Attachment插件 里的控制器寫法。和模板里url調(diào)用。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號