The Container in the HLEB2 framework is a collection of so-called services, which can be retrieved from or added to the container.
Services are logically self-contained structures with a specific purpose.
In the HLEB2 framework, the initialization of services in the container is streamlined without unnecessary abstraction.
Services are not initialized by the framework from configuration, as is typically implemented, but rather within a special class App\Bootstrap\BaseContainer, which is accessible for editing by the developer using the framework.
(Most often, you'll use the App\Bootstrap\ContainerFactory class, where services are defined as singletons.)
All the files for these classes are located in the /app/Bootstrap/ directory of the project.
This structure allows a significant number of services to be added to the container without a major impact on performance.
This class represents the container that will be used to retrieve services.
If a service needs to be a new instance of the class each time it's requested from the container, it should be specified here within a match() expression.
Adding a service is similar to adding it in the ContainerFactory class.
A factory for creating services as singletons, with the ability to override the framework's default services. It's used to add custom services, which are initialized only once per request.
For example, we might need to add a RequestIdService that returns a unique ID for the current request. This is a demonstration example of a service; in general, services represent more complex structures. Let's add its creation to the ContainerFactory class:
Now, when the RequestIdInterface is requested from the container, it will return an instance of RequestIdService, stored as a singleton.
The key for retrieval can be defined not only as an interface but also as the base class RequestIdService, as it will be utilized in DI (Dependency Injection).
Despite the fact that the match() expression can contain multiple keys to a value, to avoid duplicating services (and consequently violating the singleton principle), only one should be assigned.
To simplify working with the new service keyed by RequestIdInterface, let's add a new method in the container. This will make it easier to find in the container through the IDE.
The new method requestId is added to the container class (BaseContainer). Now the class looks like this:
Important! For this to work, the requestId method must also be added to the App\Bootstrap\ContainerInterface interface.
In the example, the service is assigned by interface, allowing the service class in the container to change while maintaining the interface linkage. For your own internal application classes, you can also omit the interface here and specify the class mapping directly.
For the framework's standard services, all these actions have already been done; you can retrieve them through the corresponding controller method.
The process of creating a new service is detailed in the example of adding a real library.
Creating interdependent services is described in the section non-standard container usage.
You have probably noticed the rollback() function in the ContainerFactory class. This function is necessary for resetting the states of services during asynchronous use of the framework, for example, when used with RoadRunner.
Here is how it works:
When the framework completes an asynchronous request, it resets the state of standard services.
Then, it calls the rollback() function to execute the code it contains to reset the state of manually added services.
Therefore, if the framework is used in asynchronous mode, you can initialize the service state reset (as well as that of any other module) here.
← Console Commands Using the Container →