typecho流程原理和插件機制淺析(第一彈)

雖然新版本0.9在屢次跳票後終於發佈了,在漫長的等待裏始終有一批人不離不棄,其中不乏一些主題和插件開發者,還有一些忠實的粉絲,可是就是這樣一個blog系統仍是因爲缺少相關文檔和主題插件使不少想要擺脫wp的用戶難以跨出最後一步,不少想要學習插件開發的人也由於對機制不瞭解找不到頭緒,今天就簡單介紹一下typecho的執行流程和插件機制,並配合一些簡單的例子來幫助那些找不到頭緒的人,大牛能夠路過了!php

簡要流程分析

Typecho是單入口程序,一切訪問都經過index.php,因此還要從index.php的代碼開始。(以0.9正式版爲例,代碼只貼流程必須部分)html

if (!@include_once 'config.inc.php') {
    file_exists('./install.php') ? header('Location: install.php') : print('Missing Config File');
    exit;
}

/** 初始化組件 */
Typecho_Widget::widget('Widget_Init');

/** 註冊一個初始化插件 */
Typecho_Plugin::factory('index.php')->begin();

/** 開始路由分發 */
Typecho_Router::dispatch();

/** 註冊一個結束插件 */
Typecho_Plugin::factory('index.php')->end();

整個index.php只作了三件事git

  • 加載config.inc.php(config.inc.php設置了相關目錄、包含路徑、加載系統基礎類庫、 程序初始化Typecho_Common::init();、最後設置數據庫鏈接參數,此處自行查看)
  • 執行Typecho_Widget::widget('Widget_Init');
  • 執行Typecho_Router::dispatch();

那麼程序初始化又作了些什麼呢?正則表達式

function __autoLoad($className)
        {
            /**
             * 自動載入函數並不判斷此類的文件是否存在, 咱們認爲當你顯式的調用它時, 你已經確認它存在了
             * 若是真的沒法被加載, 那麼系統將出現一個嚴重錯誤(Fetal Error)
             * 若是你須要判斷一個類可否被加載, 請使用 Typecho_Common::isAvailableClass 方法
             */
            @include_once str_replace('_', '/', $className) . '.php';
        }

設置自動載入,將Typecho_Widget_替換爲/,並加上.php數據庫

Typecho/Widget.php,並include_once 'Typecho/Widget.php';cookie

執行Typecho_Widght類的Widget靜態函數,參數爲Widget_Initsession

public static function widget($alias, $params = NULL, $request = NULL, $enableResponse = true)
    {
        list($className) = explode('@', $alias);

        if (!isset(self::$_widgetPool[$alias])) {
            $fileName = str_replace('_', '/', $className) . '.php';
            require_once $fileName;

            /** 若是類不存在 */
            if (!class_exists($className)) {
                /** Typecho_Exception */
                require_once 'Typecho/Widget/Exception.php';
                throw new Typecho_Widget_Exception($className);
            }

            /** 初始化request */
            if (!empty($request)) {
                $requestObject = new Typecho_Request();
                $requestObject->setParams($request);
            } else {
                $requestObject = Typecho_Request::getInstance();
            }

            /** 初始化response */
            $responseObject = $enableResponse ? Typecho_Response::getInstance()
            : Typecho_Widget_Helper_Empty::getInstance();

            /** 初始化組件 */
            $widget = new $className($requestObject, $responseObject, $params);

            $widget->execute();
            self::$_widgetPool[$alias] = $widget;
        }

        return self::$_widgetPool[$alias];
    }

一樣,將Widget_Init_替換爲/,並加上.php函數

Widget/Init.php,並require_once 'Widget/Init.php';typecho

而且執行Widget_Initexecute函數post

<?php
/**
 * Typecho Blog Platform
 *
 * @copyright  Copyright (c) 2008 Typecho team (http://www.typecho.org)
 * @license    GNU General Public License 2.0
 * @version    $Id$
 */

/**
 * 初始化模塊
 *
 * @package Widget
 */
class Widget_Init extends Typecho_Widget
{
    /**
     * 入口函數,初始化路由器
     *
     * @access public
     * @return void
     */
    public function execute()
    {
        /** 對變量賦值 */
        $options = $this->widget('Widget_Options'); 

        /** cookie初始化 */
        Typecho_Cookie::setPrefix($options->siteUrl);

        /** 初始化charset */
        Typecho_Common::$charset = $options->charset;

        /** 初始化exception */
        Typecho_Common::$exceptionHandle = 'Widget_ExceptionHandle';

        /** 設置路徑 */
        if (defined('__TYPECHO_PATHINFO_ENCODING__')) {
            $pathInfo = $this->request->getPathInfo(__TYPECHO_PATHINFO_ENCODING__, $options->charset);
        } else {
            $pathInfo = $this->request->getPathInfo();
        }

        Typecho_Router::setPathInfo($pathInfo);

        /** 初始化路由器 */
        Typecho_Router::setRoutes($options->routingTable);

        /** 初始化插件 */
        Typecho_Plugin::init($options->plugins);

        /** 初始化回執 */
        $this->response->setCharset($options->charset);
        $this->response->setContentType($options->contentType);

        /** 默認時區 */
        if (function_exists("ini_get") && !ini_get("date.timezone") && function_exists("date_default_timezone_set")) {
            @date_default_timezone_set('UTC');
        }

        /** 初始化時區 */
        Typecho_Date::setTimezoneOffset($options->timezone);

        /** 開始會話, 減少負載只針對後臺打開session支持 */
        if ($this->widget('Widget_User')->hasLogin()) {
            @session_start();
        }

        /** 監聽緩衝區 */
        ob_start();
    }
}

真正的初始化是在Typecho_Init,其中最重要的兩項任務就是

  • 獲取 pathinfo (好比訪問文章http://localhost/archives/1/,則 pathinfo 爲/archives/1/
  • 從數據庫讀取系統路由表(optionroutingTable字段)

舉例系統安裝完成默認路由

[index] => Array
                (
                    [url] => /
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^[/]?$|
                    [format] => /
                    [params] => Array
                        (
                        )

                )

            [archive] => Array
                (
                    [url] => /blog/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/blog[/]?$|
                    [format] => /blog/
                    [params] => Array
                        (
                        )

                )

            [do] => Array
                (
                    [url] => /action/[action:alpha]
                    [widget] => Widget_Do
                    [action] => action
                    [regx] => |^/action/([_0-9a-zA-Z-]+)[/]?$|
                    [format] => /action/%s
                    [params] => Array
                        (
                            [0] => action
                        )

                )

            [post] => Array
                (
                    [url] => /archives/[cid:digital]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/archives/([0-9]+)[/]?$|
                    [format] => /archives/%s/
                    [params] => Array
                        (
                            [0] => cid
                        )

                )

            [attachment] => Array
                (
                    [url] => /attachment/[cid:digital]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/attachment/([0-9]+)[/]?$|
                    [format] => /attachment/%s/
                    [params] => Array
                        (
                            [0] => cid
                        )

                )

            [category] => Array
                (
                    [url] => /category/[slug]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/category/([^/]+)[/]?$|
                    [format] => /category/%s/
                    [params] => Array
                        (
                            [0] => slug
                        )

                )

            [tag] => Array
                (
                    [url] => /tag/[slug]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/tag/([^/]+)[/]?$|
                    [format] => /tag/%s/
                    [params] => Array
                        (
                            [0] => slug
                        )

                )

            [author] => Array
                (
                    [url] => /author/[uid:digital]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/author/([0-9]+)[/]?$|
                    [format] => /author/%s/
                    [params] => Array
                        (
                            [0] => uid
                        )

                )

            [search] => Array
                (
                    [url] => /search/[keywords]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/search/([^/]+)[/]?$|
                    [format] => /search/%s/
                    [params] => Array
                        (
                            [0] => keywords
                        )

                )

            [index_page] => Array
                (
                    [url] => /page/[page:digital]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/page/([0-9]+)[/]?$|
                    [format] => /page/%s/
                    [params] => Array
                        (
                            [0] => page
                        )

                )

            [archive_page] => Array
                (
                    [url] => /blog/page/[page:digital]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/blog/page/([0-9]+)[/]?$|
                    [format] => /blog/page/%s/
                    [params] => Array
                        (
                            [0] => page
                        )

                )

            [category_page] => Array
                (
                    [url] => /category/[slug]/[page:digital]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/category/([^/]+)/([0-9]+)[/]?$|
                    [format] => /category/%s/%s/
                    [params] => Array
                        (
                            [0] => slug
                            [1] => page
                        )

                )

            [tag_page] => Array
                (
                    [url] => /tag/[slug]/[page:digital]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/tag/([^/]+)/([0-9]+)[/]?$|
                    [format] => /tag/%s/%s/
                    [params] => Array
                        (
                            [0] => slug
                            [1] => page
                        )

                )

            [author_page] => Array
                (
                    [url] => /author/[uid:digital]/[page:digital]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/author/([0-9]+)/([0-9]+)[/]?$|
                    [format] => /author/%s/%s/
                    [params] => Array
                        (
                            [0] => uid
                            [1] => page
                        )

                )

            [search_page] => Array
                (
                    [url] => /search/[keywords]/[page:digital]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/search/([^/]+)/([0-9]+)[/]?$|
                    [format] => /search/%s/%s/
                    [params] => Array
                        (
                            [0] => keywords
                            [1] => page
                        )

                )

            [archive_year] => Array
                (
                    [url] => /[year:digital:4]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/([0-9]{4})[/]?$|
                    [format] => /%s/
                    [params] => Array
                        (
                            [0] => year
                        )

                )

            [archive_month] => Array
                (
                    [url] => /[year:digital:4]/[month:digital:2]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/([0-9]{4})/([0-9]{2})[/]?$|
                    [format] => /%s/%s/
                    [params] => Array
                        (
                            [0] => year
                            [1] => month
                        )

                )

            [archive_day] => Array
                (
                    [url] => /[year:digital:4]/[month:digital:2]/[day:digital:2]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/([0-9]{4})/([0-9]{2})/([0-9]{2})[/]?$|
                    [format] => /%s/%s/%s/
                    [params] => Array
                        (
                            [0] => year
                            [1] => month
                            [2] => day
                        )

                )

            [archive_year_page] => Array
                (
                    [url] => /[year:digital:4]/page/[page:digital]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/([0-9]{4})/page/([0-9]+)[/]?$|
                    [format] => /%s/page/%s/
                    [params] => Array
                        (
                            [0] => year
                            [1] => page
                        )

                )

            [archive_month_page] => Array
                (
                    [url] => /[year:digital:4]/[month:digital:2]/page/[page:digital]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/([0-9]{4})/([0-9]{2})/page/([0-9]+)[/]?$|
                    [format] => /%s/%s/page/%s/
                    [params] => Array
                        (
                            [0] => year
                            [1] => month
                            [2] => page
                        )

                )

            [archive_day_page] => Array
                (
                    [url] => /[year:digital:4]/[month:digital:2]/[day:digital:2]/page/[page:digital]/
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/([0-9]{4})/([0-9]{2})/([0-9]{2})/page/([0-9]+)[/]?$|
                    [format] => /%s/%s/%s/page/%s/
                    [params] => Array
                        (
                            [0] => year
                            [1] => month
                            [2] => day
                            [3] => page
                        )

                )

            [comment_page] => Array
                (
                    [url] => [permalink:string]/comment-page-[commentPage:digital]
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^(.+)/comment\-page\-([0-9]+)[/]?$|
                    [format] => %s/comment-page-%s
                    [params] => Array
                        (
                            [0] => permalink
                            [1] => commentPage
                        )

                )

            [feed] => Array
                (
                    [url] => /feed[feed:string:0]
                    [widget] => Widget_Archive
                    [action] => feed
                    [regx] => |^/feed(.*)[/]?$|
                    [format] => /feed%s
                    [params] => Array
                        (
                            [0] => feed
                        )

                )

            [feedback] => Array
                (
                    [url] => [permalink:string]/[type:alpha]
                    [widget] => Widget_Feedback
                    [action] => action
                    [regx] => |^(.+)/([_0-9a-zA-Z-]+)[/]?$|
                    [format] => %s/%s
                    [params] => Array
                        (
                            [0] => permalink
                            [1] => type
                        )

                )

            [page] => Array
                (
                    [url] => /[slug].html
                    [widget] => Widget_Archive
                    [action] => render
                    [regx] => |^/([^/]+)\.html[/]?$|
                    [format] => /%s.html
                    [params] => Array
                        (
                            [0] => slug
                        )

這個在代碼裏標爲routingTable的東西其實就是程序對不一樣pathinfo的解析規則。

剩下的活就交給index.php裏的Typecho_Router::dispatch();

這句的註釋是/** 開始路由分發 */

接着上邊 pathinfo =/archives/1/的例子

dispatch函數將路由表裏的[regx]裏的正則表達式一一進行正則匹配,發現與[post]的匹配,並1提取做爲參數cid的值,並將參數cid=1做爲參數提交給Widget_Archive類的render函數,Widget_Archive類根據類型postcid=1進行數據查詢和模板渲染髮送給客戶端,至此一個簡單的執行流程就完成了!

細心的讀者可能會發現,爲何系統路由裏的怎麼都是

[widget] => Widget_Archive
[action] => render

這是由於typecho在類Widget_Archive裏還根據不一樣的訪問類型調用了不一樣的查詢函數,這個之後再說!

相關文章
相關標籤/搜索