Joomla 4 和 5 更新
主要更新
在 Joomla 4 中,开始对插件区域进行一些重大更改
- 插件通过
services/provider.php
文件通过依赖注入容器实例化。以前是通过插件的主要 PHP 文件运行插件。 - 插件订阅了想要接收通知的事件。以前,你编写了一个名称与事件名称匹配的方法,而 Joomla 使用 PHP 反射类来确定何时调用你的方法。通过订阅机制,Joomla 检查你的插件是否实现了
Joomla\Event\SubscriberInterface
,如果是,则调用getSubscribedEvents
,插件将返回想要处理的事件类型以及 Joomla 应该调用的方法
public static function getSubscribedEvents(): array
{
return [
'onContentPrepare' => 'myContentPrepareMethod',
'onContentAfterTitle' => 'myContentAfterTitleMethod',
];
}
- 事件以 Joomla Event 对象传递给插件。以前,与事件关联的每个方法都有传递的参数,这些参数特定于该事件类型。例如,当触发
onContentPrepare
事件时调用的方法为
public function onContentPrepare($context, &$row, $params, $page = 0)
在 Joomla 3 中,“事件”基本上包含
- 带事件名称的字符串,例如“onContentPrepare”,以及
- 传递给
onContentPrepare
方法的关联参数。
Joomla 团队的目标是用事件类替换这些类,每种类型的事件都有自己的具体事件类。例如,对于 onContentPrepare
事件,将有一个 ContentPrepareEvent
类,其属性为 $context
、$row
、$params
和 $page
。但是,替换所有当前代码的过程已分布在多个 Joomla 版本中,其中一些在 Joomla 4 中完成,一些在 Joomla 5 中完成。
为使插件在可以实现完整具体事件类之前使用事件类,采用了临时解决方案,即提供一个 GenericEvent
类,这意味着可以开发使用其方法中的事件参数的插件,而不是使用一串参数。
了解获取插件方法参数的方式,以及提供返回值的方式很重要,这取决于使用 GenericEvent 还是具体类。
这正是我们现在将详细了解的。
Joomla 3 / 4 / 5 比较
我们以 onContentPrepare
为例,看看它如何在这些版本中发生了变化。
Joomla 3
在 Joomla 3 中,事件在 com_content
中使用以下方式触发
$dispatcher->trigger('onContentPrepare', array ('com_content.article', &$item, &$item->params, $offset));
这会调用插件方法
public function onContentPrepare($context, &$row, $params, $page = 0)
Joomla 通过使用插件类的反射类找到此方法。
onContentPrepare
不会采用任何返回值,但其他插件(例如 onContentAfterTitle
)会。要指定返回值,只需执行以下操作
return $value;
Joomla 团队通过版本 4 和 5 为插件提供了向后兼容性,因此此机制仍可工作。但是它可能在 Joomla 6 中消失,因此你不应以这种方式编写任何新插件。
Joomla 4
在 Joomla 4 中,事件使用以下方式触发
$this->dispatchEvent(new Event('onContentPrepare', ['com_content.article', &$item, &$item->params, $offset]));
这将创建一个 GenericEvent
,它具有上述参数的参数字段,可以通过 getArguments
方法(使用 PHP 数组解构)获取该参数字段
[$context, $article, $params, $page] = array_values($event->getArguments());
此处实际上不需要 array_values
,但我们很快就会看到它的原因。
对于 GenericEvent
,result
是保存在 GenericEvent
类中的数组,并且您必须在此数组中添加任何从插件返回的值。因此,为从插件方法使用 GenericEvent
返回值,您将使用
$result = $event->getArgument('result') ?: []; // get the result argument from GenericEvent
$result[] = $value; // add your return value into the array
$event->setArgument('result', $result); // write back the updated result into the GenericEvent instance
Joomla 5
在 Joomla 5 中,以下方式触发事件
$dispatcher->dispatch('onContentPrepare', new Content\ContentPrepareEvent('onContentPrepare', $contentEventArguments));
此处使用具体事件类 ContentPrepareEvent
(来自 libraries/src/Event/Content/ContentPrepareEvent.php)。此类事件可能具有特定 getter 方法(例如 getContext
),但您仍可以使用以下方式获取参数
[$context, $article, $params, $page] = array_values($event->getArguments());
此处需要 array_values
,因此如果您使用它来获取参数,它将适用于 GenericEvent
和具体 Event 类。
为返回一个值,您的插件应使用 $event->addResult($value)
,但您可以通过以下方式检查此方法是否存在
use Joomla\CMS\Event\Result\ResultAwareInterface;
...
if ($event instanceof ResultAwareInterface) {
$event->addResult($value);
return;
} else {
// use GenericEvent approach
}
如果您正在开发处理 GenericEvent
的插件,则至关重要的是使用上述机制来避免在触发代码移动到使用具体 Event 类时插件发生故障。
摘要 - 访问事件参数
具体 Event 类
如果您在 libraries/src/Event 中具有一个特定于插件事件组的 Joomla 事件类(称为"具体"事件类),则可以使用此方法。(仅对 Joomla 4 提供限定集合)。
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
use Joomla\CMS\Event\Content\ContentPrepareEvent;
class MyPlugin extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
'onContentPrepare' => 'myOnContentPrepare',
];
}
public function myOnContentPrepare(ContentPrepareEvent $event)
{
$context = $event->getContext();
$item = $event->getItem();
$params = $event->getParams();
$page = $event->getPage();
// ...
}
在此 getter 调用中,您必须为参数使用正确的名称,并且在 Joomla 手册文档中始终指定了正确的名称。
您还可以在事件类的 API 文档中找到 getter 方法,例如 Event/Content/ContentPrepareEvent 方法。
\为使用事件类,您的插件类必须实现 Joomla\Event\SubscriberInterface 并提供 getSubscribedEvents 函数。您的插件监听器函数必须仅具有单个 $event 参数。
泛型 Event 类
如果您希望在 Joomla 4.0 之后使用事件类,但目标 Joomla 版本没有具体的事件类,则使用此方法。
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
use Joomla\Event\Event;
class MyPlugin extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
'onContentPrepare' => 'myOnContentPrepare',
];
}
public function myOnContentPrepare(Event $event)
{
[$context, $item, $params, $page] = array_values($event->getArguments());
...
}
此方法也适用于具体事件类。
它取决于事件类中参数的顺序,文档中始终给出正确的顺序。
尽管 Joomla 团队做出承诺会保留传统方法的参数顺序以及事件参数中的相同顺序,但这在 Joomla 6 中将会取消。此实现通过变量 $legacyArgumentsOrder
完成,如 Joomla\CMS\Event\Content\ContentPrepareEvent(见 libraries/src/Event/Content/ContentPrepareEvent.php)中的情况下
class ContentPrepareEvent extends ContentEvent
{
protected $legacyArgumentsOrder = ['context', 'subject', 'params', 'page'];
传统的传统方法
如果你在 Joomla 4.0 之前开发过插件,你将使用此方法。在这种情况下,你最好继续使用此方法,直至针对插件侦听的所有事件提供具体事件类。
你不能为了开发新插件而使用此方法,因为你必须重新调整此方法。
如需使用传统方法,你需要指定一个与事件名称同名的 public 函数,然后 Joomla 会利用 PHP 反射查找你的方法。你可以在函数声明中指定事件参数,例如
use Joomla\CMS\Plugin\CMSPlugin;
class MyPlugin extends CMSPlugin
{
public function onContentPrepare($context, $item, $params, $page)
{
if ($context == "com_content.article") ...
}
你无需指定函数参数的名称,仅需按其参数序列进行排列。Joomla 手册中的文档始终指定这些参数的正确顺序。
摘要 - 返回值
具体事件类
如需使用具体事件类时返回值,请使用 $event->addResult()
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Event\Content\AfterTitleEvent;
class MyPlugin extends CMSPlugin
public function onContentAfterTitle(AfterTitleEvent $event)
{
...
$event->addResult($value);
}
通用事件类
如需使用通用事件类时返回值,请使用以下方式
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\Event;
class MyPlugin extends CMSPlugin
public function onContentAfterTitle(Event $event)
{
...
$result = $event->getArgument('result') ?: []; // get the result argument from GenericEvent
$result[] = $value; // add your return value into the array
$event->setArgument('result', $result);
}
此方法也能用于具体事件类。
传统的传统方法
如需使用传统方法时返回值,请使用 PHP return
public function onContentAfterTitle($context, $item, $params, $page)
{
...
return $value;
}
其他新功能
优先级
如需使用非默认插件优先级,请在对 getSubscribedEvents
的响应中指定此项
public static function getSubscribedEvents(): array
{
return [
'onContentPrepare' => ['myContentPrepareMethod', \Joomla\Event\Priority::HIGH],
'onContentAfterTitle' => ['myContentAfterTitleMethod', \Joomla\Event\Priority::MIN],
];
}
请参阅 libraries/vendor/joomla/event/src/Priority.php 中的其他可用值。不过请谨慎使用,因为它会覆盖任何管理员可能通过排序插件而想要设置的优先级。
停止传播
要停止将事件传播到其他插件,可以调用
$event->stopPropagation();