a guest Mar 20th, 2019 61 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
- # Scoping containers: context switching
- For Yii3 we have been working to split the framework up into different parts that can be used separately.
- One of the parts I've been working on a lot is the DI implementation. Traditionally Yii uses arrays for configuration, often these arrays are defined in multiple files and recursively merged to obtain the final configuration.
- The goal of this DI implementation is to use DI containers to configure different parts of an application differently.
- ## Injecting the DI container
- One of the first things they tell you about DI is that if you're injecting a container you're doing it wrong. While this is a good rule to live by, exceptions exist. Specifically parts of your application that decide the execution path will often use a DI container as a service locator to instantiate the request handler and inject its dependencies.
- In Yii this routing happens in modules. A module parses a request and routes it to an action. This action has dependencies which are injected by the DI container.
- ## Configuration
- Configuration of an application happens at different levels. A generic logging service might be configured at the application level while some modules have a different configuration for logging.
- Configuration the module specific logging service should never leak out; module A should never get a service meant for module B from the DI container.
- Several methods could be used to implement this requirement:
- 1. Copy the DI container for each module, then override the module specific entries before passing the container to the module.
- 2. Have a module temporarily override the entries in a container using local configuration
- 3. Use contextual DI containers
- ### Copying the container
- Copying the container might seem simple at first, but quickly becomes very complex.
- Copying a container means configuration changes no longer propagate in any direction. What should happen to references to already instantiated services? How can we reuse services that are requested from both containers?
- ### Temporary overrides
- In a system that only has linear execution this could work. It stops working however as soon as we switch from one module to another during a request; it then becomes very hard to keep the DI container in the right state without closely coupling all our code to the DI implementation.
- ### Context switching
- A solution I have created uses context switching as a solution to the problem of local configuration without introducing significant overhead or the issues mentioned with the other solutions.
- The idea is simple: we start with a simple composite container. This container contains no definitions but uses a set of other containers (that must support delegate lookup) to get services.
- When registering a DI container with the main container we add a context parameter.
- The main container has extra functions for retrieving a service from a specific context only.
- Since we want DI and not SL the above functionality is of course not enough; so we add a few more methods to the DI container.
- The root container can create containers that are locked to a specific context. These contextual containers could the be injected into a module which uses it just like it would use any other container.
- Important fact is that the modules just get a container injected as their dependency without having to know about its context or in which context it operates.
RAW Paste Data