跳到主要内容
版本:5.1

扩展和子容器

每当 Joomla 启动一个扩展时,它都会创建一个子 DIC,专门用于该扩展。下面是示意图。

Child DIC

子 DIC 具有指向主 DIC 的 parent 指针,并且与主 DIC 的行为类似,但并非完全相同。

  • 每当在该子 DIC 上调用 set() 时,键值对就会插入到子容器中。
  • 每当在它上面调用 get() 时,就会从子容器中获取资源,但如果在子容器中找不到该资源,则还会搜索父容器。

从 Joomla 4 版本开始,Joomla 开发人员要求扩展开发人员通过定义一个 services/provider.php 文件,在其扩展中使用依赖注入。然后加载扩展是一个两步过程,在 services/provider.php 文件中处理:

  1. 扩展类将被输入到子 DIC 中
  2. 扩展类从子 DIC 中检索,创建该类的实例

让我们看一下 com_example 的最小示例,命名空间为 Mycompany\Component\Example。

use Joomla\CMS\Extension\ComponentInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Mycompany\Component\Example\Administrator\Extension\ExampleComponent;

return new class implements ServiceProviderInterface {
public function register(Container $container): void
{
$container->set(
ComponentInterface::class,
function (Container $container) {
$component = new ExampleComponent();
return $component;
}
);
}
};

我们可以看到,当 Joomla 对此 php 文件进行 require 时,它将返回一个类实例

$provider = require $path;  // $path points to the relevant services/provider.php file

变量 $provider 然后指向一个对象,该对象是该匿名类的实例。此外,该类实现了 Joomla\DI\ServiceProviderInterface,这基本上意味着它具有一个名为 register 的方法,具有上述签名。

当 Joomla 接下来执行以下操作时

if ($provider instanceof ServiceProviderInterface) {
$provider->register($container);

将调用 register 函数,该函数将把一个条目放入子 DIC 中,其中

  • key = ComponentInterface::class – 这只是 PHP 指定字符串“Joomla\CMS\Extension\ComponentInterface”的简写方式
  • value = 一个返回 ExampleComponent 的新实例的函数,ExampleComponent 是 com_example扩展类

然后,当 Joomla 执行以下代码时

$extension = $container->get($type);

上面的函数将运行并返回一个新的 ExampleComponent 实例。

您可以自己查看 Joomla 代码,位于 libraries/src/Extension/ExtensionManagerTrait.php 中,并确认上述模式也适用于模块和插件。

还要注意该文件中以下代码行

if ($extension instanceof BootableExtensionInterface) {
$extension->boot($container);
}

因此,如果扩展类实现了 BootableExtensionInterface,则 Joomla 将立即调用扩展实例的 boot 函数,如 扩展文档 中所述。