扩展和子容器
每当 Joomla 启动一个扩展时,它都会创建一个子 DIC,专门用于该扩展。下面是示意图。
子 DIC 具有指向主 DIC 的 parent
指针,并且与主 DIC 的行为类似,但并非完全相同。
- 每当在该子 DIC 上调用
set()
时,键值对就会插入到子容器中。 - 每当在它上面调用
get()
时,就会从子容器中获取资源,但如果在子容器中找不到该资源,则还会搜索父容器。
从 Joomla 4 版本开始,Joomla 开发人员要求扩展开发人员通过定义一个 services/provider.php 文件,在其扩展中使用依赖注入。然后加载扩展是一个两步过程,在 services/provider.php 文件中处理:
- 扩展类将被输入到子 DIC 中
- 扩展类从子 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
函数,如 扩展文档 中所述。