Skip to content

Commit

Permalink
Merge remote branch 'kriswallsmith/dic/auto-ext-load'
Browse files Browse the repository at this point in the history
* kriswallsmith/dic/auto-ext-load:
  [HttpKernel] added an subclass merge extension configuration compiler pass to ensure each bundle's "main" extension is loaded
  [DependencyInjection] extensions should only load if called during configuration
  • Loading branch information
fabpot committed Mar 5, 2011
2 parents 727326b + 33d47be commit 44d069a
Show file tree
Hide file tree
Showing 12 changed files with 251 additions and 36 deletions.
1 change: 1 addition & 0 deletions autoload.php.dist
Expand Up @@ -6,6 +6,7 @@ use Symfony\Component\ClassLoader\UniversalClassLoader;

$loader = new UniversalClassLoader();
$loader->registerNamespaces(array(
'Symfony\\Tests' => __DIR__.'/tests',
'Symfony' => __DIR__.'/src',
'Doctrine\\MongoDB' => __DIR__.'/vendor/doctrine-mongodb/lib',
'Doctrine\\ODM\\MongoDB' => __DIR__.'/vendor/doctrine-mongodb-odm/lib',
Expand Down
Expand Up @@ -30,10 +30,15 @@ public function process(ContainerBuilder $container)
$aliases = $container->getAliases();

foreach ($container->getExtensions() as $name => $extension) {
if (!$config = $container->getExtensionConfig($name)) {
// this extension was not called
continue;
}

$tmpContainer = new ContainerBuilder($container->getParameterBag());
$tmpContainer->addObjectResource($extension);

$extension->load($container->getExtensionConfig($name), $tmpContainer);
$extension->load($config, $tmpContainer);

$container->merge($tmpContainer);
}
Expand Down
47 changes: 12 additions & 35 deletions src/Symfony/Component/DependencyInjection/ContainerBuilder.php
Expand Up @@ -46,6 +46,8 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
public function __construct(ParameterBagInterface $parameterBag = null)
{
parent::__construct($parameterBag);

$this->compiler = new Compiler();
}

/**
Expand Down Expand Up @@ -166,47 +168,35 @@ public function loadFromExtension($extension, array $values = array())
}

/**
* Adds a compiler pass at the end of the current passes
* Adds a compiler pass.
*
* @param CompilerPassInterface $pass
* @param string $type
* @param CompilerPassInterface $pass A compiler pass
* @param string $type The type of compiler pass
*/
public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION)
{
if (null === $this->compiler) {
$this->initializeCompiler();
}

$this->compiler->addPass($pass, $type);

$this->addObjectResource($pass);
}

/**
* Returns the compiler pass config which can then be modified
* Returns the compiler pass config which can then be modified.
*
* @return PassConfig
* @return PassConfig The compiler pass config
*/
public function getCompilerPassConfig()
{
if (null === $this->compiler) {
$this->initializeCompiler();
}

return $this->compiler->getPassConfig();
}

/**
* Returns the compiler instance
* Returns the compiler.
*
* @return Compiler
* @return Compiler The compiler
*/
public function getCompiler()
{
if (null === $this->compiler) {
$this->initializeCompiler();
}

return $this->compiler;
}

Expand Down Expand Up @@ -379,7 +369,7 @@ public function merge(ContainerBuilder $container)
public function getExtensionConfig($name)
{
if (!isset($this->extensionConfigs[$name])) {
return array(array());
$this->extensionConfigs[$name] = array();
}

return $this->extensionConfigs[$name];
Expand All @@ -401,8 +391,8 @@ public function getExtensionConfig($name)
*/
public function compile()
{
if (null === $this->compiler) {
$this->initializeCompiler();
foreach ($this->compiler->getPassConfig()->getPasses() as $pass) {
$this->addObjectResource($pass);
}

$this->compiler->compile($this);
Expand Down Expand Up @@ -836,19 +826,6 @@ public function findTaggedServiceIds($name)
return $tags;
}

/**
* Initializes the compiler
*
* @return void
*/
protected function initializeCompiler()
{
$this->compiler = new Compiler();
foreach ($this->compiler->getPassConfig()->getPasses() as $pass) {
$this->addObjectResource($pass);
}
}

/**
* Returns the Service Conditionals.
*
Expand Down
@@ -0,0 +1,43 @@
<?php

/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Symfony\Component\HttpKernel\DependencyInjection;

use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass as BaseMergeExtensionConfigurationPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
* Handles automatically loading each bundle's default extension.
*
* @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
*/
class MergeExtensionConfigurationPass extends BaseMergeExtensionConfigurationPass
{
public function process(ContainerBuilder $container)
{
foreach ($container->getParameter('kernel.bundles') as $bundleName => $bundleClass) {
$bundleRefl = new \ReflectionClass($bundleClass);
$extClass = $bundleRefl->getNamespaceName().'\\DependencyInjection\\'.substr($bundleName, 0, -6).'Extension';

if (class_exists($extClass)) {
$ext = new $extClass();
$alias = $ext->getAlias();

// ensure all "main" extensions are loaded
if (!count($container->getExtensionConfig($alias))) {
$container->loadFromExtension($alias, array());
}
}
}

parent::process($container);
}
}
2 changes: 2 additions & 0 deletions src/Symfony/Component/HttpKernel/Kernel.php
Expand Up @@ -24,6 +24,7 @@
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Symfony\Component\HttpKernel\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\Config\Loader\DelegatingLoader;
use Symfony\Component\Config\ConfigCache;
Expand Down Expand Up @@ -457,6 +458,7 @@ protected function buildContainer()
$parameterBag = new ParameterBag($this->getKernelParameters());

$container = new ContainerBuilder($parameterBag);
$container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass());
foreach ($this->bundles as $bundle) {
$bundle->build($container);

Expand Down
Expand Up @@ -447,6 +447,29 @@ public function testExtension()
$container->getExtension('no_registered');
}

public function testRegisteredButNotLoadedExtension()
{
$extension = $this->getMock('Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface');
$extension->expects($this->once())->method('getAlias')->will($this->returnValue('project'));
$extension->expects($this->never())->method('load');

$container = new ContainerBuilder();
$container->registerExtension($extension);
$container->compile();
}

public function testRegisteredAndLoadedExtension()
{
$extension = $this->getMock('Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface');
$extension->expects($this->exactly(2))->method('getAlias')->will($this->returnValue('project'));
$extension->expects($this->once())->method('load')->with(array(array('foo' => 'bar')));

$container = new ContainerBuilder();
$container->registerExtension($extension);
$container->loadFromExtension('project', array('foo' => 'bar'));
$container->compile();
}

/**
* @covers Symfony\Component\DependencyInjection\ContainerBuilder::addInterfaceInjector
* @covers Symfony\Component\DependencyInjection\ContainerBuilder::addInterfaceInjectors
Expand Down
@@ -0,0 +1,66 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Tests\Component\HttpKernel;

use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass;

class MergeExtensionConfigurationPassTest extends \PHPUnit_Framework_TestCase
{
public function testAutoloadMainExtension()
{
$bundles = array(
'ExtensionAbsentBundle' => 'Symfony\\Tests\\Component\\HttpKernel\\Fixtures\\ExtensionAbsentBundle\\ExtensionAbsentBundle',
'ExtensionLoadedBundle' => 'Symfony\\Tests\\Component\\HttpKernel\\Fixtures\\ExtensionLoadedBundle\\ExtensionLoadedBundle',
'ExtensionPresentBundle' => 'Symfony\\Tests\\Component\\HttpKernel\\Fixtures\\ExtensionPresentBundle\\ExtensionPresentBundle',
);

$container = $this->getMock('Symfony\\Component\\DependencyInjection\\ContainerBuilder');
$params = $this->getMock('Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBag');

$container->expects($this->once())
->method('getParameter')
->with('kernel.bundles')
->will($this->returnValue($bundles));
$container->expects($this->exactly(2))
->method('getExtensionConfig')
->will($this->returnCallback(function($name) {
switch ($name) {
case 'extension_present':
return array();
case 'extension_loaded':
return array(array());
}
}));
$container->expects($this->once())
->method('loadFromExtension')
->with('extension_present', array());

$container->expects($this->any())
->method('getParameterBag')
->will($this->returnValue($params));
$params->expects($this->any())
->method('all')
->will($this->returnValue(array()));
$container->expects($this->any())
->method('getDefinitions')
->will($this->returnValue(array()));
$container->expects($this->any())
->method('getAliases')
->will($this->returnValue(array()));
$container->expects($this->any())
->method('getExtensions')
->will($this->returnValue(array()));

$configPass = new MergeExtensionConfigurationPass();
$configPass->process($container);
}
}
@@ -0,0 +1,18 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Tests\Component\HttpKernel\Fixtures\ExtensionAbsentBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class ExtensionAbsentBundle extends Bundle
{
}
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Tests\Component\HttpKernel\Fixtures\ExtensionLoadedBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

class ExtensionLoadedExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
}
}
@@ -0,0 +1,18 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Tests\Component\HttpKernel\Fixtures\ExtensionLoadedBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class ExtensionLoadedBundle extends Bundle
{
}
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Tests\Component\HttpKernel\Fixtures\ExtensionPresentBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

class ExtensionPresentExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
}
}
@@ -0,0 +1,18 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Tests\Component\HttpKernel\Fixtures\ExtensionPresentBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class ExtensionPresentBundle extends Bundle
{
}

0 comments on commit 44d069a

Please sign in to comment.