diff --git a/src/Symfony/Component/HttpKernel/Bundle/BundleDependenciesInterface.php b/src/Symfony/Component/HttpKernel/Bundle/BundleDependenciesInterface.php new file mode 100644 index 000000000000..769a859c4267 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Bundle/BundleDependenciesInterface.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Bundle; + +/** + * Class BundleDependenciesInterface. + * + * Adds capability to let Bundles specify other bundles they need to load (before this one), both required and optional + * dependencies. + * + * This allows you to define only the bundle you want to use in registerBundles(), but don't need to take care about + * registering the dependencies it uses, and you won't need to make any changes in your kernel if those dependencies + * change. + * + * NOTE: In this interface bundle dependencies are returned as FQN strings because several bundles (and root) might + * register the same bundle. This means dependencies can not have arguments in its constructor, reflecting best practice + * in Symfony of not having any logic in Bundle constructors given it is executed on every Kernel boot. + * + * NOTE2: This functionality is not a bundle plugin system, but rather a way to set your upstream dependencies, + * or in the case of a distribution bundle, all bundles they are to be used with. + * + * @author André Roemcke + * + * @since 2.8 + * + * @api + */ +interface BundleDependenciesInterface +{ + /** + * @const Flag a Bundle Dependency as required, if missing throw exception + * + * @link \Symfony\Component\HttpKernel\Exception\DependencyMismatchException + */ + const DEP_REQUIRED = true; + + /** + * @const Flag a Bundle Dependency as optional, if missing silently ignore it + */ + const DEP_OPTIONAL = false; + + /** + * Returns an array of bundle dependencies Kernel should register on boot. + * + * Dependencies will be registered before current bundle, implying current bundle *MUST* be loaded after as it + * for instance extends it. + * + * Example of use: + * ```php + * class AcmeBundle extends Bundle implements BundleDependenciesInterface + * { + * public function getBundleDependencies($environment, $debug) + * { + * $dependencies = array(); + * + * // If you need to load some bundles only in dev using $environment (or in $debug) + * if ($environment === 'dev') { + * $dependencies['Egulias\SecurityDebugCommandBundle\EguliasSecurityDebugCommandBundle'] = self::DEP_REQUIRED; + * } + * + * return $dependencies + array( + * // Keys must be Fully Qualified Name (FQN) for the class to avoid bundles being loaded several times + * // Note: Currently, use of DEP_OPTIONAL causes a *uncached* file system call on boot (every request) + * // if dependency is missing, in a future versions this information might be cached. + * 'FOS\HttpCacheBundle\FOSHttpCacheBundle' => self::DEP_OPTIONAL, + * + * // If you require PHP 5.5+ it is possible to use `::class` constant for required dependencies: + * Oneup\FlysystemBundle\OneupFlysystemBundle::class => self::DEP_REQUIRED, + * ); + * } + * } + * ``` + * + * @param string $environment The current environment + * @param bool $debug Whether to debugging is enabled or not + * + * @return mixed[string] An array where key is bundle class (FQN) names as strings, and value DEP_* constants + * + * @api + */ + public function getBundleDependencies($environment, $debug); +} diff --git a/src/Symfony/Component/HttpKernel/Exception/DependencyMismatchException.php b/src/Symfony/Component/HttpKernel/Exception/DependencyMismatchException.php new file mode 100644 index 000000000000..3a4f8bb9cf50 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Exception/DependencyMismatchException.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * DependencyMismatchException. + * + * For exceptions related to dependency issues, like missing required dependency or recursive dependencies. + * + * @author André Roemcke + * + * @since 2.8 + */ +class DependencyMismatchException extends \RuntimeException +{ + /** + * Constructor. + * + * @param string $msg The message; for recursion issues, missing required dependency, .. + * @param array $stack The Bundle dependency stack trace up until the mismatch using bundle name or FQN + * @param \Exception $previous The previous exception if there was one + */ + public function __construct($msg, array $stack, \Exception $previous = null) + { + parent::__construct( + sprintf("%s, bundle dependencies stack trace: '%s'", $msg, var_export($stack, true)), + 0, + $previous + ); + } +} diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index d70439bd0822..3cc95405d5c6 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -27,10 +27,12 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\HttpKernel\Bundle\BundleDependenciesInterface; use Symfony\Component\HttpKernel\Config\EnvParametersResource; use Symfony\Component\HttpKernel\Config\FileLocator; use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; use Symfony\Component\HttpKernel\DependencyInjection\AddClassesToCachePass; +use Symfony\Component\HttpKernel\Exception\DependencyMismatchException; use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\Config\Loader\DelegatingLoader; use Symfony\Component\Config\ConfigCache; @@ -468,7 +470,7 @@ protected function initializeBundles() $topMostBundles = array(); $directChildren = array(); - foreach ($this->registerBundles() as $bundle) { + foreach ($this->registeredDependencies() 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)); @@ -514,6 +516,100 @@ protected function initializeBundles() } } + /** + * Returns an array of bundles to register, with dependencies, ordered. + * + * @uses appendDependenciesRecursively + * + * @return BundleInterface[] An array of bundle instances. + */ + protected function registeredDependencies() + { + $bundles = $this->registerBundles(); + $children = $rootBundles = array(); + $hasDependencies = false; + + // Build up bundles as a hash with FQN for basis to rebuild again in correct order given dependencies + foreach ($bundles as $bundle) { + $bundleFQN = get_class($bundle); + $children[$bundleFQN] = BundleDependenciesInterface::DEP_REQUIRED; + $rootBundles[$bundleFQN] = $bundle; + + if ($bundle instanceof BundleDependenciesInterface) { + $hasDependencies = true; + } + } + + if (!$hasDependencies) { + return $bundles; + } + + // Rebuild bundles order with dependencies recursively + $bundles = array(); + $stack = array(get_class($this) => true); + $this->appendDependenciesRecursively( + $children, + $bundles, + $rootBundles, + $stack + ); + + return $bundles; + } + + /** + * Append dependencies of bundles recursively. + * + * Accepted arguments are as documented below where `string` implies a string with class FQN(Fully Qualified Name), + * this string must be in exactly same format as returned by get_class() and PHP 5.5's CLASS constant. + * Example of FQN string: "Symfony\Component\HttpKernel\Bundle\BundleInterface" + * + * @link BundleDependenciesInterface For further details on dependencies and DEP_* constants. + * + * @param mixed[string] $children Dependencies to apply, key FQN, value {@see BundleDependenciesInterface} constants + * @param BundleInterface[string] $bundles Ordered bundles to append dependencies to + * @param BundleInterface[string] $rootBundles Loaded Bundles from registerRootBundles() + * @param bool[string] $stack For recursion protection, and for debug use on exceptions + * + * @throws \Symfony\Component\HttpKernel\Exception\DependencyMismatchException On missing dependencies + */ + protected function appendDependenciesRecursively(array $children, array &$bundles, array $rootBundles, array $stack) + { + foreach ($children as $dependencyFQN => $requiredFlag) { + if (isset($bundles[$dependencyFQN])) { + continue; + } elseif (isset($stack[$dependencyFQN])) { + throw new DependencyMismatchException(sprintf('Recursive dependency for "%s"', $dependencyFQN), array_keys($stack)); + } + + // If already loaded root bundle, use that to not re instantiate + if (isset($rootBundles[$dependencyFQN])) { + $dependency = $rootBundles[$dependencyFQN]; + } else { + if (!class_exists($dependencyFQN)) { + if ($requiredFlag === BundleDependenciesInterface::DEP_OPTIONAL) { + continue; + } + throw new DependencyMismatchException(sprintf('Could not find "%s"', $dependencyFQN), array_keys($stack)); + } + $dependency = new $dependencyFQN(); + } + + // Append dependencies of the dependency before we append dependency itself + if ($dependency instanceof BundleDependenciesInterface) { + $stack[$dependencyFQN] = true; + $this->appendDependenciesRecursively( + $dependency->getBundleDependencies($this->environment, $this->debug), + $bundles, + $rootBundles, + $stack + ); + } + + $bundles[$dependencyFQN] = $dependency; + } + } + /** * Gets the container class. * diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleADependenciesNon.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleADependenciesNon.php new file mode 100644 index 000000000000..775645ce4174 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleADependenciesNon.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies; + +use Symfony\Component\HttpKernel\Bundle\BundleDependenciesInterface; + +class BundleADependenciesNon implements BundleDependenciesInterface +{ + public function getBundleDependencies($environment, $debug) + { + return array(); + } +} diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleBDependenciesA.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleBDependenciesA.php new file mode 100644 index 000000000000..ac40582dff9e --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleBDependenciesA.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies; + +use Symfony\Component\HttpKernel\Bundle\BundleDependenciesInterface; + +class BundleBDependenciesA implements BundleDependenciesInterface +{ + public function getBundleDependencies($environment, $debug) + { + return array('Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies\BundleADependenciesNon' => self::DEP_REQUIRED); + } +} diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleCDependenciesBA.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleCDependenciesBA.php new file mode 100644 index 000000000000..269ae131da9a --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleCDependenciesBA.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies; + +use Symfony\Component\HttpKernel\Bundle\BundleDependenciesInterface; + +class BundleCDependenciesBA implements BundleDependenciesInterface +{ + public function getBundleDependencies($environment, $debug) + { + if ('prod_test' === $environment) { + return array(); + } elseif ($debug) { + return array('Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies\BundleADependenciesNon' => self::DEP_REQUIRED); + } + + return array( + 'Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies\BundleBDependenciesA' => self::DEP_REQUIRED, + 'Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies\BundleADependenciesNon' => self::DEP_OPTIONAL, + ); + } +} diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleDDependenciesE.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleDDependenciesE.php new file mode 100644 index 000000000000..d9469054b073 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleDDependenciesE.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies; + +use Symfony\Component\HttpKernel\Bundle\BundleDependenciesInterface; + +class BundleDDependenciesE implements BundleDependenciesInterface +{ + public function getBundleDependencies($environment, $debug) + { + return array('Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies\BundleEDependenciesD' => self::DEP_REQUIRED); + } +} diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleEDependenciesD.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleEDependenciesD.php new file mode 100644 index 000000000000..cc363eb466b1 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleEDependenciesD.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies; + +use Symfony\Component\HttpKernel\Bundle\BundleDependenciesInterface; + +class BundleEDependenciesD implements BundleDependenciesInterface +{ + public function getBundleDependencies($environment, $debug) + { + return array('Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies\BundleDDependenciesE' => self::DEP_REQUIRED); + } +} diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleFDependenciesMissing.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleFDependenciesMissing.php new file mode 100644 index 000000000000..af7f7ba74108 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleFDependenciesMissing.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies; + +use Symfony\Component\HttpKernel\Bundle\BundleDependenciesInterface; + +class BundleFDependenciesMissing implements BundleDependenciesInterface +{ + public function getBundleDependencies($environment, $debug) + { + return array('Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies\BundleMissing' => self::DEP_REQUIRED); + } +} diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleGDependenciesMissingOptional.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleGDependenciesMissingOptional.php new file mode 100644 index 000000000000..325edfee36cc --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/BundleDependencies/BundleGDependenciesMissingOptional.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies; + +use Symfony\Component\HttpKernel\Bundle\BundleDependenciesInterface; + +class BundleGDependenciesMissingOptional implements BundleDependenciesInterface +{ + public function getBundleDependencies($environment, $debug) + { + return array('Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies\BundleMissing' => self::DEP_OPTIONAL); + } +} diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForTest.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForTest.php index 5fd61bbc7e7c..09ca1c8f72f6 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/KernelForTest.php @@ -34,4 +34,9 @@ public function isBooted() { return $this->booted; } + + public function registeredDependencies() + { + return parent::registeredDependencies(); + } } diff --git a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php index c9e00dd68ea7..5d2e346dbf2f 100644 --- a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php @@ -13,6 +13,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Config\EnvParametersResource; +use Symfony\Component\HttpKernel\Exception\DependencyMismatchException; use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpFoundation\Request; @@ -20,6 +21,7 @@ use Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest; use Symfony\Component\HttpKernel\Tests\Fixtures\KernelForOverrideName; use Symfony\Component\HttpKernel\Tests\Fixtures\FooBarBundle; +use Symfony\Component\HttpKernel\Tests\Fixtures\BundleDependencies; class KernelTest extends \PHPUnit_Framework_TestCase { @@ -613,10 +615,10 @@ public function testInitializeBundles() $child = $this->getBundle(null, 'ParentABundle', 'ChildABundle'); // use test kernel so we can access getBundleMap() - $kernel = $this->getKernelForTest(array('registerBundles')); + $kernel = $this->getKernelForTest(array('registeredDependencies')); $kernel ->expects($this->once()) - ->method('registerBundles') + ->method('registeredDependencies') ->will($this->returnValue(array($parent, $child))) ; $kernel->boot(); @@ -632,10 +634,10 @@ public function testInitializeBundlesSupportInheritanceCascade() $child = $this->getBundle(null, 'ParentBBundle', 'ChildBBundle'); // use test kernel so we can access getBundleMap() - $kernel = $this->getKernelForTest(array('registerBundles')); + $kernel = $this->getKernelForTest(array('registeredDependencies')); $kernel ->expects($this->once()) - ->method('registerBundles') + ->method('registeredDependencies') ->will($this->returnValue(array($grandparent, $parent, $child))) ; $kernel->boot(); @@ -664,10 +666,10 @@ public function testInitializeBundlesSupportsArbitraryBundleRegistrationOrder() $child = $this->getBundle(null, 'ParentCBundle', 'ChildCBundle'); // use test kernel so we can access getBundleMap() - $kernel = $this->getKernelForTest(array('registerBundles')); + $kernel = $this->getKernelForTest(array('registeredDependencies')); $kernel ->expects($this->once()) - ->method('registerBundles') + ->method('registeredDependencies') ->will($this->returnValue(array($parent, $grandparent, $child))) ; $kernel->boot(); @@ -717,6 +719,133 @@ public function testInitializeBundleThrowsExceptionWhenABundleExtendsItself() $kernel->boot(); } + public function getRegisteredDependenciesData() + { + $aNon = new BundleDependencies\BundleADependenciesNon(); + $bA = new BundleDependencies\BundleBDependenciesA(); + $cBA = new BundleDependencies\BundleCDependenciesBA(); + $g = new BundleDependencies\BundleGDependenciesMissingOptional(); + + return array( + array( + array($bA, $aNon), + array($aNon, $bA), + ), + array( + array($cBA, $bA, $aNon), + array($aNon, $bA, $cBA), + ), + array( + array($cBA, $aNon, $bA), + array($aNon, $bA, $cBA), + ), + array( + array($bA, $aNon, $cBA), + array($aNon, $bA, $cBA), + ), + array( + array($bA, $cBA, $aNon), + array($aNon, $bA, $cBA), + ), + array( + array($aNon, $bA, $cBA), + array($aNon, $bA, $cBA), + ), + array( + array($aNon, $cBA, $bA), + array($aNon, $bA, $cBA), + ), + array( + array($cBA), + array($aNon, $bA, $cBA), + ), + array( + array($aNon, $cBA), + array($aNon, $bA, $cBA), + ), + array( + array($bA, $cBA), + array($aNon, $bA, $cBA), + ), + // optional + array( + array($g), + array($g), + ), + // params + array( + array($cBA), + array($cBA), + 'prod_test', + ), + array( + array($cBA), + array($aNon, $cBA), + 'test', + true, + ), + ); + } + + /** + * @dataProvider getRegisteredDependenciesData + */ + public function testRegisteredDependencies(array $dependencies, array $expected, $environment = 'test', $debug = false) + { + // use test kernel so we can test registeredDependencies() directly + $kernel = $this->getKernelForTest(array('registerBundles'), $environment, $debug); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue($dependencies)) + ; + + $bundles = array_values($kernel->registeredDependencies()); + $this->assertEquals($expected, $bundles); + } + + public function getRegisteredDependenciesExceptionData() + { + $dE = new BundleDependencies\BundleDDependenciesE(); + $eD = new BundleDependencies\BundleEDependenciesD(); + $f = new BundleDependencies\BundleFDependenciesMissing(); + + return array( + array( + array($dE), + 'Recursive dependency for "'.get_class($dE), + ), + array( + array($eD), + 'Recursive dependency for "'.get_class($eD), + ), + array( + array($f), + 'Could not find "', + ), + ); + } + /** + * @dataProvider getRegisteredDependenciesExceptionData + */ + public function testRegisteredDependenciesException(array $dependencies, $prefix) + { + // use test kernel so we can test registeredDependencies() directly + $kernel = $this->getKernelForTest(array('registerBundles')); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue($dependencies)) + ; + + try { + $kernel->registeredDependencies(); + $this->fail('Expected DependencyMismatchException'); + } catch (DependencyMismatchException $e) { + $this->assertStringStartsWith($prefix, $e->getMessage()); + } + } + public function testTerminateReturnsSilentlyIfKernelIsNotBooted() { $kernel = $this->getKernel(array('getHttpKernel')); @@ -765,9 +894,9 @@ public function testTerminateDelegatesTerminationOnlyForTerminableInterface() } /** - * Returns a mock for the BundleInterface + * Returns a mock for the BundleInterface. * - * @return BundleInterface + * @return \Symfony\Component\HttpKernel\Bundle\BundleInterface|\PHPUnit_Framework_MockObject_MockObject */ protected function getBundle($dir = null, $parent = null, $className = null, $bundleName = null) { @@ -810,11 +939,11 @@ protected function getBundle($dir = null, $parent = null, $className = null, $bu * @param array $methods Additional methods to mock (besides the abstract ones) * @param array $bundles Bundles to register * - * @return Kernel + * @return Kernel|\PHPUnit_Framework_MockObject_MockObject */ protected function getKernel(array $methods = array(), array $bundles = array()) { - $methods[] = 'registerBundles'; + $methods[] = 'registeredDependencies'; $kernel = $this ->getMockBuilder('Symfony\Component\HttpKernel\Kernel') @@ -823,7 +952,7 @@ protected function getKernel(array $methods = array(), array $bundles = array()) ->getMockForAbstractClass() ; $kernel->expects($this->any()) - ->method('registerBundles') + ->method('registeredDependencies') ->will($this->returnValue($bundles)) ; $p = new \ReflectionProperty($kernel, 'rootDir'); @@ -833,10 +962,19 @@ protected function getKernel(array $methods = array(), array $bundles = array()) return $kernel; } - protected function getKernelForTest(array $methods = array()) + /** + * Returns a mock for the abstract kernel. + * + * @param array $methods Additional methods to mock (besides the abstract ones) + * @param string $environment The current environment + * @param bool $debug Whether to debugging is enabled or not + * + * @return KernelForTest|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getKernelForTest(array $methods = array(), $environment = 'test', $debug = false) { $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') - ->setConstructorArgs(array('test', false)) + ->setConstructorArgs(array($environment, $debug)) ->setMethods($methods) ->getMock(); $p = new \ReflectionProperty($kernel, 'rootDir');