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

构造函数有两个参数: s u b j e c t ∗ 指 向 一 个 ∗ J E v e n t D i s p a t c h e r ∗ 对 象 , ∗ ’ subject*指向一个*JEventDispatcher*对象,*’ subjectJEventDispatcherconfg代表设置。

插件的构造函数一般不操作*$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);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值