Additionally/Accessibility/Container and services/Non-standard container use

Non-Standard Use of the Container


#Initializing a service in a service

Although creating an object in the container using new with an empty constructor is a good practice, eventually, you can outsource the creation of all necessary dependencies to a separate method in a special class and register its execution in the container. However, there are ways to resolve dependencies without resorting to creating a separate wrapper class.

If it becomes necessary to reuse a service from the container to initialize another service in the container, we turn to the capabilities provided by dependency injection. In the class App\Bootstrap\ContainerFactory, these methods are available, as they are in a special class for creating the container.

For example, it is necessary to initialize the constructor of a service in the container. To do this, in the body of the match operator of the App\Bootstrap\ContainerFactory class, you need to add approximately the following match:

// 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),

// ... //

Now in the constructor of the DemoService class, the current ExampleService will be injected as defined in the container. All dependencies not explicitly specified in the used example will be resolved automatically (variant 2).

It is important to ensure that dependencies do not form a cyclic dependency, which can occur if the object in the container makes another request to the container for the initialization of itself.

A more complex example:

// 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)]),

// ... //

In this way, in the framework's container, despite its seeming simplicity, you can add various interdependent services.


#Adding Services in User Code

By default, the framework does not allow adding services after the container has been initialized. However, by overriding the getSingleton() method to be public in the ContainerFactory class, you gain the ability to add objects to the container in your user code through this static method. Here’s an example of modifying the class:

// File /app/Bootstrap/ContainerFactory.php

use Hleb\Constructor\Containers\BaseContainerFactory;

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

        
if (is_callable(self::$singletons[$id])) {
            
self::$singletons[$id] = self::$singletons[$id]();
        }
        return 
self::$singletons[$id];
    }

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

From the example, it is clear that support for lazy initialization through the callable type and its handler has also been added.

Page translated: chatgpt 4-o
Back to top