Расширения и дочерние контейнеры в Joomla 5
Перевод с английского: Joomla! Programmers Documentation for Joomla 5.2
Всякий раз, когда Joomla загружает расширение, она создает дочерний Dependency Injection Container (далее контейнер), исключительно для использования этого расширения. Это показано на схеме ниже.

Дочерний контейнер содержит указатель на родительский контейнер и функционирует аналогично основному контейнеру, но с некоторыми отличиями:
При каждом вызове метода
set()
для этого дочернего контенера пара ключ-значение добавляется в дочерний контейнер;При каждом вызове метода
get()
для этого дочернего контейнера ресурс извлекается из дочернего контейнера, но если он там не найден, то поиск выполняется также в родительском контейнере.
Начиная с версии Joomla 4, разработчики Joomla рекомендуют создателям расширений использовать внедрение зависимостей (dependency injection) для своих расширений, определяя файл services/provider.php
. Загрузка расширения теперь выполняется в два этапа, которые обрабатываются внутри файла services/provider.php
:
Класс расширения регистрируется в дочернем контейнере;
Класс расширения извлекается из дочернего контейнера, создавая экземпляр этого класса.
Давайте рассмотрим минимальный пример этого для компонента 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 выполняет require
этого PHP-файла, возвращается экземпляр класса.
$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
, который добавит запись в дочерний контейнер с
key
-ComponentInterface::class
– это сокращённый способ в PHP указать строку'Joomla\CMS\Extension\ComponentInterface'
;value
- функция, которая возвращает новый экземпляр класса ExampleComponent (основного класса расширения компонентаcom_example
).
$extension = $container->get($type);
Функция выше будет запущена и вернет новый экземпляр ExampleComponent
.
Вы можете самостоятельно изучить код Joomla в файле libraries/src/Extension/ExtensionManagerTrait.php
и убедиться, что описанный выше шаблон также применяется к модулям и плагинам.
Обратите также внимание на следующую строку в этом файле:
if ($extension instanceof BootableExtensionInterface)
{
$extension->boot($container);
}
Таким образом, если класс расширения реализует интерфейс BootableExtensionInterface
, Joomla немедленно вызовет метод boot()
экземпляра расширения, как описано в документации по расширениям.