Skip to content

Commit 74d3037

Browse files
committed
Adding a MicroKernel that has a few special powers:
1) Services can be configured 2) Bundles can be registered using a method that also allows them to be immediately configured
1 parent 0a5807b commit 74d3037

File tree

4 files changed

+516
-0
lines changed

4 files changed

+516
-0
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Kernel;
13+
14+
use Symfony\Component\Config\Loader\LoaderInterface;
15+
use Symfony\Component\Config\Loader\LoaderResolverInterface;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
18+
/**
19+
* Configuration loader that holds the ContainerBuilder inside.
20+
*
21+
* It can be useful to pass this to KernelInterface::registerContainerConfiguration()
22+
* instead of a normal Loader if that method will need the ContainerBuilder.
23+
*
24+
* @author Ryan Weaver <ryan@knpuniversity.com>
25+
*/
26+
class ContainerBuilderAwareLoader implements LoaderInterface
27+
{
28+
/**
29+
* @var ContainerBuilder
30+
*/
31+
private $containerBuilder;
32+
33+
/**
34+
* @var LoaderInterface
35+
*/
36+
private $resourceLoader;
37+
38+
public function __construct(ContainerBuilder $builder, LoaderInterface $resourceLoader)
39+
{
40+
$this->containerBuilder = $builder;
41+
$this->resourceLoader = $resourceLoader;
42+
}
43+
44+
public function getContainerBuilder()
45+
{
46+
return $this->containerBuilder;
47+
}
48+
49+
public function getResourceLoader()
50+
{
51+
return $this->resourceLoader;
52+
}
53+
54+
/**
55+
* @see LoaderInterface
56+
*/
57+
public function load($resource, $type = null)
58+
{
59+
return $this->resourceLoader->load($resource, $type);
60+
}
61+
62+
/**
63+
* @see LoaderInterface
64+
*/
65+
public function supports($resource, $type = null)
66+
{
67+
return $this->resourceLoader->supports($resource, $type);
68+
}
69+
70+
/**
71+
* @see LoaderInterface
72+
*/
73+
public function getResolver()
74+
{
75+
return $this->resourceLoader->getResolver();
76+
}
77+
78+
/**
79+
* @see LoaderInterface
80+
*/
81+
public function setResolver(LoaderResolverInterface $resolver)
82+
{
83+
return $this->resourceLoader->setResolver($resolver);
84+
}
85+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Kernel;
13+
14+
use Symfony\Component\Config\Loader\Loader;
15+
use Symfony\Component\Config\Loader\LoaderInterface;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\ContainerInterface;
18+
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
19+
use Symfony\Component\HttpKernel\Kernel;
20+
21+
/**
22+
* A Kernel that allows you to configure services
23+
*
24+
* @author Ryan Weaver <ryan@knpuniversity.com>
25+
*/
26+
abstract class MicroKernel extends Kernel
27+
{
28+
private $bundlesRegistered = false;
29+
30+
/**
31+
* @var BundleInterface[]
32+
*/
33+
private $bundlesToRegister = array();
34+
35+
/**
36+
* An associative array of bundle names and their configuration arrays.
37+
*
38+
* @var array
39+
*/
40+
private $bundleConfiguration = array();
41+
42+
/**
43+
* Call $this->addBundle() to add (and optionally configure) your bundles.
44+
*
45+
* @return void
46+
*/
47+
abstract protected function configureBundles();
48+
49+
/**
50+
* Add any service definitions to your container.
51+
*
52+
* @param ContainerBuilder $c
53+
* @param LoaderInterface $loader
54+
* @return void
55+
*/
56+
abstract protected function configureServices(ContainerBuilder $c, LoaderInterface $loader);
57+
58+
/**
59+
* Adds a bundle to the kernel, which can optionally be configured.
60+
*
61+
* @param BundleInterface $bundle
62+
* @param array $configuration Configuration for this bundle
63+
* @return $this
64+
*/
65+
protected function addBundle(BundleInterface $bundle, $configuration = array())
66+
{
67+
if ($this->bundlesRegistered) {
68+
throw new \LogicException('addBundle() cannot be called after configureBundles() is called!');
69+
}
70+
71+
$this->bundlesToRegister[$bundle->getName()] = $bundle;
72+
73+
if ($configuration) {
74+
// add the configuration for the extension if any is given
75+
$this->configureBundle($bundle->getName(), $configuration);
76+
}
77+
78+
return $this;
79+
}
80+
81+
/**
82+
* Configure a bundle after it's already been added.
83+
*
84+
* @param string $name Like, FrameworkBundle
85+
* @param array $configuration
86+
*/
87+
protected function configureBundle($name, array $configuration)
88+
{
89+
if (!isset($this->bundlesToRegister[$name])) {
90+
throw new \InvalidArgumentException(sprintf(
91+
'No bundle called "%s" has been registered.',
92+
$name
93+
));
94+
}
95+
96+
$current = isset($this->bundleConfiguration[$name]) ? $this->bundleConfiguration[$name] : array();
97+
// merge the new configuration on the old
98+
$this->bundleConfiguration[$name] = array_merge($current, $configuration);
99+
}
100+
101+
/**
102+
* {@inheritdoc}
103+
*/
104+
public function registerBundles()
105+
{
106+
$this->configureBundles();
107+
$this->bundlesRegistered = true;
108+
109+
return array_values($this->bundlesToRegister);
110+
}
111+
112+
/**
113+
* Applies the bundle configuration and calls configureServices() for continued building.
114+
*
115+
* @param LoaderInterface $loader
116+
*/
117+
public function registerContainerConfiguration(LoaderInterface $loader)
118+
{
119+
if (!$loader instanceof ContainerBuilderAwareLoader) {
120+
throw new \LogicException('registerContainerConfiguration requires the LoaderInterface to be a ContainerBuilderAwareLoader.');
121+
}
122+
123+
/* @var ContainerBuilderAwareLoader $loader */
124+
$builder = $loader->getContainerBuilder();
125+
$loader = $loader->getResourceLoader();
126+
127+
// load the bundle configuration
128+
foreach ($this->bundleConfiguration as $bundleName => $configuration) {
129+
$extension = $this->bundlesToRegister[$bundleName]->getContainerExtension();
130+
if (!$extension) {
131+
throw new \LogicException('Bundle "%s" does not have an extension that can be configured.');
132+
}
133+
134+
$alias = $extension->getAlias();
135+
$builder->loadFromExtension($alias, $configuration);
136+
}
137+
138+
// allow the kernel to configure its services
139+
$this->configureServices(
140+
$builder,
141+
$loader
142+
);
143+
}
144+
145+
/**
146+
* Returns a loader with the ContainerBuilder embedded inside of it.
147+
*
148+
* @param ContainerInterface $container
149+
* @return ContainerBuilderAwareLoader
150+
*/
151+
protected function getContainerLoader(ContainerInterface $container)
152+
{
153+
if (!$container instanceof ContainerBuilder) {
154+
throw new \LogicException('Only ContainerBuilder instances are supported.');
155+
}
156+
157+
$loader = parent::getContainerLoader($container);
158+
159+
return new ContainerBuilderAwareLoader($container, $loader);
160+
}
161+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Kernel;
13+
14+
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernel;
15+
use Symfony\Component\Config\Loader\LoaderInterface;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\ContainerInterface;
18+
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
19+
20+
class MicroKernelForTest extends MicroKernel
21+
{
22+
private $configureBundlesCalled = false;
23+
24+
private $configureServicesCalled = false;
25+
26+
public function addBundleExternally(BundleInterface $bundle, $configuration = array())
27+
{
28+
$this->addBundle($bundle, $configuration);
29+
}
30+
31+
public function configureBundleExternally($bundleName, array $configuration)
32+
{
33+
$this->configureBundle($bundleName, $configuration);
34+
}
35+
36+
public function getContainerLoaderExternally(ContainerInterface $container)
37+
{
38+
return $this->getContainerLoader($container);
39+
}
40+
41+
protected function configureBundles()
42+
{
43+
$this->configureBundlesCalled = true;
44+
}
45+
46+
protected function configureServices(ContainerBuilder $c, LoaderInterface $loader)
47+
{
48+
$this->configureServicesCalled = true;
49+
}
50+
51+
public function wasConfigureBundlesCalled()
52+
{
53+
return $this->configureBundlesCalled;
54+
}
55+
56+
public function wasConfigureServicesCalled()
57+
{
58+
return $this->configureServicesCalled;
59+
}
60+
}

0 commit comments

Comments
 (0)