跳至主要内容
版本:5.1

注册子依赖项

理解服务提供程序文件的最后一个方面是注册子依赖项。加载扩展的两步过程(在上一节中部分描述过)实际上如下所示

  1. 扩展类被输入到子 DIC 中,并且扩展类的任何依赖项都已注册(即输入到子 DIC 中)
  2. 从子 DIC 中检索扩展类,创建类的实例。并且从 DIC 中检索扩展类的任何子依赖项,扩展实例在需要时将存储指向这些依赖对象的指针。

最小的 services/provider.php 文件

让我们来看一个 com_example 的最小示例,命名空间为 Mycompany\Component\Example。在这种情况下,我们将假设 com_example 不需要自己的 Extension 类,并且可以使用 Joomla 提供的 MVCComponent 类作为其 Extension。因此,此 services/provider.php 文件对于基本组件来说是最简化的。

use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Extension\MVCComponent;
use Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;

return new class implements ServiceProviderInterface {
public function register(Container $container): void
{
$container->registerServiceProvider(new Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory('\\Mycompany\\Component\\Example'));
$container->registerServiceProvider(new Joomla\CMS\Extension\Service\Provider\MVCFactory('\\Mycompany\\Component\\Example'));
$container->set(
ComponentInterface::class,
function (Container $container) {
$component = new MVCComponent($container->get(ComponentDispatcherFactoryInterface::class));
$component->setMVCFactory($container->get(MVCFactoryInterface::class));
return $component;
}
);
}
};

在这里,我们在 services/provider.php 文件中包含了一些额外的行,这些行与 com_example Extension 实例将需要的类有关

  • 将需要 DispatcherFactory 来创建 Dispatcher 类实例,然后 Joomla 将在此 Dispatcher 对象上调用 dispatch(),作为运行组件的下一步
  • 将需要 MVCFactory 代表组件创建 Controller、View、Model 和 Table 类实例。

这两个 registerServiceProvider 调用导致这两个依赖项加载到子 DIC 中。

然后在实例化组件时,会进行 get 调用以从子 DIC 中获取这两个 Factory 类的实例。

重复类警告!

警告!请注意,这里有一些类名具有相同的 FQN 后半部分

ComponentDispatcherFactory

  • 可以指 libraries/src/Service/Provider/ComponentDispatcherFactory.php 中的 \Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory
  • 也可以指 libraries/src/Dispatcher/ComponentDispatcherFactory.php 中的 \Joomla\CMS\Dispatcher\ComponentDispatcherFactory。

MVCFactory

  • 可以指 libraries/src/Service/Provider/MVCFactory.php 中的 \Joomla\CMS\Extension\Service\Provider\MVCFactory
  • 也可以指 libraries/src/MVC/Factory/MVCFactory.php 中的 \Joomla\CMS\MVC\Factory\MVCFactory。

在每种情况下,后者都是真正的“工厂”类,前者是服务提供程序类,它使真正的工厂类能够通过 DIC 实例化。为了尽量减少混淆,上面使用了服务提供程序类的 FQN。

解析 ComponentDispatcherFactory 依赖项

在第二步中,当 Joomla 调用 get(ComponentInterface::class) 时,将实例化扩展类(在此示例中为 MVCComponent 类)并解析依赖项。

让我们先看看 ComponentDispatcherFactory

$component = new MVCComponent($container->get(ComponentDispatcherFactoryInterface::class));

代码 $container->get(ComponentDispatcherFactoryInterface::class) 运行与 ComponentDispatcherFactoryInterface::class 键关联的子 DIC 中的函数。此函数位于 libraries/src/Extension/Service/Provider/ComponentDispatcherFactory.php 中,返回的对象

new \Joomla\CMS\Dispatcher\ComponentDispatcherFactory($this->namespace, $container->get(MVCFactoryInterface::class))

作为参数传递到 MVCComponent 构造函数中。组件类 Joomla\CMS\Extension\MVCComponent 继承自 Joomla\CMS\Extension\Component,在后者的构造函数中,它存储对传入的 ComponentDispatcherFactory 的引用

public function __construct(ComponentDispatcherFactoryInterface $dispatcherFactory)
{
$this->dispatcherFactory = $dispatcherFactory;
}

它还有一个函数 getDispatcher 用于检索它,并用它来实例化 Dispatcher 类

public function getDispatcher(CMSApplicationInterface $application): DispatcherInterface
{
return $this->dispatcherFactory->createDispatcher($application);
}

让我们再看看实例化 ComponentDispatcherFactory 时的代码行

new \Joomla\CMS\Dispatcher\ComponentDispatcherFactory($this->namespace, $container->get(MVCFactoryInterface::class))

在这里,代码还从子 DIC 中获取 MVCFactory 实例;ComponentDispatcherFactory 反过来依赖于 MVCFactory。这是为什么?如调度程序文档中所述,每当调用 Dispatcher 的 dispatch 函数时,它都会分析 URL 的 task 参数,以决定要实例化哪个 Controller 类。因此,它存储在构造函数中传递的 MVCFactory 对象,以便以后可以使用它来创建 Controller 类

$controller = $this->mvcFactory->createController(...);
$controller->execute($task);

因此,ComponentDispatcherFactory 从 DIC 中获取 MVCFactory,存储对它的引用,然后在实例化它时将其传递给 Dispatcher 类。其代码位于 libraries/src/Dispatcher/ComponentDispatcherFactory.php 中。

解析 MVCFactory 依赖项

这遵循与 ComponentDispatcherFactory 依赖项类似的模式。

依赖项使用以下代码行注册

$container->registerServiceProvider(new Joomla\CMS\Extension\Service\Provider\MVCFactory('\\Mycompany\\Component\\Example'));

这会导致 libraries/src/Extension/Service/Provider/MVCFactory.php 中的 register 函数在该类的实例上运行。如果您查看此文件,您会发现 MVCFactory 有几个依赖项,所有这些依赖项都将通过从父 DIC 中获取条目来解析。

第二步涉及实例化扩展类并解析其依赖项

$component = new MVCComponent($container->get(ComponentDispatcherFactoryInterface::class));
$component->setMVCFactory($container->get(MVCFactoryInterface::class));

在第一行中,扩展类(MVCComponent)被实例化,并将 ComponentDispatchFactory 实例传递到构造函数中(如上所述)。

在第二行中,代码 $container->get(MVCFactoryInterface::class 从子 DIC 中获取 MVCFactory 实例,然后通过将其传递给 setMVCFactory 在本地存储对它的引用。此函数可通过特征 Joomla\CMS\MVC\Factory\MVCFactoryServiceTrait 供 MVCComponent 使用,该特征用于 MVCComponent,其中包含以下代码行

public function setMVCFactory(MVCFactoryInterface $mvcFactory)
{
$this->mvcFactory = $mvcFactory;
}

命名空间参数

您可能已经注意到,命名空间 '\Mycompany\Component\Example' 作为参数传递到几个类的构造函数中。(顺便说一句,这与使用双反斜杠而不是单反斜杠的等效 PHP 字符串完全相同)。

这样做的原因是,这些 Factory 类实例负责代表组件实例化各种类

  • ComponentDispatcherFactory 将实例化 Dispatcher 类
  • MVCFactory 将实例化 Controller、View、Model 和 Table 类

例如,ComponentDispatcherFactory 的 createDispatcher 函数会查看是否存在 com_example 的特定 Dispatcher 类,方法是形成一个类名

$className = '\\' . trim($this->namespace, '\\') . '\\' . $name . '\\Dispatcher\\Dispatcher';

(其中 $name 为“站点”或“管理员”)并查看该类是否存在。如果不存在,则实例化默认的 Joomla\CMS\Dispatcher\ComponentDispatcher 类。

类似地,MVCFactory 使用命名空间来计算 com_example Controller、View、Model 和 Table 类的完全限定类名,以便实例化它们。

这是子 DIC 必要的其中一个原因。在响应 HTTP 请求的过程中,Joomla 将实例化多个扩展,每个扩展都有其依赖项。由于 Joomla 使用通用键(例如,组件的 ComponentInterface::class),因此子 DIC 对于分离每个扩展的条目是必要的。