1
0
EMLOG 内核分析
MENGX 2021-03-17 01:09 40 0

首先要学习的是emlog MVC设计模式,(听说emlog没有用框架是自己写的),MVC即使model view Controller的缩写,是模型(model)-视图(view)-控制器(controller)的缩写

目录结构

├─admin  
│  ├─editor
│  │  ├─lang
│  │  ├─plugins
│  │  └─themes
│  └─views
├─content
│  ├─backup
│  ├─cache
│  ├─plugins
│  ├─templates
│  └─uploadfile
├─include
│  ├─controller
│  ├─lib
│  └─model
├─m
│  └─view
└─t

admin 是管理目录
content    存放了emlog中上传的模版、插件、图片、备份的文件以及emlog的缓存文件
include     emlog的内核文件,此文件定义emlog的MVC操作方式
m              m目录是手机模板目录,用处不多
t                t目录是微语功能的目录 但 emlog似乎在6.0中删除了这个功能

模板的调度过程

emlog使用的是类似MVC的设计模式,将功能和模板分开了

自动加载

在文件/include/lib/function.base.php中

/**
 * 基础函数库
 * @copyright (c) Emlog All Rights Reserved
 */
function __autoload($class) {
    $class = strtolower($class);
    if (file_exists(EMLOG_ROOT . '/include/model/' . $class . '.php')) {
        require_once(EMLOG_ROOT . '/include/model/' . $class . '.php');
    } elseif (file_exists(EMLOG_ROOT . '/include/lib/' . $class . '.php')) {
        require_once(EMLOG_ROOT . '/include/lib/' . $class . '.php');
    } elseif (file_exists(EMLOG_ROOT . '/include/controller/' . $class . '.php')) {
        require_once(EMLOG_ROOT . '/include/controller/' . $class . '.php');
    } else {
        emMsg($class . '加载失败。');
    }
}

emlog加载时调用了_autoload 这个函数的功能是自动寻找调用的Class

模板显示过程

首页index.php中的代码

define('TEMPLATE_PATH', TPLS_PATH.Option::get('nonce_templet').'/');//前台模板路径
$emDispatcher = Dispatcher::getInstance();
$emDispatcher->dispatch();
View::output();

是模板显示的关键,,转到Dispatcher这个类就可以看到实现的方法

public static function getInstance() {//大概功能就是创建这个类
        if(self::$_instance == null) {
            self::$_instance = new Dispatcher();
            return self::$_instance;
        } else {
            return self::$_instance;
        }
    }

    private function __construct() {//初始化就调用的类
        $this->_path = $this->setPath();
        $this->_routingTable = Option::getRoutingTable();

        $urlMode = Option::get('isurlrewrite');
        foreach ($this->_routingTable as $route) {
            if (!isset($route['reg_' . $urlMode])) {
                $reg = isset($route['reg']) ? $route['reg'] : $route['reg_0'];
            } else {
                $reg = $route['reg_' . $urlMode];
            }
            if (preg_match($reg, $this->_path, $matches)) {
                $this->_model = $route['model'];
                $this->_method = $route['method'];
                $this->_params = $matches;
                break;
            } elseif (preg_match($route['reg_0'], $this->_path, $matches)) {
                $this->_model = $route['model'];
                $this->_method = $route['method'];
                $this->_params = $matches;
                break;
            }
        }

        if (empty($this->_model)) {
            show_404_page();
        }
    }

    public function dispatch(){
        $module = new $this->_model();
        $method = $this->_method;
        $module->$method($this->_params);
}

这个函数实现了不同的路由可以调用不同的类来显示界面

$this->_routingTable = Option::getRoutingTable(); 存储了这个对应的列表

 static function getRoutingTable(){
$routingtable = array(
array(
'model' => 'calendar',
'method' => 'generate',
'reg_0' => '|^.*/\?action=cal|',
),
array(
'model' => 'Log_Controller',
'method' => 'displayContent',
'reg_0' => '|^.*/\?(post)=(\d+)(&(comment-page)=(\d+))?([\?&].*)?$|',
'reg_1' => '|^.*/(post)-(\d+)\.html(/(comment-page)-(\d+))?/?([\?&].*)?$|',
'reg_2' => '|^.*/(post)/(\d+)(/(comment-page)-(\d+))?/?$|',
'reg_3' => '|^/([^\./\?=]+)(\.html)?(/(comment-page)-(\d+))?/?([\?&].*)?$|',
),
array(
'model' => 'Record_Controller',
'method' => 'display',
'reg_0' => '|^.*/\?(record)=(\d{6,8})(&(page)=(\d+))?([\?&].*)?$|',
'reg' => '|^.*/(record)/(\d{6,8})/?((page)/(\d+))?/?([\?&].*)?$|',
),
array(
'model' => 'Sort_Controller',
'method' => 'display',
'reg_0' => '|^.*/\?(sort)=(\d+)(&(page)=(\d+))?([\?&].*)?$|',
'reg' => '|^.*/(sort)/([^\./\?=]+)/?((page)/(\d+))?/?([\?&].*)?$|',
),
array(
'model' => 'Tag_Controller',
'method' => 'display',
'reg_0' => '|^.*/\?(tag)=([^&]+)(&(page)=(\d+))?([\?&].*)?$|',
'reg' => '|^.*/(tag)/([^/?]+)/?((page)/(\d+))?/?([\?&].*)?$|',
),
array(
'model' => 'Author_Controller',
'method' => 'display',
'reg_0' => '|^.*/\?(author)=(\d+)(&(page)=(\d+))?([\?&].*)?$|',
'reg' => '|^.*/(author)/(\d+)/?((page)/(\d+))?/?([\?&].*)?$|',
),
array(
'model' => 'Log_Controller',
'method' => 'display',
'reg_0' => '|^.*/\?(page)=(\d+)([\?&].*)?$|',
'reg' => '|^.*/(page)/(\d+)/?([\?&].*)?$|',
),
array(
'model' => 'Search_Controller',
'method' =>'display',
'reg_0' => '|^.*/\?(keyword)=([^/&]+)(&(page)=(\d+))?([\?&].*)?$|',
),
array(
'model' => 'Comment_Controller',
'method' => 'addComment',
'reg_0' => '|^.*/\?(action)=(addcom)([\?&].*)?$|',
),
array(
'model' => 'Plugin_Controller',
'method' => 'loadPluginShow',
'reg_0' => '|^.*/\?(plugin)=([\w\-]+).*([\?&].*)?$|',
),
array(
'model' => 'Log_Controller',
'method' => 'displayContent',
'reg_0' => '|^.*?/([^/\.=\?]+)(\.html)?(/(comment-page)-(\d+))?/?([\?&].*)?$|',
),
array(
'model' => 'Log_Controller',
'method' => 'display',
'reg_0' => '|^/?([\?&].*)?$|',
),
);
return $routingtable;
}

上面函数就是具体的调用函数了,'model' 是class类,'method' 是显示方式,'reg_'是通过地址的正则,想修改自定义url的修改这里就可以了。通过这种方法就直接调用了控制器,通过控制器看到首页的控制方法
前面的内置变量可以认为是emlog提供给我们的“API”

include View::getView('header');
include View::getView('log_list');

就是调用我们的模版了

插件系统详解

emlog插件系统的调度方法,emlog的实现方法很简单,定义一个全局的钩子,钩子里内置很多函数,需要的时候直接调用就行了。

插件功能实现

在init.php最后
从缓存读取调用的插件,直接include_once这个插件。(不是应该用require_once吗) 然后看定义的钩子函数

/**
 * 该函数在插件中调用,挂载插件函数到预留的钩子上
 *
 * @param string $hook
 * @param string $actionFunc
 * @return boolearn
 */
function addAction($hook, $actionFunc) {
    global $emHooks;
    if (!@in_array($actionFunc, $emHooks[$hook])) {
        $emHooks[$hook][] = $actionFunc;
    }
    return true;
}

/**
 * 执行挂在钩子上的函数,支持多参数 eg:doAction('post_comment', $author, $email, $url, $comment);
 *
 * @param string $hook
 */
function doAction($hook) {
    global $emHooks;
    $args = array_slice(func_get_args(), 1);
    if (isset($emHooks[$hook])) {
        foreach ($emHooks[$hook] as $function) {
            $string = call_user_func_array($function, $args);
        }
    }
}

首先在模板内内置 类似

doAction('post_comment', $author, $email, $url, $comment);

插件里面直接调用 addAction("钩子名称", "调用的函数") 来引用函数即可
emlog内置了许多钩子可以方便插件

插件界面

根据路由器的指引来到 plugin_controller 控制器

<?php
    /**
    * 前台加载插件页面
    *
    * @copyright (c) Emlog All Rights Reserved
    */


    class Plugin_Controller {
    function loadPluginShow($params) {
    $plugin = isset($params[1]) && $params[1] == 'plugin' ? addslashes($params[2]) : '' ;
    if (preg_match("/^[\w\-]+$/", $plugin) && file_exists(EMLOG_ROOT."/content/plugins/{$plugin}/{$plugin}_show.php")) {
    include_once("./content/plugins/{$plugin}/{$plugin}_show.php");
    }
    }
    }

这里就是插件页面前台显示的函数

插件前台显示页面 如果想让插件在前台输出一个页面,可以在插件中定义一个 pluginname_show.php 的文件。
此时插件的前台显示地址为:http:博客地址/?plugin=pluginname
这样就可以在pluginname_show.php文件构建插件的页面显示。

插件后台显示配置页面,如果你想让插件在后台输出一个设置页面,可以在插件中定义一个 pluginname_setting.php 的文件,此时插件的后台配置地址为:http:博客地址/admin/plugin.php?plugin=pluginname

后台的插件显示相关

这些定义都在 plugin_model.php 中,可自行查看
大概就是执行插件的激活与关闭,在emlog后台的插件管理中,点击每个插件后的状态按钮即可激活/关闭插件。
如果插件需要,可以给插件增加 plugin_callback.php 文件,其中包含两个函数: callback_init()为插件激活时调用 callback_rm()为插件关闭时调用

本文作者:MENGX, 转载请注明来自 MENGX

评论0

登录 / 注册 后在评论区发布内容哦