雖然新版本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
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_Init
session
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_Init
的execute
函數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,其中最重要的兩項任務就是
http://localhost/archives/1/
,則 pathinfo 爲/archives/1/
)option
表routingTable
字段)舉例系統安裝完成默認路由
[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
類根據類型post
、cid=1
進行數據查詢和模板渲染髮送給客戶端,至此一個簡單的執行流程就完成了!
細心的讀者可能會發現,爲何系統路由裏的怎麼都是
[widget] => Widget_Archive [action] => render
這是由於typecho在類Widget_Archive
裏還根據不一樣的訪問類型調用了不一樣的查詢函數,這個之後再說!