From 728dd938e15c4aa1e16eab7d764fe1332810dcaf Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 23 Jan 2013 15:18:20 +0100 Subject: [PATCH 01/12] [FrameworkBundle] Fix the DI config see #6829 --- .../FrameworkBundle/DependencyInjection/Configuration.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 885752b89da6b..0de067d8c0478 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -121,7 +121,8 @@ private function addRouterProxySection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('router_proxy') ->info('proxy configuration for the HTTP content renderer') - ->canBeDisabled() + ->addDefaultsIfNotSet() + ->canBeEnabled() ->children() ->scalarNode('path')->defaultValue('/_proxy')->end() ->end() From 868dc27833dd2dab9ddbd81b29154430f8b4d0cc Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 23 Jan 2013 16:55:02 +0100 Subject: [PATCH 02/12] [DIC] tweaks --- .../Extension/Extension.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Extension/Extension.php b/src/Symfony/Component/DependencyInjection/Extension/Extension.php index e5c51a7b58922..7a1ba0faf2d19 100644 --- a/src/Symfony/Component/DependencyInjection/Extension/Extension.php +++ b/src/Symfony/Component/DependencyInjection/Extension/Extension.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Extension; use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\Definition\Processor; @@ -62,26 +63,19 @@ public function getNamespace() * * @return string The alias * - * @throws \BadMethodCallException When the extension name does not follow conventions + * @throws BadMethodCallException When the extension name does not follow conventions */ public function getAlias() { $className = get_class($this); if (substr($className, -9) != 'Extension') { - throw new \BadMethodCallException('This extension does not follow the naming convention; you must overwrite the getAlias() method.'); + throw new BadMethodCallException('This extension does not follow the naming convention; you must overwrite the getAlias() method.'); } $classBaseName = substr(strrchr($className, '\\'), 1, -9); return Container::underscore($classBaseName); } - final protected function processConfiguration(ConfigurationInterface $configuration, array $configs) - { - $processor = new Processor(); - - return $processor->processConfiguration($configuration, $configs); - } - /** * {@inheritDoc} */ @@ -104,4 +98,11 @@ public function getConfiguration(array $config, ContainerBuilder $container) return null; } + + final protected function processConfiguration(ConfigurationInterface $configuration, array $configs) + { + $processor = new Processor(); + + return $processor->processConfiguration($configuration, $configs); + } } From 2c2521a077ff06305383c0d6df4e243207c73c6a Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 23 Jan 2013 16:56:02 +0100 Subject: [PATCH 03/12] [WIP][DIC] Better handling of enableable array nodes --- .../DependencyInjection/Configuration.php | 13 +- .../FrameworkExtension.php | 171 +++++++++--------- .../Fixtures/php/validation_annotations.php | 2 +- src/Symfony/Component/Config/CHANGELOG.md | 3 + .../Builder/ArrayNodeDefinition.php | 2 + .../Extension/Extension.php | 18 ++ .../Tests/Extension/ExtensionTest.php | 81 +++++++++ 7 files changed, 198 insertions(+), 92 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Extension/ExtensionTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 0de067d8c0478..29822462b7723 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -91,10 +91,10 @@ private function addFormSection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('form') ->info('form configuration') - ->canBeDisabled() + ->canBeEnabled() ->end() ->arrayNode('csrf_protection') - ->canBeDisabled() + ->canBeEnabled() ->children() ->scalarNode('field_name')->defaultValue('_token')->end() ->end() @@ -109,7 +109,7 @@ private function addEsiSection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('esi') ->info('esi configuration') - ->canBeDisabled() + ->canBeEnabled() ->end() ->end() ; @@ -121,7 +121,6 @@ private function addRouterProxySection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('router_proxy') ->info('proxy configuration for the HTTP content renderer') - ->addDefaultsIfNotSet() ->canBeEnabled() ->children() ->scalarNode('path')->defaultValue('/_proxy')->end() @@ -137,7 +136,7 @@ private function addProfilerSection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('profiler') ->info('profiler configuration') - ->canBeDisabled() + ->canBeEnabled() ->children() ->booleanNode('only_exceptions')->defaultFalse()->end() ->booleanNode('only_master_requests')->defaultFalse()->end() @@ -368,7 +367,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('translator') ->info('translator configuration') - ->canBeDisabled() + ->canBeEnabled() ->children() ->scalarNode('fallback')->defaultValue('en')->end() ->end() @@ -383,7 +382,7 @@ private function addValidationSection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('validation') ->info('validation configuration') - ->canBeDisabled() + ->canBeEnabled() ->children() ->scalarNode('cache')->end() ->booleanNode('enable_annotations')->defaultFalse()->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 958c2ce35065c..dcf1f8c42e125 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -81,38 +81,21 @@ public function load(array $configs, ContainerBuilder $container) $this->registerSessionConfiguration($config['session'], $container, $loader); } - if (isset($config['form']) && !empty($config['form']['enabled'])) { + if ($this->isConfigEnabled($container, $config['form'])) { $this->registerFormConfiguration($config, $container, $loader); $config['validation']['enabled'] = true; } - if (!empty($config['validation']['enabled'])) { - $this->registerValidationConfiguration($config['validation'], $container, $loader); - } - - if (isset($config['esi'])) { - $this->registerEsiConfiguration($config['esi'], $loader); - } - - if (isset($config['router_proxy'])) { - $this->registerRouterProxyConfiguration($config['router_proxy'], $container, $loader); - } - - if (isset($config['profiler'])) { - $this->registerProfilerConfiguration($config['profiler'], $container, $loader); - } - - if (isset($config['router'])) { - $this->registerRouterConfiguration($config['router'], $container, $loader); - } - if (isset($config['templating'])) { $this->registerTemplatingConfiguration($config['templating'], $config['ide'], $container, $loader); } - if (isset($config['translator'])) { - $this->registerTranslatorConfiguration($config['translator'], $container); - } + $this->registerValidationConfiguration($config['validation'], $container, $loader); + $this->registerEsiConfiguration($config['esi'], $container, $loader); + $this->registerRouterProxyConfiguration($config['router_proxy'], $container, $loader); + $this->registerProfilerConfiguration($config['profiler'], $container, $loader); + $this->registerRouterConfiguration($config['router'], $container, $loader); + $this->registerTranslatorConfiguration($config['translator'], $container); $this->registerAnnotationsConfiguration($config['annotations'], $container, $loader); @@ -161,7 +144,7 @@ public function load(array $configs, ContainerBuilder $container) private function registerFormConfiguration($config, ContainerBuilder $container, XmlFileLoader $loader) { $loader->load('form.xml'); - if (isset($config['csrf_protection'])) { + if ($this->isConfigEnabled($container, $config['csrf_protection'])) { if (!isset($config['session'])) { throw new \LogicException('CSRF protection needs that sessions are enabled.'); } @@ -178,28 +161,34 @@ private function registerFormConfiguration($config, ContainerBuilder $container, /** * Loads the ESI configuration. * - * @param array $config An ESI configuration array - * @param XmlFileLoader $loader An XmlFileLoader instance + * @param array $config A proxy configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance */ - private function registerEsiConfiguration(array $config, XmlFileLoader $loader) + private function registerEsiConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { - if (!empty($config['enabled'])) { - $loader->load('esi.xml'); + if (!$this->isConfigEnabled($container, $config)) { + return; } + + $loader->load('esi.xml'); } /** * Loads the router proxy configuration. * - * @param array $config A proxy configuration array - * @param XmlFileLoader $loader An XmlFileLoader instance + * @param array $config A proxy configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance */ private function registerRouterProxyConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { - if (!empty($config['enabled'])) { - $loader->load('proxy.xml'); - $container->setParameter('http_content_renderer.proxy_path', $config['path']); + if (!$this->isConfigEnabled($container, $config)) { + return; } + + $loader->load('proxy.xml'); + $container->setParameter('http_content_renderer.proxy_path', $config['path']); } /** @@ -213,6 +202,10 @@ private function registerRouterProxyConfiguration(array $config, ContainerBuilde */ private function registerProfilerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { + if (!$this->isConfigEnabled($container, $config)) { + return; + } + $loader->load('profiling.xml'); $loader->load('collectors.xml'); @@ -272,6 +265,10 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ */ private function registerRouterConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { + if (!$this->isConfigEnabled($container, $config)) { + return; + } + $loader->load('routing.xml'); $container->setParameter('router.resource', $config['resource']); @@ -530,61 +527,63 @@ private function createPackageDefinition(ContainerBuilder $container, array $htt */ private function registerTranslatorConfiguration(array $config, ContainerBuilder $container) { - if (!empty($config['enabled'])) { - // Use the "real" translator instead of the identity default - $container->setAlias('translator', 'translator.default'); - $translator = $container->findDefinition('translator.default'); - $translator->addMethodCall('setFallbackLocale', array($config['fallback'])); - - // Discover translation directories - $dirs = array(); - if (class_exists('Symfony\Component\Validator\Validator')) { - $r = new \ReflectionClass('Symfony\Component\Validator\Validator'); - - $dirs[] = dirname($r->getFilename()).'/Resources/translations'; - } - if (class_exists('Symfony\Component\Form\Form')) { - $r = new \ReflectionClass('Symfony\Component\Form\Form'); + if (!$this->isConfigEnabled($container, $config)) { + return; + } - $dirs[] = dirname($r->getFilename()).'/Resources/translations'; - } - if (class_exists('Symfony\Component\Security\Core\Exception\AuthenticationException')) { - $r = new \ReflectionClass('Symfony\Component\Security\Core\Exception\AuthenticationException'); + // Use the "real" translator instead of the identity default + $container->setAlias('translator', 'translator.default'); + $translator = $container->findDefinition('translator.default'); + $translator->addMethodCall('setFallbackLocale', array($config['fallback'])); - $dirs[] = dirname($r->getFilename()).'/../../Resources/translations'; - } - $overridePath = $container->getParameter('kernel.root_dir').'/Resources/%s/translations'; - foreach ($container->getParameter('kernel.bundles') as $bundle => $class) { - $reflection = new \ReflectionClass($class); - if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/translations')) { - $dirs[] = $dir; - } - if (is_dir($dir = sprintf($overridePath, $bundle))) { - $dirs[] = $dir; - } + // Discover translation directories + $dirs = array(); + if (class_exists('Symfony\Component\Validator\Validator')) { + $r = new \ReflectionClass('Symfony\Component\Validator\Validator'); + + $dirs[] = dirname($r->getFilename()).'/Resources/translations'; + } + if (class_exists('Symfony\Component\Form\Form')) { + $r = new \ReflectionClass('Symfony\Component\Form\Form'); + + $dirs[] = dirname($r->getFilename()).'/Resources/translations'; + } + if (class_exists('Symfony\Component\Security\Core\Exception\AuthenticationException')) { + $r = new \ReflectionClass('Symfony\Component\Security\Core\Exception\AuthenticationException'); + + $dirs[] = dirname($r->getFilename()).'/../../Resources/translations'; + } + $overridePath = $container->getParameter('kernel.root_dir').'/Resources/%s/translations'; + foreach ($container->getParameter('kernel.bundles') as $bundle => $class) { + $reflection = new \ReflectionClass($class); + if (is_dir($dir = dirname($reflection->getFilename()).'/Resources/translations')) { + $dirs[] = $dir; } - if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/translations')) { + if (is_dir($dir = sprintf($overridePath, $bundle))) { $dirs[] = $dir; } + } + if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/translations')) { + $dirs[] = $dir; + } - // Register translation resources - if ($dirs) { - foreach ($dirs as $dir) { - $container->addResource(new DirectoryResource($dir)); - } - $finder = Finder::create() - ->files() - ->filter(function (\SplFileInfo $file) { - return 2 === substr_count($file->getBasename(), '.') && preg_match('/\.\w+$/', $file->getBasename()); - }) - ->in($dirs) - ; - - foreach ($finder as $file) { - // filename is domain.locale.format - list($domain, $locale, $format) = explode('.', $file->getBasename(), 3); - $translator->addMethodCall('addResource', array($format, (string) $file, $locale, $domain)); - } + // Register translation resources + if ($dirs) { + foreach ($dirs as $dir) { + $container->addResource(new DirectoryResource($dir)); + } + $finder = Finder::create() + ->files() + ->filter(function (\SplFileInfo $file) { + return 2 === substr_count($file->getBasename(), '.') && preg_match('/\.\w+$/', $file->getBasename()); + }) + ->in($dirs) + ; + + foreach ($finder as $file) { + // filename is domain.locale.format + list($domain, $locale, $format) = explode('.', $file->getBasename(), 3); + $translator->addMethodCall('addResource', array($format, (string) $file, $locale, $domain)); } } } @@ -598,6 +597,10 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder */ private function registerValidationConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { + if (!$this->isConfigEnabled($container, $config)) { + return; + } + $loader->load('validator.xml'); $container->setParameter('validator.translation_domain', $config['translation_domain']); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_annotations.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_annotations.php index 75c8f48d49b57..d0c1fdab52d96 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_annotations.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/validation_annotations.php @@ -3,7 +3,7 @@ $container->loadFromExtension('framework', array( 'secret' => 's3cr3t', 'validation' => array( - 'enabled' => true, + 'enabled' => true, 'enable_annotations' => true, ), )); diff --git a/src/Symfony/Component/Config/CHANGELOG.md b/src/Symfony/Component/Config/CHANGELOG.md index 1478ca1403ef9..6cd50d37be5ad 100644 --- a/src/Symfony/Component/Config/CHANGELOG.md +++ b/src/Symfony/Component/Config/CHANGELOG.md @@ -4,6 +4,9 @@ CHANGELOG 2.2.0 ----- + * [BC BREAK] changed ArrayNodeDefinition::canBeEnabled() and ArrayNodeDefinition::canBeDisabled() + to set the defaults when the node is not set - the methods were ineffective + if ArrayNodeDefinition::setDefaultsIfNotSet() was not explicitely called. * added a `normalizeKeys()` method for array nodes (to avoid key normalization) * added numerical type handling for config definitions * added convenience methods for optional configuration sections to ArrayNodeDefinition diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php index f40a934d82b84..91525336eb247 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -221,6 +221,7 @@ public function canBeUnset($allow = true) public function canBeEnabled() { $this + ->addDefaultsIfNotSet() ->treatFalseLike(array('enabled' => false)) ->treatTrueLike(array('enabled' => true)) ->treatNullLike(array('enabled' => true)) @@ -242,6 +243,7 @@ public function canBeEnabled() public function canBeDisabled() { $this + ->addDefaultsIfNotSet() ->treatFalseLike(array('enabled' => false)) ->treatTrueLike(array('enabled' => true)) ->treatNullLike(array('enabled' => true)) diff --git a/src/Symfony/Component/DependencyInjection/Extension/Extension.php b/src/Symfony/Component/DependencyInjection/Extension/Extension.php index 7a1ba0faf2d19..bc1eaa1ffac81 100644 --- a/src/Symfony/Component/DependencyInjection/Extension/Extension.php +++ b/src/Symfony/Component/DependencyInjection/Extension/Extension.php @@ -13,6 +13,7 @@ use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\Definition\Processor; @@ -105,4 +106,21 @@ final protected function processConfiguration(ConfigurationInterface $configurat return $processor->processConfiguration($configuration, $configs); } + + /** + * @param ContainerBuilder $container + * @param array $config + * + * @return Boolean Whether the configuration is enabled + * + * @throws InvalidArgumentException When the config is not enableable + */ + protected function isConfigEnabled(ContainerBuilder $container, array $config) + { + if (!array_key_exists('enabled', $config)) { + throw new InvalidArgumentException("The config array has no 'enabled' key."); + } + + return (Boolean) $container->getParameterBag()->resolveValue($config['enabled']); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Extension/ExtensionTest.php b/src/Symfony/Component/DependencyInjection/Tests/Extension/ExtensionTest.php new file mode 100644 index 0000000000000..e35bbd5d3c8ad --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Extension/ExtensionTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Extension; + +class ExtensionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getResolvedEnabledFixtures + */ + public function testIsConfigEnabledReturnsTheResolvedValue($enabled) + { + $pb = $this->getMockBuilder('Symfony\Component\DependencyInjection\ParameterBag\ParameterBag') + ->setMethods(array('resolveValue')) + ->getMock() + ; + + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder') + ->setMethods(array('getParameterBag')) + ->getMock() + ; + + $pb->expects($this->once()) + ->method('resolveValue') + ->with($this->equalTo($enabled)) + ->will($this->returnValue($enabled)) + ; + + $container->expects($this->once()) + ->method('getParameterBag') + ->will($this->returnValue($pb)) + ; + + $extension = $this->getMockBuilder('Symfony\Component\DependencyInjection\Extension\Extension') + ->setMethods(array()) + ->getMockForAbstractClass() + ; + + $r = new \ReflectionMethod('Symfony\Component\DependencyInjection\Extension\Extension', 'isConfigEnabled'); + $r->setAccessible(true); + + $r->invoke($extension, $container, array('enabled' => $enabled)); + } + + public function getResolvedEnabledFixtures() + { + return array( + array(true), + array(false) + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The config array has no 'enabled' key. + */ + public function testIsConfigEnabledOnNonEnableableConfig() + { + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder') + ->getMock() + ; + + $extension = $this->getMockBuilder('Symfony\Component\DependencyInjection\Extension\Extension') + ->setMethods(array()) + ->getMockForAbstractClass() + ; + + $r = new \ReflectionMethod('Symfony\Component\DependencyInjection\Extension\Extension', 'isConfigEnabled'); + $r->setAccessible(true); + + $r->invoke($extension, $container, array()); + } +} From ccbabffbf1a2e0fe022cc78261b86effc53668cc Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 24 Jan 2013 09:29:26 +0100 Subject: [PATCH 04/12] implement feedback --- .../DependencyInjection/FrameworkExtension.php | 10 +++++----- src/Symfony/Component/Config/CHANGELOG.md | 2 +- src/Symfony/Component/DependencyInjection/CHANGELOG.md | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index dcf1f8c42e125..93b71d56fc712 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -153,8 +153,10 @@ private function registerFormConfiguration($config, ContainerBuilder $container, } $loader->load('form_csrf.xml'); - $container->setParameter('form.type_extension.csrf.enabled', $config['csrf_protection']['enabled']); + $container->setParameter('form.type_extension.csrf.enabled', true); $container->setParameter('form.type_extension.csrf.field_name', $config['csrf_protection']['field_name']); + } else { + $container->setParameter('form.type_extension.csrf.enabled', false); } } @@ -203,6 +205,8 @@ private function registerRouterProxyConfiguration(array $config, ContainerBuilde private function registerProfilerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { if (!$this->isConfigEnabled($container, $config)) { + $container->getDefinition('profiler')->addMethodCall('disable', array()); + return; } @@ -250,10 +254,6 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ } } } - - if (!$config['enabled']) { - $container->getDefinition('profiler')->addMethodCall('disable', array()); - } } /** diff --git a/src/Symfony/Component/Config/CHANGELOG.md b/src/Symfony/Component/Config/CHANGELOG.md index 6cd50d37be5ad..b58a86ce3348a 100644 --- a/src/Symfony/Component/Config/CHANGELOG.md +++ b/src/Symfony/Component/Config/CHANGELOG.md @@ -5,7 +5,7 @@ CHANGELOG ----- * [BC BREAK] changed ArrayNodeDefinition::canBeEnabled() and ArrayNodeDefinition::canBeDisabled() - to set the defaults when the node is not set - the methods were ineffective + to add the defaults when the node is not set - the methods were ineffective if ArrayNodeDefinition::setDefaultsIfNotSet() was not explicitely called. * added a `normalizeKeys()` method for array nodes (to avoid key normalization) * added numerical type handling for config definitions diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 686bd5656f866..4839bd8bee7b4 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 2.2.0 ----- + * added Extension::isConfigEnabled() to ease working with enableable configurations * added an Extension base class with sensible defaults to be used in conjunction with the Config component. * added PrependExtensionInterface (to be able to allow extensions to prepend From 1fb6ede8b156af39010221713846518c7d0227ee Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 25 Jan 2013 15:08:47 +0100 Subject: [PATCH 05/12] [FrameworkBundle] Fix the configuration info --- .../FrameworkBundle/DependencyInjection/Configuration.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 29822462b7723..7faeac7ff586c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -176,8 +176,10 @@ private function addRouterSection(ArrayNodeDefinition $rootNode) ->scalarNode('https_port')->defaultValue(443)->end() ->scalarNode('strict_requirements') ->info( - 'set to false to disable exceptions when a route is '. - 'generated with invalid parameters (and return null instead)' + "set to true to throw an exception when a parameter does not match the requirements\n". + "set to false to disable exceptions when a parameter does not match the requirements (and return null instead)\n". + "set to null to disable parameter checks against requirements\n". + "'true' is the preferred configuration in development mode, while 'false' or 'null' might be preferred in production" ) ->defaultTrue() ->end() From 30167b30dcf2bfa4c685c14b3092be116bcbadab Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 25 Jan 2013 16:11:26 +0100 Subject: [PATCH 06/12] fixes --- .../FrameworkExtension.php | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 93b71d56fc712..9c04a3b284e57 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -94,9 +94,12 @@ public function load(array $configs, ContainerBuilder $container) $this->registerEsiConfiguration($config['esi'], $container, $loader); $this->registerRouterProxyConfiguration($config['router_proxy'], $container, $loader); $this->registerProfilerConfiguration($config['profiler'], $container, $loader); - $this->registerRouterConfiguration($config['router'], $container, $loader); $this->registerTranslatorConfiguration($config['translator'], $container); + if (isset($config['router'])) { + $this->registerRouterConfiguration($config['router'], $container, $loader); + } + $this->registerAnnotationsConfiguration($config['annotations'], $container, $loader); $this->addClassesToCompile(array( @@ -204,12 +207,6 @@ private function registerRouterProxyConfiguration(array $config, ContainerBuilde */ private function registerProfilerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { - if (!$this->isConfigEnabled($container, $config)) { - $container->getDefinition('profiler')->addMethodCall('disable', array()); - - return; - } - $loader->load('profiling.xml'); $loader->load('collectors.xml'); @@ -254,6 +251,10 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ } } } + + if (!$this->isConfigEnabled($container, $config)) { + $container->getDefinition('profiler')->addMethodCall('disable', array()); + } } /** @@ -265,10 +266,6 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ */ private function registerRouterConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { - if (!$this->isConfigEnabled($container, $config)) { - return; - } - $loader->load('routing.xml'); $container->setParameter('router.resource', $config['resource']); From 49799605d1299da7783362532b37c9cdf245a023 Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 25 Jan 2013 16:11:54 +0100 Subject: [PATCH 07/12] tweak the enableable nodes --- .../Definition/Builder/ArrayNodeDefinition.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php index 91525336eb247..54678ad1e729d 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -214,7 +214,14 @@ public function canBeUnset($allow = true) /** * Adds an "enabled" boolean to enable the current section. * - * By default, the section is disabled. + * By default, the section is disabled. If any configuration is specified then + * the node will be automatically enabled: + * + * enableableArrayNode: {enabled: true} # The config is enabled & use the default values + * enableableArrayNode: ~ # The config is enabled & use the default values + * enableableArrayNode: true # The config is enabled & use the default values + * enableableArrayNode: {other: value} # The config is enabled & default values get overridden + * enableableArrayNode: false # The config is disabled * * @return ArrayNodeDefinition */ @@ -225,6 +232,13 @@ public function canBeEnabled() ->treatFalseLike(array('enabled' => false)) ->treatTrueLike(array('enabled' => true)) ->treatNullLike(array('enabled' => true)) + ->beforeNormalization() + ->ifArray() + ->then(function($v) { + $v['enabled'] = true; + return $v; + }) + ->end() ->children() ->booleanNode('enabled') ->defaultFalse() From f714095e81bae2a118f136ce011702bc0579d01a Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 28 Jan 2013 13:29:03 +0100 Subject: [PATCH 08/12] [Config] Tweak tests --- .../Config/Tests/Definition/ArrayNodeTest.php | 12 +++--------- .../Definition/Builder/ArrayNodeDefinitionTest.php | 5 ++++- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php index 27445ea339f2d..6d241cad5725a 100644 --- a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php @@ -25,19 +25,13 @@ public function testNormalizeThrowsExceptionWhenFalseIsNotAllowed() } /** - * normalize() should protect against child values with no corresponding node + * @expectedException Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage Unrecognized options "foo" under "root" */ public function testExceptionThrownOnUnrecognizedChild() { $node = new ArrayNode('root'); - - try { - $node->normalize(array('foo' => 'bar')); - $this->fail('An exception should have been throw for a bad child node'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Config\Definition\Exception\InvalidConfigurationException', $e); - $this->assertEquals('Unrecognized options "foo" under "root"', $e->getMessage()); - } + $node->normalize(array('foo' => 'bar')); } /** diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php index 4510da5df042f..f07878b47b199 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php @@ -62,7 +62,10 @@ public function providePrototypeNodeSpecificCalls() public function testConcreteNodeSpecificOption() { $node = new ArrayNodeDefinition('root'); - $node->addDefaultsIfNotSet()->prototype('array'); + $node + ->addDefaultsIfNotSet() + ->prototype('array') + ; $node->getNode(); } From ea94ab0594253af0a3762dc08af3ea261efedaf3 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 28 Jan 2013 13:58:01 +0100 Subject: [PATCH 09/12] [Config] Tweak the Processor --- .../Component/Config/Definition/Processor.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Config/Definition/Processor.php b/src/Symfony/Component/Config/Definition/Processor.php index 058c3bed34ec4..025e69378f948 100644 --- a/src/Symfony/Component/Config/Definition/Processor.php +++ b/src/Symfony/Component/Config/Definition/Processor.php @@ -79,18 +79,19 @@ public static function normalizeConfig($config, $key, $plural = null) $plural = $key.'s'; } - $values = array(); if (isset($config[$plural])) { - $values = $config[$plural]; - } elseif (isset($config[$key])) { + return $config[$plural]; + } + + if (isset($config[$key])) { if (is_string($config[$key]) || !is_int(key($config[$key]))) { // only one - $values = array($config[$key]); - } else { - $values = $config[$key]; + return array($config[$key]); } + + return $config[$key]; } - return $values; + return array(); } } From 1f540c056a1d717eceb68769db4c5e1b9c2a6ca9 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 28 Jan 2013 14:08:46 +0100 Subject: [PATCH 10/12] [ArrayNode] Allow explicitely disabling Enableable nodes, UTs --- .../Builder/ArrayNodeDefinition.php | 13 +++--- .../Builder/ArrayNodeDefinitionTest.php | 45 +++++++++++++++++++ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php index 54678ad1e729d..4db15480dc249 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -217,11 +217,12 @@ public function canBeUnset($allow = true) * By default, the section is disabled. If any configuration is specified then * the node will be automatically enabled: * - * enableableArrayNode: {enabled: true} # The config is enabled & use the default values - * enableableArrayNode: ~ # The config is enabled & use the default values - * enableableArrayNode: true # The config is enabled & use the default values - * enableableArrayNode: {other: value} # The config is enabled & default values get overridden - * enableableArrayNode: false # The config is disabled + * enableableArrayNode: {enabled: true, ...} # The config is enabled & default values get overridden + * enableableArrayNode: ~ # The config is enabled & use the default values + * enableableArrayNode: true # The config is enabled & use the default values + * enableableArrayNode: {other: value, ...} # The config is enabled & default values get overridden + * enableableArrayNode: {enabled: false, ...} # The config is disabled + * enableableArrayNode: false # The config is disabled * * @return ArrayNodeDefinition */ @@ -235,7 +236,7 @@ public function canBeEnabled() ->beforeNormalization() ->ifArray() ->then(function($v) { - $v['enabled'] = true; + $v['enabled'] = isset($v['enabled']) ? $v['enabled'] : true; return $v; }) ->end() diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php index f07878b47b199..06dacf2ab8cbd 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Config\Tests\Definition\Builder; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Processor; use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition; use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; @@ -152,6 +153,50 @@ public function testNestedPrototypedArrayNodes() $node->getNode(); } + public function testEnabledNodeDefaults() + { + $node = new ArrayNodeDefinition('root'); + $node + ->canBeEnabled() + ->children() + ->scalarNode('foo')->defaultValue('bar')->end() + ; + + $this->assertEquals(array('enabled' => false, 'foo' => 'bar'), $node->getNode()->getDefaultValue()); + } + + /** + * @dataProvider getEnableableNodeFixtures + */ + public function testTrueEnableEnabledNode($expected, $config, $message) + { + $processor = new Processor(); + $node = new ArrayNodeDefinition('root'); + $node + ->canBeEnabled() + ->children() + ->scalarNode('foo')->defaultValue('bar')->end() + ; + + $this->assertEquals( + $expected, + $processor->process($node->getNode(), $config), + $message + ); + } + + public function getEnableableNodeFixtures() + { + return array( + array(array('enabled' => true, 'foo' => 'bar'), array(true), 'true enables an enableable node'), + array(array('enabled' => true, 'foo' => 'bar'), array(null), 'null enables an enableable node'), + array(array('enabled' => true, 'foo' => 'bar'), array(array('enabled' => true)), 'An enableable node can be enabled'), + array(array('enabled' => true, 'foo' => 'baz'), array(array('foo' => 'baz')), 'any configuration enables an enableable node'), + array(array('enabled' => false, 'foo' => 'baz'), array(array('foo' => 'baz', 'enabled' => false)), 'An enableable node can be disabled'), + array(array('enabled' => false, 'foo' => 'bar'), array(false), 'false disables an enableable node'), + ); + } + protected function getField($object, $field) { $reflection = new \ReflectionProperty($object, $field); From c274274fc53a9fa5425e8d57d14ca3c2074b2a9b Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 28 Jan 2013 15:23:06 +0100 Subject: [PATCH 11/12] [FrameworkBundle] Update the UTs --- .../DependencyInjection/ConfigurationTest.php | 97 ++++++++++++++----- 1 file changed, 73 insertions(+), 24 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 77670cc005c0e..7f40dffd49bec 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -16,46 +16,41 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase { - /** - * @dataProvider getTestConfigTreeData - */ - public function testConfigTree($options, $results) + public function testDefaultConfig() { $processor = new Processor(); - $configuration = new Configuration(array()); - $config = $processor->processConfiguration($configuration, array($options)); + $config = $processor->processConfiguration(new Configuration(), array(array('secret' => 's3cr3t'))); - $this->assertEquals($results, $config); - } - - public function getTestConfigTreeData() - { - return array( - array(array('secret' => 's3cr3t'), array('secret' => 's3cr3t', 'trusted_proxies' => array(), 'trust_proxy_headers' => false, 'ide' => NULL, 'annotations' => array('cache' => 'file', 'file_cache_dir' => '%kernel.cache_dir%/annotations', 'debug' => '%kernel.debug%'), 'default_locale' => 'en', 'charset' => null)), + $this->assertEquals( + array_merge(array('secret' => 's3cr3t'), self::getBundleDefaultConfig()), + $config ); } /** * @dataProvider getTestValidTrustedProxiesData */ - public function testValidTrustedProxies($options, $results) + public function testValidTrustedProxies($trustedProxies, $processedProxies) { $processor = new Processor(); $configuration = new Configuration(array()); - $config = $processor->processConfiguration($configuration, array($options)); + $config = $processor->processConfiguration($configuration, array(array( + 'secret' => 's3cr3t', + 'trusted_proxies' => $trustedProxies + ))); - $this->assertEquals($results, $config); + $this->assertEquals($processedProxies, $config['trusted_proxies']); } public function getTestValidTrustedProxiesData() { return array( - array(array('secret' => 's3cr3t', 'trusted_proxies' => array('127.0.0.1')), array('secret' => 's3cr3t', 'trusted_proxies' => array('127.0.0.1'), 'trust_proxy_headers' => false, 'ide' => NULL, 'annotations' => array('cache' => 'file', 'file_cache_dir' => '%kernel.cache_dir%/annotations', 'debug' => '%kernel.debug%'), 'default_locale' => 'en', 'charset' => null)), - array(array('secret' => 's3cr3t', 'trusted_proxies' => array('::1')), array('secret' => 's3cr3t', 'trusted_proxies' => array('::1'), 'trust_proxy_headers' => false, 'ide' => NULL, 'annotations' => array('cache' => 'file', 'file_cache_dir' => '%kernel.cache_dir%/annotations', 'debug' => '%kernel.debug%'), 'default_locale' => 'en', 'charset' => null)), - array(array('secret' => 's3cr3t', 'trusted_proxies' => array('127.0.0.1', '::1')), array('secret' => 's3cr3t', 'trusted_proxies' => array('127.0.0.1', '::1'), 'trust_proxy_headers' => false, 'ide' => NULL, 'annotations' => array('cache' => 'file', 'file_cache_dir' => '%kernel.cache_dir%/annotations', 'debug' => '%kernel.debug%'), 'default_locale' => 'en', 'charset' => null)), - array(array('secret' => 's3cr3t', 'trusted_proxies' => null), array('secret' => 's3cr3t', 'trusted_proxies' => array(), 'trust_proxy_headers' => false, 'ide' => NULL, 'annotations' => array('cache' => 'file', 'file_cache_dir' => '%kernel.cache_dir%/annotations', 'debug' => '%kernel.debug%'), 'default_locale' => 'en', 'charset' => null)), - array(array('secret' => 's3cr3t', 'trusted_proxies' => false), array('secret' => 's3cr3t', 'trusted_proxies' => array(), 'trust_proxy_headers' => false, 'ide' => NULL, 'annotations' => array('cache' => 'file', 'file_cache_dir' => '%kernel.cache_dir%/annotations', 'debug' => '%kernel.debug%'), 'default_locale' => 'en', 'charset' => null)), - array(array('secret' => 's3cr3t', 'trusted_proxies' => array()), array('secret' => 's3cr3t', 'trusted_proxies' => array(), 'trust_proxy_headers' => false, 'ide' => NULL, 'annotations' => array('cache' => 'file', 'file_cache_dir' => '%kernel.cache_dir%/annotations', 'debug' => '%kernel.debug%'), 'default_locale' => 'en', 'charset' => null)), + array(array('127.0.0.1'), array('127.0.0.1')), + array(array('::1'), array('::1')), + array(array('127.0.0.1', '::1'), array('127.0.0.1', '::1')), + array(null, array()), + array(false, array()), + array(array(), array()), ); } @@ -66,7 +61,12 @@ public function testInvalidTypeTrustedProxies() { $processor = new Processor(); $configuration = new Configuration(array()); - $config = $processor->processConfiguration($configuration, array(array('secret' => 's3cr3t', 'trusted_proxies' => 'Not an IP address'))); + $processor->processConfiguration($configuration, array( + array( + 'secret' => 's3cr3t', + 'trusted_proxies' => 'Not an IP address' + ) + )); } /** @@ -76,6 +76,55 @@ public function testInvalidValueTrustedProxies() { $processor = new Processor(); $configuration = new Configuration(array()); - $config = $processor->processConfiguration($configuration, array(array('secret' => 's3cr3t', 'trusted_proxies' => array('Not an IP address')))); + $processor->processConfiguration($configuration, array( + array( + 'secret' => 's3cr3t', + 'trusted_proxies' => array('Not an IP address') + ) + )); + } + + protected static function getBundleDefaultConfig() + { + return array( + 'charset' => null, + 'trust_proxy_headers' => false, + 'trusted_proxies' => array(), + 'ide' => null, + 'default_locale' => 'en', + 'form' => array('enabled' => false), + 'csrf_protection' => array( + 'enabled' => false, + 'field_name' => '_token', + ), + 'esi' => array('enabled' => false), + 'router_proxy' => array( + 'enabled' => false, + 'path' => '/_proxy', + ), + 'profiler' => array( + 'enabled' => false, + 'only_exceptions' => false, + 'only_master_requests' => false, + 'dsn' => 'file:%kernel.cache_dir%/profiler', + 'username' => '', + 'password' => '', + 'lifetime' => 86400, + ), + 'translator' => array( + 'enabled' => false, + 'fallback' => 'en', + ), + 'validation' => array( + 'enabled' => false, + 'enable_annotations' => false, + 'translation_domain' => 'validators', + ), + 'annotations' => array( + 'cache' => 'file', + 'file_cache_dir' => '%kernel.cache_dir%/annotations', + 'debug' => '%kernel.debug%', + ), + ); } } From 58f221aa6b25775d5f4cda042454579bafb7935c Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 28 Jan 2013 17:51:07 +0100 Subject: [PATCH 12/12] [Config] Fix the changelog The features has not been released yet -> not a BC break --- src/Symfony/Component/Config/CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Config/CHANGELOG.md b/src/Symfony/Component/Config/CHANGELOG.md index b58a86ce3348a..59b30a3a7a6d8 100644 --- a/src/Symfony/Component/Config/CHANGELOG.md +++ b/src/Symfony/Component/Config/CHANGELOG.md @@ -4,9 +4,9 @@ CHANGELOG 2.2.0 ----- - * [BC BREAK] changed ArrayNodeDefinition::canBeEnabled() and ArrayNodeDefinition::canBeDisabled() - to add the defaults when the node is not set - the methods were ineffective - if ArrayNodeDefinition::setDefaultsIfNotSet() was not explicitely called. + * added ArrayNodeDefinition::canBeEnabled() and ArrayNodeDefinition::canBeDisabled() + to ease configuration when some sections are respectively disabled / enabled + by default. * added a `normalizeKeys()` method for array nodes (to avoid key normalization) * added numerical type handling for config definitions * added convenience methods for optional configuration sections to ArrayNodeDefinition