ch12Joomla的結構——Joomla外掛開發
Joomla的啟動過程
當使用Joomla來生成一個頁面時,有許多不同的步驟來到達最終的結果:傳送HTML頁面到瀏覽器。這些步驟被稱為啟動bootstrap
。注意不是那個前端框架Twitter Bootstrap。
啟動Joomla——Initialise
Joomla啟動CMS的第一步是在defines.php
中定義需要包含的PHP檔案。之後,Joomla就可以載入各種類庫了。configuration.php
同時被載入。這樣Joomla才知道如何連線資料庫。當所需的檔案都被載入後,Joomla的應用類JApplication
被例項化,並呼叫execute()
方法。一旦application
JFactory::getUser()
方法可以使用了。此時,外掛被載入。因為資料庫連線上了,所有的啟用外掛從
#__extensions
資料表中被讀取。因為使用者會話開始了,不符合ACL規則的外掛被跳過。通過一個查詢命令,所有的屬於同一型別的外掛可以被同時載入。外掛記錄在Joomla應用中。下一步,系統外掛通過
JPluginHelper::importPlugin(′system′)
載入。此時,外掛記錄列表用來定位和包含每個系統外掛檔案。外掛被例項化,並且儲存在** event dispatcher**事件分發器的列表中。之後,
onAfterInitalise
onAfterInitialise()
方法被呼叫。
請求路由化
現在Joomla核心已經初始化並且運行了。Joomla需要解析URL,處理請求。JRouter
類被呼叫來解析當前的URL($_SERVER[′REQUEST_URI′]
。現在的URL通常是SEF URL。需要將其與Menu-item的別名進行匹配。大部分時候Menu-item指向一個元件。當請求被分發後,這個元件將會被呼叫。有時候請求的地址和Menu-item的別名一樣,JRouter
類就會直接呼叫這個選單的URL引數。
比如,Menu-item的別名是blog,就是完全匹配*/blog這個URL。但是,如果URL是
JRouter
類會呼叫元件的router.php繼續解析。解析出的會被插入到JApplication->input物件中
Itemid = 3
option = com_content
view = article
id = 23
一旦這些工作完成,SEF URL就有效的轉變成了系統請求,然後事件onAfterRoute就會觸發。
事件onAfterRoute之後,應用的*authorise() *會被呼叫,用來檢測當前使用者是否有許可權進入這個頁面。
將請求分發到元件
下一步是分發請求到元件。input變數包含了這個頁面請求的型別:RSS,HTML還是其他。根據這個型別,JDocument的一個特定子類被例項化,比如JDocumentHtml用來例項化HTML頁面。一些基本的資訊,比如頁面標題、描述、立即插入到文件中。但是,此時最重要的任務是呼叫元件繼續解析。換句話說就是,將請求分發到元件。
通過呼叫JComponentHelper::renderComponent()
來生成元件輸出。元件的入口檔案被呼叫。以com_content為例,入口檔案時components/com_content/content.php
。如何一個元件是com_example,那麼它的入口檔案是/com_example/example.php
。
下一步如何做完全取決於元件自身:可以通過入口檔案直接輸出內容,也可以使用MVC結構,用控制器呼叫檢視來輸出佈局檔案,也可以直接通過控制器重定向到其他的URL。
當元件生成輸出時,不是直接傳送給瀏覽器。而是,將輸出存放在JDocument
物件的快取中。之後,onAfterDispatch觸發。Joomla啟動的下一步是渲染整個頁面。元件已經被渲染了。下一步需要渲染的是其他的擴充套件:模板和模組。
渲染頁面
元件的輸出已經快取到JDocument快取中了,但是模板和模組還沒有。下一步就是呼叫模板,看看那個模組位置定義了,那些模組需要被例項化。
模板渲染之前會觸發onBeforeRender事件。所有的預留位置會被實際的內容替換:
<jdoc:include type=″head″ />
<jdoc:include type=″modules″ name=″position-0″ />
<jdoc:include type=″message″ />
元件位置處替換成元件的輸出:
<jdoc:include type=″component />
渲染過程完成後,onAfterRender事件觸發。此時,渲染輸出包含了所有的內容。
傳送內容到瀏覽器
所有的輸出會被髮送到瀏覽器。之前,根據**Global Confguration **的引數,決定壓不壓縮頁面。如果壓縮,快取的HTML會被壓縮,然後onAfterCompress事件觸發。之後所有的快取輸出,傳送到瀏覽器。傳送完畢後,onAfterRespond事件觸發,此時可以讓系統外掛做一些清理工作。
啟動過程的不同
啟動過程會根據不同的情況有所不同。比如,如果當前頁面是RSS,模板和模組的渲染過程會跳過。如果URL中帶了引數*?tmpl=component*,元件會在模板中渲染,但是不包括任何模組。
同樣,當元件條狀到其他頁面時,餘下的所有過程會被跳過。onAfterRender,
onAfterCompress and onAfterRespond事件都不會觸發。
如果一個外掛或者元件決定停止應用,比如exit(),die() or JApplication::close()
,應用會停止,之後的所有Joomla程式碼都不會執行。
元件內部是如何工作的
JPlugin和JEvent的父類
每個外掛的父類都是JPlugin。這個類提供了一些有用的功能:將JSON格式的引數轉化成JRegistry物件,放在$this->params
變數中,$this->app
和$this->db
物件被定義,定義了一個loadLanguage()
方法,可以直接呼叫,或者使用$autoloadLanguage
標籤。
JPlugin的父類都是JEvent, JEvent的父類都是JObject。JEvent使用了觀察值模式,把自己變成被觀察者。
引用外掛
當Joomla觸發事件是,程式碼可能如下:
JPluginHelper::importPlugin(′somegroup′);
$dispatcher = JEventDispatcher::getInstance();
$dispatcher->trigger(′onSomeEvent′);
一行一行的看,JPluginHelper載入某個型別的所有啟用的外掛。例項化每個外掛類,因此會自動呼叫每個外掛的建構函式。將外掛載入記憶體,可以快速的呼叫外掛,而不是用的時候再去請求資料庫。保證了效能。
將事件繫結到分發器上
看一下外掛的建構函式,總是要先呼叫父類的建構函式,否則就啥都做不了。
class PlgSystemExample extends JPlugin
{
public function __construct(&$subject, $confg = array())
{
parent::__construct($subject, $confg);
}
}
建構函式有兩個引數:confg代表設定。
外掛的建構函式一般不操作*$subject引數,其父類JEvent建構函式將外掛繫結到dispatcher*分發器上,這樣後面外掛就和分發器聯絡上了。
這些檔案的位置:
JPlugin:libraries/src/Plugin/CMSPlugin.php
;
JEvent:libraries/joomla/Event/event.php
;
JEventDispatcher:libraries/joomla/event/dispatcher.php
;
JPluginHelper:libraries/src/Plugin/PluginHelper.php
;
abstract class JEvent extends JObject
{
public function __construct(&$subject)
{
$subject->attach($this);
$this->_subject = &$subject;
}
}
當$subject->attach($this)
方法呼叫時,外掛被新增到*$Subject*(是個dispatcher例項)的一個內部陣列(名字是*$_observers*)。這樣,分發器就知道哪些外掛是可用的了。在attach()
方法內部會做許多檢查,來確保外掛能夠這樣工作而不是導致Joomla死掉。
另外,每個外掛的方法會對映到dispather的一個內部陣列*$_methods*。一個系統外掛可能實現了一大堆事件方法,如果每次事件觸發時,dispather都需要檢查事件方法存不存在,
在外掛中使用dispatcher
$Subject變數的一個用途是用來向觸發事件的觸發器(dispatcher)傳送資訊,告知外掛執行用出現了錯誤。
public function onSomeEvent()
{
if($somethingGoesWrong)
{
$this->_subject->setError(′SOMETHING_WENT_WRONG′);
return false;
}
return true;
}
觸發器的錯誤並不能像會話錯誤那樣自動列印。呼叫的程式碼決定是否顯示它。在元件裡,可以用下面的程式碼來檢視外掛是否出現錯誤:
$results = $dispatcher->trigger(′onSomeEvent′, array(&$item));
if (in_array(false, $results))
{
throw new Exception($dispatcher->getError(), 500);
}
JEventDispatcher繼承自JObject,這個基類可以使用getters和setters來獲取屬性值,而不需要真正的定義這個方法。這就意味著,在外掛中,可以為*$Subject*變數插入任何值。
$this->_subject->setAnything(′foobar′);
在元件中,這個值可以通過dispatcher變數獲取,並由會話訊息使用。
$foobar = $dispatcher->getAnything();
JFactory::getApplication()->enqueueMessage($foobar);