cookiebot
/
addons
/
lib
/
ioc
/
container-interop
/
container-interop
/
docs
/
Delegate-lookup-meta.md
Delegate-lookup-meta.md in Cookiebot by Usercentrics – Automatic Cookie Banner for GDPR/CCPA & Google Consent Mode 3.10.1, at addons/lib/ioc/container-interop/container-interop/docs/Delegate-lookup-meta.md
| 1 | Delegate lookup feature Meta Document |
| 2 | ===================================== |
| 3 | |
| 4 | 1. Summary |
| 5 | ---------- |
| 6 | |
| 7 | This document describes the *delegate lookup feature*. |
| 8 | Containers are not required to implement this feature to respect the `ContainerInterface`. |
| 9 | However, containers implementing this feature will offer a greater lever of interoperability |
| 10 | with other containers, allowing multiple containers to share entries in the same application. |
| 11 | Implementation of this feature is therefore recommanded. |
| 12 | |
| 13 | 2. Why Bother? |
| 14 | -------------- |
| 15 | |
| 16 | The [](../src/Interop/Container/ContainerInterface.php`ContainerInterface`](../src/Interop/Container/ContainerInterface.php](../src/Interop/Container/ContainerInterface.php) ([](ContainerInterface.mdmeta doc](ContainerInterface.md](ContainerInterface.md)) |
| 17 | standardizes how frameworks and libraries make use of a container to obtain objects and parameters. |
| 18 | |
| 19 | By standardizing such a behavior, frameworks and libraries relying on the `ContainerInterface` |
| 20 | could work with any compatible container. |
| 21 | That would allow end users to choose their own container based on their own preferences. |
| 22 | |
| 23 | The `ContainerInterface` is also enough if we want to have several containers side-by-side in the same |
| 24 | application. For instance, this is what the [](https://github.com/jeremeamia/acclimate-container/blob/master/src/CompositeContainer.phpCompositeContainer](https://github.com/jeremeamia/acclimate-container/blob/master/src/CompositeContainer.php](https://github.com/jeremeamia/acclimate-container/blob/master/src/CompositeContainer.php) |
| 25 | class of [](https://github.com/jeremeamia/acclimate-containerAcclimate](https://github.com/jeremeamia/acclimate-container](https://github.com/jeremeamia/acclimate-container) is designed for: |
| 26 | |
| 27 |  |
| 28 | |
| 29 | However, an instance in container 1 cannot reference an instance in container 2. |
| 30 | |
| 31 | It would be better if an instance of container 1 could reference an instance in container 2, |
| 32 | and the opposite should be true. |
| 33 | |
| 34 |  |
| 35 | |
| 36 | In the sample above, entry 1 in container 1 is referencing entry 3 in container 2. |
| 37 | |
| 38 | 3. Scope |
| 39 | -------- |
| 40 | |
| 41 | ### 3.1 Goals |
| 42 | |
| 43 | The goal of the *delegate lookup* feature is to allow several containers to share entries. |
| 44 | |
| 45 | 4. Approaches |
| 46 | ------------- |
| 47 | |
| 48 | ### 4.1 Chosen Approach |
| 49 | |
| 50 | Containers implementing this feature can perform dependency lookups in other containers. |
| 51 | |
| 52 | A container implementing this feature: |
| 53 | |
| 54 | - must implement the `ContainerInterface` |
| 55 | - must provide a way to register a *delegate container* (using a constructor parameter, or a setter, or any |
| 56 | possible way). The *delegate container* must implement the `ContainerInterface`. |
| 57 | |
| 58 | When a *delegate container* is configured on a container: |
| 59 | |
| 60 | - Calls to the `get` method should only return an entry if the entry is part of the container. |
| 61 | If the entry is not part of the container, an exception should be thrown (as required in the `ContainerInterface`). |
| 62 | - Calls to the `has` method should only return *true* if the entry is part of the container. |
| 63 | If the entry is not part of the container, *false* should be returned. |
| 64 | - Finally, the important part: if the entry we are fetching has dependencies, |
| 65 | **instead** of perfoming the dependency lookup in the container, the lookup is performed on the *delegate container*. |
| 66 | |
| 67 | Important! By default, the lookup should be performed on the delegate container **only**, not on the container itself. |
| 68 | |
| 69 | It is however allowed for containers to provide exception cases for special entries, and a way to lookup into |
| 70 | the same container (or another container) instead of the delegate container. |
| 71 | |
| 72 | ### 4.2 Typical usage |
| 73 | |
| 74 | The *delegate container* will usually be a composite container. A composite container is a container that |
| 75 | contains several other containers. When performing a lookup on a composite container, the inner containers are |
| 76 | queried until one container returns an entry. |
| 77 | An inner container implementing the *delegate lookup feature* will return entries it contains, but if these |
| 78 | entries have dependencies, the dependencies lookup calls will be performed on the composite container, giving |
| 79 | a chance to all containers to answer. |
| 80 | |
| 81 | Interestingly enough, the order in which containers are added in the composite container matters. Indeed, |
| 82 | the first containers to be added in the composite container can "override" the entries of containers with |
| 83 | lower priority. |
| 84 | |
| 85 |  |
| 86 | |
| 87 | In the example above, "container 2" contains a controller "myController" and the controller is referencing an |
| 88 | "entityManager" entry. "Container 1" contains also an entry named "entityManager". |
| 89 | Without the *delegate lookup* feature, when requesting the "myController" instance to container 2, it would take |
| 90 | in charge the instanciation of both entries. |
| 91 | |
| 92 | However, using the *delegate lookup* feature, here is what happens when we ask the composite container for the |
| 93 | "myController" instance: |
| 94 | |
| 95 | - The composite container asks container 1 if if contains the "myController" instance. The answer is no. |
| 96 | - The composite container asks container 2 if if contains the "myController" instance. The answer is yes. |
| 97 | - The composite container performs a `get` call on container 2 for the "myController" instance. |
| 98 | - Container 2 sees that "myController" has a dependency on "entityManager". |
| 99 | - Container 2 delegates the lookup of "entityManager" to the composite container. |
| 100 | - The composite container asks container 1 if if contains the "entityManager" instance. The answer is yes. |
| 101 | - The composite container performs a `get` call on container 1 for the "entityManager" instance. |
| 102 | |
| 103 | In the end, we get a controller instanciated by container 2 that references an entityManager instanciated |
| 104 | by container 1. |
| 105 | |
| 106 | ### 4.3 Alternative: the fallback strategy |
| 107 | |
| 108 | The first proposed approach we tried was to perform all the lookups in the "local" container, |
| 109 | and if a lookup fails in the container, to use the delegate container. In this scenario, the |
| 110 | delegate container is used in "fallback" mode. |
| 111 | |
| 112 | This strategy has been described in @moufmouf blog post: http://mouf-php.com/container-interop-whats-next (solution 1). |
| 113 | It was also discussed [](https://github.com/container-interop/container-interop/pull/8#issuecomment-33570697here](https://github.com/container-interop/container-interop/pull/8#issuecomment-33570697](https://github.com/container-interop/container-interop/pull/8#issuecomment-33570697) and |
| 114 | [](https://github.com/container-interop/container-interop/pull/20#issuecomment-56599631here](https://github.com/container-interop/container-interop/pull/20#issuecomment-56599631](https://github.com/container-interop/container-interop/pull/20#issuecomment-56599631). |
| 115 | |
| 116 | Problems with this strategy: |
| 117 | |
| 118 | - Heavy problem regarding infinite loops |
| 119 | - Unable to overload a container entry with the delegate container entry |
| 120 | |
| 121 | ### 4.4 Alternative: force implementing an interface |
| 122 | |
| 123 | The first proposed approach was to develop a `ParentAwareContainerInterface` interface. |
| 124 | It was proposed here: https://github.com/container-interop/container-interop/pull/8 |
| 125 | |
| 126 | The interface would have had the behaviour of the delegate lookup feature but would have forced the addition of |
| 127 | a `setParentContainter` method: |
| 128 | |
| 129 | ```php |
| 130 | interface ParentAwareContainerInterface extends ReadableContainerInterface { |
| 131 | /** |
| 132 | * Sets the parent container associated to that container. This container will call |
| 133 | * the parent container to fetch dependencies. |
| 134 | * |
| 135 | * @param ContainerInterface $container |
| 136 | */ |
| 137 | public function setParentContainer(ContainerInterface $container); |
| 138 | } |
| 139 | ``` |
| 140 | |
| 141 | The interface idea was first questioned by @Ocramius [](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777). |
| 142 | @Ocramius expressed the idea that an interface should not contain setters, otherwise, it is forcing implementation |
| 143 | details on the class implementing the interface. |
| 144 | Then @mnapoli made a proposal for a "convention" [](https://github.com/container-interop/container-interop/pull/8#issuecomment-51841079here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51841079](https://github.com/container-interop/container-interop/pull/8#issuecomment-51841079), |
| 145 | this idea was further discussed until all participants in the discussion agreed to remove the interface idea |
| 146 | and replace it with a "standard" feature. |
| 147 | |
| 148 | **Pros:** |
| 149 | |
| 150 | If we had had an interface, we could have delegated the registration of the delegate/composite container to the |
| 151 | the delegate/composite container itself. |
| 152 | For instance: |
| 153 | |
| 154 | ```php |
| 155 | $containerA = new ContainerA(); |
| 156 | $containerB = new ContainerB(); |
| 157 | |
| 158 | $compositeContainer = new CompositeContainer([$containerA, $containerB]); |
| 159 | |
| 160 | // The call to 'setParentContainer' is delegated to the CompositeContainer |
| 161 | // It is not the responsibility of the user anymore. |
| 162 | class CompositeContainer { |
| 163 | ... |
| 164 | |
| 165 | public function __construct($containers) { |
| 166 | foreach ($containers as $container) { |
| 167 | if ($container instanceof ParentAwareContainerInterface) { |
| 168 | $container->setParentContainer($this); |
| 169 | } |
| 170 | } |
| 171 | ... |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | ``` |
| 176 | |
| 177 | **Cons:** |
| 178 | |
| 179 | Cons have been extensively discussed [](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777here](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777](https://github.com/container-interop/container-interop/pull/8#issuecomment-51721777). |
| 180 | Basically, forcing a setter into an interface is a bad idea. Setters are similar to constructor arguments, |
| 181 | and it's a bad idea to standardize a constructor: how the delegate container is configured into a container is an implementation detail. This outweights the benefits of the interface. |
| 182 | |
| 183 | ### 4.4 Alternative: no exception case for delegate lookups |
| 184 | |
| 185 | Originally, the proposed wording for delegate lookup calls was: |
| 186 | |
| 187 | > Important! The lookup MUST be performed on the delegate container **only**, not on the container itself. |
| 188 | |
| 189 | This was later replaced by: |
| 190 | |
| 191 | > Important! By default, the lookup SHOULD be performed on the delegate container **only**, not on the container itself. |
| 192 | > |
| 193 | > It is however allowed for containers to provide exception cases for special entries, and a way to lookup |
| 194 | > into the same container (or another container) instead of the delegate container. |
| 195 | |
| 196 | Exception cases have been allowed to avoid breaking dependencies with some services that must be provided |
| 197 | by the container (on @njasm proposal). This was proposed here: https://github.com/container-interop/container-interop/pull/20#issuecomment-56597235 |
| 198 | |
| 199 | ### 4.5 Alternative: having one of the containers act as the composite container |
| 200 | |
| 201 | In real-life scenarios, we usually have a big framework (Symfony 2, Zend Framework 2, etc...) and we want to |
| 202 | add another DI container to this container. Most of the time, the "big" framework will be responsible for |
| 203 | creating the controller's instances, using it's own DI container. Until *container-interop* is fully adopted, |
| 204 | the "big" framework will not be aware of the existence of a composite container that it should use instead |
| 205 | of its own container. |
| 206 | |
| 207 | For this real-life use cases, @mnapoli and @moufmouf proposed to extend the "big" framework's DI container |
| 208 | to make it act as a composite container. |
| 209 | |
| 210 | This has been discussed [](https://github.com/container-interop/container-interop/pull/8#issuecomment-40367194here](https://github.com/container-interop/container-interop/pull/8#issuecomment-40367194](https://github.com/container-interop/container-interop/pull/8#issuecomment-40367194) |
| 211 | and [](http://mouf-php.com/container-interop-whats-next#solution4here](http://mouf-php.com/container-interop-whats-next#solution4](http://mouf-php.com/container-interop-whats-next#solution4). |
| 212 | |
| 213 | This was implemented in Symfony 2 using: |
| 214 | |
| 215 | - [](https://github.com/thecodingmachine/interop.symfony.di/tree/v0.1.0interop.symfony.di](https://github.com/thecodingmachine/interop.symfony.di/tree/v0.1.0](https://github.com/thecodingmachine/interop.symfony.di/tree/v0.1.0) |
| 216 | - [](https://github.com/mnapoli/framework-interop/framework interop](https://github.com/mnapoli/framework-interop/](https://github.com/mnapoli/framework-interop/) |
| 217 | |
| 218 | This was implemented in Silex using: |
| 219 | |
| 220 | - [](https://github.com/thecodingmachine/interop.silex.diinterop.silex.di](https://github.com/thecodingmachine/interop.silex.di](https://github.com/thecodingmachine/interop.silex.di) |
| 221 | |
| 222 | Having a container act as the composite container is not part of the delegate lookup standard because it is |
| 223 | simply a temporary design pattern used to make existing frameworks that do not support yet ContainerInterop |
| 224 | play nice with other DI containers. |
| 225 | |
| 226 | |
| 227 | 5. Implementations |
| 228 | ------------------ |
| 229 | |
| 230 | The following projects already implement the delegate lookup feature: |
| 231 | |
| 232 | - [](http://mouf-php.comMouf](http://mouf-php.com](http://mouf-php.com), through the [](https://github.com/thecodingmachine/mouf/blob/2.0/src/Mouf/MoufManager.php#L2120`setDelegateLookupContainer` method](https://github.com/thecodingmachine/mouf/blob/2.0/src/Mouf/MoufManager.php#L2120](https://github.com/thecodingmachine/mouf/blob/2.0/src/Mouf/MoufManager.php#L2120) |
| 233 | - [](http://php-di.org/PHP-DI](http://php-di.org/](http://php-di.org/), through the [](https://github.com/mnapoli/PHP-DI/blob/master/src/DI/Container.php#L72`$wrapperContainer` parameter of the constructor](https://github.com/mnapoli/PHP-DI/blob/master/src/DI/Container.php#L72](https://github.com/mnapoli/PHP-DI/blob/master/src/DI/Container.php#L72) |
| 234 | - [](https://github.com/moufmouf/pimple-interoppimple-interop](https://github.com/moufmouf/pimple-interop](https://github.com/moufmouf/pimple-interop), through the [](https://github.com/moufmouf/pimple-interop/blob/master/src/Interop/Container/Pimple/PimpleInterop.php#L62`$container` parameter of the constructor](https://github.com/moufmouf/pimple-interop/blob/master/src/Interop/Container/Pimple/PimpleInterop.php#L62](https://github.com/moufmouf/pimple-interop/blob/master/src/Interop/Container/Pimple/PimpleInterop.php#L62) |
| 235 | |
| 236 | 6. People |
| 237 | --------- |
| 238 | |
| 239 | Are listed here all people that contributed in the discussions, by alphabetical order: |
| 240 | |
| 241 | - [](https://github.com/drealecsAlexandru Pătrănescu](https://github.com/drealecs](https://github.com/drealecs) |
| 242 | - [](https://github.com/pothercaBen Peachey](https://github.com/potherca](https://github.com/potherca) |
| 243 | - [](https://github.com/moufmoufDavid Négrier](https://github.com/moufmouf](https://github.com/moufmouf) |
| 244 | - [](https://github.com/jeremeamiaJeremy Lindblom](https://github.com/jeremeamia](https://github.com/jeremeamia) |
| 245 | - [](https://github.com/OcramiusMarco Pivetta](https://github.com/Ocramius](https://github.com/Ocramius) |
| 246 | - [](https://github.com/mnapoliMatthieu Napoli](https://github.com/mnapoli](https://github.com/mnapoli) |
| 247 | - [](https://github.com/njasmNelson J Morais](https://github.com/njasm](https://github.com/njasm) |
| 248 | - [](https://github.com/philsturgeonPhil Sturgeon](https://github.com/philsturgeon](https://github.com/philsturgeon) |
| 249 | - [](https://github.com/shochdoerferStephan Hochdörfer](https://github.com/shochdoerfer](https://github.com/shochdoerfer) |
| 250 | |
| 251 | 7. Relevant Links |
| 252 | ----------------- |
| 253 | |
| 254 | _**Note:** Order descending chronologically._ |
| 255 | |
| 256 | - [](https://github.com/container-interop/container-interop/pull/20Pull request on the delegate lookup feature](https://github.com/container-interop/container-interop/pull/20](https://github.com/container-interop/container-interop/pull/20) |
| 257 | - [](https://github.com/container-interop/container-interop/pull/8Pull request on the interface idea](https://github.com/container-interop/container-interop/pull/8](https://github.com/container-interop/container-interop/pull/8) |
| 258 | - [](http://mouf-php.com/container-interop-whats-nextOriginal article exposing the delegate lookup idea along many others](http://mouf-php.com/container-interop-whats-next](http://mouf-php.com/container-interop-whats-next) |
| 259 | |
| 260 |