From 5e5b6f0cf82b22b9d187b538829aaf0d42539b3d Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 28 Jan 2011 22:40:03 +0100 Subject: [PATCH] [HttpKernel] made sure that parent bundles are registered before their descendants --- src/Symfony/Component/HttpKernel/Kernel.php | 57 ++++++++++++------- .../Tests/Component/HttpKernel/KernelTest.php | 33 ++++++++++- 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 1ab343a2b5..da64b790a7 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -329,11 +329,11 @@ abstract class Kernel implements KernelInterface /** * Initialize the data structures related to the bundle management: - * - the bundle property maps a bundle name to a bundle instance, - * - the bundleMap property maps a bundle name to the bundle inheritance hierarchy. + * - the bundle property maps a bundle name to the bundle instance, + * - the bundleMap property maps a bundle name to the bundle inheritance hierarchy (most derived bundle first). * * @throws \LogicException if two bundles share a common name - * @throws \LogicException if a bundle tries to extend a non-existing bundle + * @throws \LogicException if a bundle tries to extend a non-registered bundle * @throws \LogicException if two bundles extend the same ancestor * */ @@ -341,36 +341,49 @@ abstract class Kernel implements KernelInterface { // init bundles $this->bundles = array(); - $this->bundleMap = array(); + $topMostBundles = array(); + $directChildren = array(); + foreach ($this->registerBundles() as $bundle) { $name = $bundle->getName(); if (isset($this->bundles[$name])) { throw new \LogicException(sprintf('Trying to register two bundles with the same name "%s"', $name)); - } + } $this->bundles[$name] = $bundle; - $this->bundleMap[$name] = array($bundle); + + if ($parentName = $bundle->getParent()) { + if (isset($directChildren[$parentName])) { + throw new \LogicException(sprintf('Bundle "%s" is directly extended by two bundles "%s" and "%s".', $parentName, $name, $directChildren[$parentName])); + } + $directChildren[$parentName] = $name; + } else { + $topMostBundles[$name] = $bundle; + } + } + + // look for orphans + if (count($diff = array_diff(array_keys($directChildren), array_keys($this->bundles)))) { + throw new \LogicException(sprintf('Bundle "%s" extends bundle "%s", which is not registered.', $directChildren[$diff[0]], $diff[0])); } // inheritance - $extended = array(); - foreach ($this->bundles as $name => $bundle) { - $parent = $bundle; - $first = true; - while ($parentName = $parent->getParent()) { - if (!isset($this->bundles[$parentName])) { - throw new \LogicException(sprintf('Bundle "%s" extends bundle "%s", which is not registered.', $name, $parentName)); - } + $this->bundleMap = array(); + foreach ($topMostBundles as $name => $bundle) { + $bundleMap = array($bundle); + $hierarchy = array($name); - if ($first && isset($extended[$parentName])) { - throw new \LogicException(sprintf('Bundle "%s" is directly extended by two bundles "%s" and "%s".', $parentName, $name, $extended[$parentName])); - } - - $first = false; - $parent = $this->bundles[$parentName]; - $extended[$parentName] = $name; - array_unshift($this->bundleMap[$parentName], $bundle); + while (isset($directChildren[$name])) { + $name = $directChildren[$name]; + array_unshift($bundleMap, $this->bundles[$name]); + $hierarchy[] = $name; + } + + foreach ($hierarchy as $bundle) { + $this->bundleMap[$bundle] = $bundleMap; + array_pop($bundleMap); } } + } protected function initializeContainer() diff --git a/tests/Symfony/Tests/Component/HttpKernel/KernelTest.php b/tests/Symfony/Tests/Component/HttpKernel/KernelTest.php index 9a9e64b780..2511387ccd 100644 --- a/tests/Symfony/Tests/Component/HttpKernel/KernelTest.php +++ b/tests/Symfony/Tests/Component/HttpKernel/KernelTest.php @@ -12,6 +12,7 @@ namespace Symfony\Tests\Component\HttpKernel; use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; use Symfony\Component\DependencyInjection\Loader\LoaderInterface; class KernelTest extends \PHPUnit_Framework_TestCase @@ -167,7 +168,7 @@ class KernelTest extends \PHPUnit_Framework_TestCase $kernel ->expects($this->once()) ->method('registerBundles') - ->will($this->returnValue(array($parent, $grandparent, $child))) + ->will($this->returnValue(array($grandparent, $parent, $child))) ; $kernel->initializeBundles(); @@ -194,6 +195,27 @@ class KernelTest extends \PHPUnit_Framework_TestCase $kernel->initializeBundles(); } + public function testInitializeBundlesSupportsArbitaryBundleRegistrationOrder() + { + $grandparent = $this->getBundle(null, null, 'GrandParentCCundle'); + $parent = $this->getBundle(null, 'GrandParentCCundle', 'ParentCCundle'); + $child = $this->getBundle(null, 'ParentCCundle', 'ChildCCundle'); + + $kernel = $this->getKernel(); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue(array($parent, $grandparent, $child))) + ; + + $kernel->initializeBundles(); + + $map = $kernel->getBundleMap(); + $this->assertEquals(array($child, $parent, $grandparent), $map['GrandParentCCundle']); + $this->assertEquals(array($child, $parent), $map['ParentCCundle']); + $this->assertEquals(array($child), $map['ChildCCundle']); + } + /** * @expectedException \LogicException */ @@ -232,7 +254,7 @@ class KernelTest extends \PHPUnit_Framework_TestCase protected function getBundle($dir = null, $parent = null, $className = null, $bundleName = null) { $bundle = $this - ->getMockBuilder('Symfony\Tests\Component\HttpKernel\KernelForTest') + ->getMockBuilder('Symfony\Tests\Component\HttpKernel\BundleForTest') ->setMethods(array('getPath', 'getParent', 'getName')) ->disableOriginalConstructor() ; @@ -241,7 +263,7 @@ class KernelTest extends \PHPUnit_Framework_TestCase $bundle->setMockClassName($className); } - $bundle = $bundle->getMock(); + $bundle = $bundle->getMockForAbstractClass(); $bundle ->expects($this->any()) @@ -312,3 +334,8 @@ class KernelForTest extends Kernel parent::initializeBundles(); } } + +abstract class BundleForTest implements BundleInterface +{ + // We can not extend Symfony\Component\HttpKernel\Bundle\Bundle as we want to mock getName() which is final +} \ No newline at end of file