Дополнительно/Специальные возможности/Контейнер и сервисы/Нестандартное использование контейнера

Нестандартное использование контейнера


#Инициализация сервиса в сервисе

Несмотря на то, что создание объекта в контейнере через new и c пустым конструктором является хорошей практикой, в конце концов вы можете вынести в отдельный метод специального класса создание всех необходимых зависимостей и зарегистрировать его выполнение в контейнере, несмотря на это есть способы разрешения зависимостей не прибегая к созданию отдельного класса-обёртки.

В случае возникновения необходимости переиспользовать сервис из контейнера для инициализации другого сервиса в контейнере обратимся к возможностям, которые даёт внедрение зависимостей. В классе App\Bootstrap\ContainerFactory эти методы доступны, как и в отдельном специальном классе для создания контейнера.

Например, необходимо проинициализировать конструктор сервиса в контейнере. Для этого в теле оператора match класса App\Bootstrap\ContainerFactory нужно добавить примерно такое соответствие:

// File /app/Bootstrap/ContainerFactory.php
use Hleb\Static\DI;
// ... //

ExampleService::class => new ExampleService(),

// variant 1
DemoService::class => new DemoService(DI::object(ExampleService::class)),

// variant 2
DemoService::class => DI::object(DemoService::class),

// ... //

Теперь в конструктор класса DemoService будет попадать текущий сервис ExampleService как он определен в контейнере. Все зависимости, не указанные явно, в используемом примере будут разрешены автоматически (вариант 2).

Важно следить за тем, чтобы зависимости не зациклились, это может быть в случае если при инициализации объекта в контейнере он повторно обращается в контейнер для инициализации этого же объекта.

Более сложный пример:

// File /app/Bootstrap/ContainerFactory.php
use Hleb\Static\DI;

// ... //

SenderServiceInterface::class => new MailTransportService(),

SiestaService::class => DI::method(DI::object(
    
SiestaService::class,
    [
        
'start' => (new DateTimeImmutable())->setTime(140),
        
'end' => (new DateTimeImmutable())->setTime(160),
    ]
), 
'setSender', ['transport' => DI::object(SenderServiceInterface::class)]),

// ... //

Подобным образом в контейнер фреймворка, несмотря на его кажущуюся простоту, можно добавлять различные взаимозависимые сервисы.


#Добавление сервисов в пользовательском коде

По умолчанию во фреймворке не допускается добавление сервисов после инициализации контейнера. Но при переопределении метода getSingleton() на публичный в классе ContainerFactory появляется возможность добавлять в контейнер объекты в пользовательском коде через этот статический метод. Образец модификации класса:

// File /app/Bootstrap/ContainerFactory.php

use Hleb\Constructor\Containers\BaseContainerFactory;

final class 
ContainerFactory extends BaseContainerFactory
{
    public static function 
getSingleton(string $id): mixed
    
{
        
// ... //

        
if (self::$singletons[$id] instanceof \Closure) {
            
self::$singletons[$id] = self::$singletons[$id]();
        }
        return 
self::$singletons[$id];
    }

    #[\Override]
    public static function 
setSingleton(string $idobject|null $value): void
    
{
        
parent::setSingleton($id$value);
    }
}

Из примера видно, что также добавлена поддержка отложенной инициализации через callable тип и его обработчик.

Страница создана: @fomiash
К началу страницы