04 - Расширения и дочерние Контейнеры
Всякий раз, когда Joomla загружает расширение, она создает дочерний DIC исключительно для использования этого расширения. Это показано схематически ниже.
Дочерний DIC имеет родительский указатель на главный DIC и действует аналогично главному DIC, но не совсем:
- Всякий раз, когда
set()
вызывается на этом дочернем DIC, пара ключ - значение вставляется в дочерний контейнер. - Всякий раз, когда
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 выполняет 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
будет вызвана функция, которая поместит в дочернюю DIC запись с
- key = ComponentInterface::class – это просто сокращенный способ PHP указать строку 'Joomla \ CMS \ Extension \ComponentInterface'
- value = функция, возвращая новый экземпляр 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
функция экземпляра расширения, как описано в документации по расширению .