1. 程式人生 > >ch12Joomla的結構——Joomla外掛開發

ch12Joomla的結構——Joomla外掛開發

Joomla的啟動過程

當使用Joomla來生成一個頁面時,有許多不同的步驟來到達最終的結果:傳送HTML頁面到瀏覽器。這些步驟被稱為啟動bootstrap。注意不是那個前端框架Twitter Bootstrap

啟動Joomla——Initialise

Joomla啟動CMS的第一步是在defines.php中定義需要包含的PHP檔案。之後,Joomla就可以載入各種類庫了。configuration.php同時被載入。這樣Joomla才知道如何連線資料庫。當所需的檔案都被載入後,Joomla的應用類JApplication被例項化,並呼叫execute()方法。一旦application

例項化後,資料庫開始連線,session開始,同時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是

/blog/23-example-article*,URL餘下的部分23-example-article就需要繼續被解析。此時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程式碼都不會執行。

元件內部是如何工作的

JPluginJEvent的父類

每個外掛的父類都是JPlugin。這個類提供了一些有用的功能:將JSON格式的引數轉化成JRegistry物件,放在$this->params變數中,$this->app$this->db物件被定義,定義了一個loadLanguage()方法,可以直接呼叫,或者使用$autoloadLanguage標籤。

JPlugin的父類都是JEvent, JEvent的父類都是JObjectJEvent使用了觀察值模式,把自己變成被觀察者。

引用外掛

當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);
}
}

建構函式有兩個引數:subjectJEventDispatchersubject*指向一個*JEventDispatcher*物件,*’confg代表設定。

外掛的建構函式一般不操作*$subject引數,其父類JEvent建構函式將外掛繫結到dispatcher*分發器上,這樣後面外掛就和分發器聯絡上了。

這些檔案的位置:
JPluginlibraries/src/Plugin/CMSPlugin.php;
JEventlibraries/joomla/Event/event.php;
JEventDispatcherlibraries/joomla/event/dispatcher.php;
JPluginHelperlibraries/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,這個基類可以使用getterssetters來獲取屬性值,而不需要真正的定義這個方法。這就意味著,在外掛中,可以為*$Subject*變數插入任何值。

$this->_subject->setAnything(′foobar′);

在元件中,這個值可以通過dispatcher變數獲取,並由會話訊息使用。

$foobar = $dispatcher->getAnything();
JFactory::getApplication()->enqueueMessage($foobar);