├── .styleci.yml ├── DependencyInjection ├── Compiler │ └── RemoveContainerWrapperPass.php ├── Configuration.php ├── ContainerWrapper.php └── LiipContainerWrapperExtension.php ├── LiipContainerWrapperBundle.php ├── README.md ├── Resources ├── config │ └── container_wrapper.xml └── meta │ └── LICENSE └── composer.json /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: symfony -------------------------------------------------------------------------------- /DependencyInjection/Compiler/RemoveContainerWrapperPass.php: -------------------------------------------------------------------------------- 1 | getParameter('liip_container_wrapper.disable_optimization')) { 16 | return; 17 | } 18 | 19 | $class = $container->getDefinition('liip_container_wrapper.service')->getClass(); 20 | 21 | foreach ($container->getDefinitions() as $id => $definition) { 22 | if ($definition->isAbstract() || $definition->getClass() != $class) { 23 | continue; 24 | } 25 | 26 | $arguments = $definition->getArguments(); 27 | if (!$this->containsMappings($arguments[0]) 28 | && !$this->containsMappings($container->getParameterBag()->resolveValue($arguments[1])) 29 | && !$this->containsMappings($arguments[2]) 30 | && !$this->containsMappings($container->getParameterBag()->resolveValue($arguments[3])) 31 | ) { 32 | $container->setAlias($id, 'service_container'); 33 | } 34 | } 35 | } 36 | 37 | private function containsMappings($list) 38 | { 39 | foreach ($list as $item) { 40 | if ($item !== true) { 41 | return true; 42 | } 43 | } 44 | 45 | return false; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class Configuration implements ConfigurationInterface 17 | { 18 | /** 19 | * Generates the configuration tree. 20 | * 21 | * @return TreeBuilder 22 | */ 23 | public function getConfigTreeBuilder() 24 | { 25 | $treeBuilder = new TreeBuilder(); 26 | $rootNode = $treeBuilder->root('liip_contrainer_wrapper', 'array'); 27 | 28 | $rootNode 29 | ->fixXmlConfig('service', 'services') 30 | ->children() 31 | ->arrayNode('services') 32 | ->useAttributeAsKey('services') 33 | ->prototype('scalar')->end() 34 | ->end() 35 | ->end() 36 | ->fixXmlConfig('parameter', 'parameters') 37 | ->children() 38 | ->arrayNode('parameters') 39 | ->useAttributeAsKey('parameters') 40 | ->prototype('scalar')->end() 41 | ->end() 42 | ->booleanNode('disable_optimization')->defaultValue('%kernel.debug%')->end() 43 | ->end() 44 | ->end(); 45 | 46 | return $treeBuilder; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /DependencyInjection/ContainerWrapper.php: -------------------------------------------------------------------------------- 1 | 13 | * @license MIT 14 | */ 15 | class ContainerWrapper extends ContainerAware implements ContainerInterface 16 | { 17 | protected $serviceIds; 18 | protected $container; 19 | protected $parameterNames; 20 | 21 | /** 22 | * Constructor. 23 | * 24 | * @param array $serviceIds A list of service ids 25 | * @param array $parameterNames A list of parameter name 26 | * @param array $defaultServiceIds A second list of service ids, usually a list of default services 27 | * @param array $defaultParameterNames A second list of parameter names, usually a list of default parameters 28 | */ 29 | public function __construct( 30 | array $serviceIds, 31 | array $parameterNames = array(), 32 | array $defaultServiceIds = array(), 33 | array $defaultParameterNames = array() 34 | ) { 35 | $this->parameterNames = $this->resolveConfig($parameterNames, $defaultParameterNames); 36 | $this->serviceIds = $this->resolveConfig($serviceIds, $defaultServiceIds); 37 | } 38 | 39 | /** 40 | * Resolve configuration mappings with defaults. 41 | */ 42 | private function resolveConfig($config, $defaultConfig) 43 | { 44 | foreach ($config as $name => $mappedName) { 45 | if (true === $mappedName) { 46 | $config[$name] = $name; 47 | } 48 | } 49 | 50 | foreach ($defaultConfig as $name => $mappedName) { 51 | if (empty($config[$name])) { 52 | $config[$name] = true === $mappedName ? $name : $mappedName; 53 | } 54 | } 55 | 56 | return $config; 57 | } 58 | 59 | /** 60 | * {@inheritdoc} 61 | */ 62 | public function getParameterBag() 63 | { 64 | throw new \LogicException('getParameterBag() is not supported by the service container wrapper.'); 65 | } 66 | 67 | /** 68 | * {@inheritdoc} 69 | */ 70 | public function getParameter($name) 71 | { 72 | if (!array_key_exists($name, $this->parameterNames)) { 73 | throw new \InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); 74 | } 75 | 76 | return $this->container->getParameter($this->parameterNames[$name]); 77 | } 78 | 79 | /** 80 | * {@inheritdoc} 81 | */ 82 | public function hasParameter($name) 83 | { 84 | if (!array_key_exists($name, $this->parameterNames)) { 85 | return false; 86 | } 87 | 88 | return $this->container->hasParameter($this->parameterNames[$name]); 89 | } 90 | 91 | /** 92 | * {@inheritdoc} 93 | */ 94 | public function setParameter($name, $value) 95 | { 96 | if (!array_key_exists($name, $this->parameterNames)) { 97 | $msg = "Parameter '%s' not supported. The following parameters are supported: %s"; 98 | throw new \InvalidArgumentException(sprintf($msg, $name, implode(', ', $this->parameterNames))); 99 | } 100 | 101 | return $this->container->getParameter($this->parameterNames[$name], $value); 102 | } 103 | 104 | /** 105 | * {@inheritdoc} 106 | */ 107 | public function set($id, $service, $scope = self::SCOPE_CONTAINER) 108 | { 109 | if (self::SCOPE_PROTOTYPE === $scope) { 110 | throw new \InvalidArgumentException('You cannot set services of scope "prototype".'); 111 | } 112 | 113 | $id = strtolower($id); 114 | 115 | if (!array_key_exists($id, $this->serviceIds)) { 116 | $msg = "Service '%s' not supported. The following services are supported: %s"; 117 | throw new \InvalidArgumentException(sprintf($msg, $id, implode(', ', $this->serviceIds))); 118 | } 119 | 120 | return $this->container->set($this->serviceIds[$id], $service, $scope); 121 | } 122 | 123 | /** 124 | * {@inheritdoc} 125 | */ 126 | public function has($id) 127 | { 128 | if (!array_key_exists($id, $this->serviceIds)) { 129 | return false; 130 | } 131 | 132 | return $this->container->has($this->serviceIds[$id]); 133 | } 134 | 135 | /** 136 | * {@inheritdoc} 137 | */ 138 | public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) 139 | { 140 | if (!array_key_exists($id, $this->serviceIds)) { 141 | if ($invalidBehavior) { 142 | $msg = "Service '%s' not supported. The following services are supported: %s"; 143 | throw new \InvalidArgumentException(sprintf($msg, $id, implode(', ', $this->serviceIds))); 144 | } 145 | 146 | return; 147 | } 148 | 149 | return $this->container->get($this->serviceIds[$id], $invalidBehavior); 150 | } 151 | 152 | /** 153 | * {@inheritdoc} 154 | */ 155 | public function getServiceIds() 156 | { 157 | return $this->serviceIds; 158 | } 159 | 160 | /** 161 | * {@inheritdoc} 162 | */ 163 | public function enterScope($name) 164 | { 165 | return $this->container->enterScope($name); 166 | } 167 | 168 | /** 169 | * {@inheritdoc} 170 | */ 171 | public function leaveScope($name) 172 | { 173 | return $this->container->leaveScope($name); 174 | } 175 | 176 | /** 177 | * {@inheritdoc} 178 | */ 179 | public function addScope(ScopeInterface $scope) 180 | { 181 | return $this->container->addScope($scope); 182 | } 183 | 184 | /** 185 | * {@inheritdoc} 186 | */ 187 | public function hasScope($name) 188 | { 189 | return $this->container->hasScope($name); 190 | } 191 | 192 | /** 193 | * {@inheritdoc} 194 | */ 195 | public function isScopeActive($name) 196 | { 197 | return $this->container->isScopeActive($name); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /DependencyInjection/LiipContainerWrapperExtension.php: -------------------------------------------------------------------------------- 1 | processConfiguration($configuration, $configs); 24 | 25 | $loader = $this->getFileLoader($container); 26 | $loader->load('container_wrapper.xml'); 27 | 28 | $container->setParameter($this->getAlias().'.disable_optimization', $config['disable_optimization']); 29 | 30 | if (!empty($config['services'])) { 31 | $container->setParameter($this->getAlias().'.default_service_map', $config['services']); 32 | } 33 | 34 | if (!empty($config['parameters'])) { 35 | $container->setParameter($this->getAlias().'.default_parameter_map', $config['parameters']); 36 | } 37 | } 38 | 39 | /** 40 | * Get File Loader. 41 | * 42 | * @param ContainerBuilder $container 43 | */ 44 | public function getFileLoader($container) 45 | { 46 | return new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /LiipContainerWrapperBundle.php: -------------------------------------------------------------------------------- 1 | addCompilerPass(new RemoveContainerWrapperPass(), PassConfig::TYPE_OPTIMIZE); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | UNMAINTAINED 2 | ============ 3 | 4 | This bundle is no longer maintained. Feel free to fork it if needed. 5 | 6 | ContainerWrapperBundle 7 | ====================== 8 | 9 | Because don't screw DI by just injecting everything. 10 | 11 | This bundle is for people who do not like container injection but are forced to use it. 12 | It provides a configurable proxy container that exposes only parts of the actual container, 13 | or has certain services re-defined. Instead of injecting the full container you can then 14 | inject this wrapper instead, which gives you back fine-grained control over the dependencies 15 | of that component. 16 | 17 | 1. It provides an abstract ``liip_container_wrapper.service`` service to extend from. 18 | 19 | 2. It provides a way to easy set default service and parameters to map 20 | 21 | 3. It can replace itself with an alias to ``service_container`` via a config 22 | option as long as no service/parameter is mapped to a different id/name 23 | 24 | Installation 25 | ============ 26 | 27 | 1. Install with Composer 28 | 29 | `php composer.phar require liip/container-wrapper-bundle` 30 | 31 | 2. Add this bundle to your application's kernel: 32 | 33 | // application/ApplicationKernel.php 34 | public function registerBundles() 35 | { 36 | return array( 37 | // ... 38 | new Liip\ContainerWrapperBundle\LiipContainerWrapperBundle(), 39 | // ... 40 | ); 41 | } 42 | 43 | Configuration 44 | ============= 45 | 46 | Default services and parameters maybe configured inside the application configuration. 47 | Setting ``disable_optimization`` to true will remove the ContainerWrapper service in favor of an 48 | alias to ``service_container`` in all cases where no mapping is used: 49 | 50 | # app/config.yml 51 | liip_container_wrapper: 52 | services: 53 | templating: acme_hello.templating 54 | parameters: 55 | kernel.debug: true 56 | disable_optimization: %kernel.debug% 57 | 58 | Both ``services`` and ``parameters`` are configured as key value pairs. The key is the id/name 59 | that is reachable from this specific ``ContainerWrapper`` instance. The value may either be 60 | ``true`` or an id/name of a different service or parameter. In case of a non ``true`` value 61 | the id/name will be mapped to this other id/name. 62 | 63 | Take the above example: 64 | 65 | // will return an instance of the 'acme_hello.templating' service 66 | $container->get('templating'); 67 | 68 | // will return an the value of the 'kernel.debug' parameter 69 | $container->getParameter('kernel.debug'); 70 | 71 | Note that because ``templating`` is mapped to a different service id, setting 72 | ``disable_optimization`` to ``false`` would have no effect, since a normal 73 | ``Container`` instance would not be able to support setting different alias's 74 | for ``templating``. 75 | 76 | Example use 77 | =========== 78 | 79 | The following YAML configuration extends the ``liip_container_wrapper.service`` abstract 80 | service to define an ``acme_hello.container`` service that can be injected in place of 81 | a ``Container`` instance that limits access to the services and parameters defined 82 | in the bundle configuration as well as the ones defined in this configuration: 83 | 84 | acme_hello.foo.controller: 85 | class: Acme\HelloBundle\Controller\FooController 86 | calls: 87 | - ['setContainer', [ @acme_hello.container ] ] 88 | 89 | acme_hello.container: 90 | parent: liip_container_wrapper.service 91 | arguments: 92 | index_0: 93 | some_service: true 94 | 95 | The story of saved kittens 96 | ========================== 97 | 98 | Why oh why? 99 | ----------- 100 | 101 | Yes, why oh why would someone bother to setup a nice dependency injection container 102 | and then waste all its goodness by just injecting the entire container, thereby 103 | effectively making their code dependent on essentially everything configured in the 104 | DI container? I am sure god kills more than a few kittens whenever .. 105 | 106 | Aside from the kittens, injecting the container also prevents granular adjustments 107 | to your dependencies. Aka controller Blabla needs a different templating service 108 | injected than controller DingDing, but how do you do that if your code uses 109 | ``$this->container->get('templating')`` in both? Praying to god is not the answer, 110 | he is busy killing kittens anyway. 111 | 112 | And those insane enough to bother with unit testing will also quickly realize that 113 | its even less fun to have to wrap everything they want to inject into a container 114 | mock object. 115 | 116 | Oh and no IDE auto completion support without jumping through hoops is also a major 117 | let down of injecting the container or is there an IDE yet that can parse your DIC 118 | to figure out wtf ``$this->container->get('i_hate_kittens')`` returns? 119 | 120 | But ok, there are many crappy answers like lazyness and such to still inject the DIC, 121 | but there are three semi acceptable reasons: 122 | 123 | 1) someone else wrote useful code, but thought it was a great idea to require injecting 124 | the entire DIC 125 | 126 | 2) there are a fair bit of optional dependencies which do not really solve themselves 127 | by splitting up the service (actually 2) is often a reason why 1) happens even for 128 | code written by good people). 129 | 130 | 3) you need to inject a service before the service actually can exist, like the 131 | request service in Symfony2 132 | 133 | But wait there is hope! 134 | ----------------------- 135 | 136 | In those cases you now have a way to prevent little kittens from being slain! 137 | 138 | Instead you can use the ContainerWrapper to explicitly configure your dependencies 139 | again and to map hardcoded service id's to regain the flexibility that was forsaking 140 | by not injecting the dependencies explicitly. 141 | 142 | But parameters! 143 | --------------- 144 | 145 | Yeah, parameters are also handled by the wrapper, though they don't really benefit 146 | from the lazy loading argument all that much, but I guess once a developer has gone 147 | the path of darkness, he might just keep using the DI container instead of explicitly 148 | injecting the parameters, so yeah, probably parameter support should be added too. Evil 149 | is just so resourceful at being evil. 150 | -------------------------------------------------------------------------------- /Resources/config/container_wrapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | %liip_container_wrapper.default_service_map% 18 | %liip_container_wrapper.default_parameter_map% 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Resources/meta/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2011 Liip 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "liip/container-wrapper-bundle", 3 | "type": "symfony-bundle", 4 | "description": "Because mommy taught you to not screw DI by just injecting everything.", 5 | "keywords": ["dependency injection"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Liip AG", 10 | "homepage": "http://www.liip.ch/" 11 | }, 12 | { 13 | "name": "Community contributions", 14 | "homepage": "https://github.com/liip/LiipThemeBundle/contributors" 15 | } 16 | ], 17 | "require": { 18 | "php": "^5.3.9|^7.0", 19 | "symfony/dependency-injection": "~2.3", 20 | "symfony/framework-bundle": "~2.3" 21 | }, 22 | "autoload": { 23 | "psr-4": { "Liip\\ContainerWrapperBundle\\": "" } 24 | }, 25 | "extra": { 26 | "branch-alias": { 27 | "dev-master": "1.1-dev" 28 | } 29 | } 30 | } 31 | --------------------------------------------------------------------------------