diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 885752b89da6..7faeac7ff586 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,7 @@ private function addRouterProxySection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('router_proxy') ->info('proxy configuration for the HTTP content renderer') - ->canBeDisabled() + ->canBeEnabled() ->children() ->scalarNode('path')->defaultValue('/_proxy')->end() ->end() @@ -136,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() @@ -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() @@ -367,7 +369,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode) ->children() ->arrayNode('translator') ->info('translator configuration') - ->canBeDisabled() + ->canBeEnabled() ->children() ->scalarNode('fallback')->defaultValue('en')->end() ->end() @@ -382,7 +384,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 958c2ce35065..9c04a3b284e5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -81,39 +81,25 @@ 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['templating'])) { + $this->registerTemplatingConfiguration($config['templating'], $config['ide'], $container, $loader); } - if (isset($config['profiler'])) { - $this->registerProfilerConfiguration($config['profiler'], $container, $loader); - } + $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->registerTranslatorConfiguration($config['translator'], $container); 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->registerAnnotationsConfiguration($config['annotations'], $container, $loader); $this->addClassesToCompile(array( @@ -161,7 +147,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.'); } @@ -170,36 +156,44 @@ 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); } } /** * 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']); } /** @@ -258,7 +252,7 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ } } - if (!$config['enabled']) { + if (!$this->isConfigEnabled($container, $config)) { $container->getDefinition('profiler')->addMethodCall('disable', array()); } } @@ -530,61 +524,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 +594,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/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 77670cc005c0..7f40dffd49be 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%', + ), + ); } } 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 75c8f48d49b5..d0c1fdab52d9 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 1478ca1403ef..59b30a3a7a6d 100644 --- a/src/Symfony/Component/Config/CHANGELOG.md +++ b/src/Symfony/Component/Config/CHANGELOG.md @@ -4,6 +4,9 @@ CHANGELOG 2.2.0 ----- + * 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 diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php index f40a934d82b8..4db15480dc24 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -214,16 +214,32 @@ 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 & 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 */ public function canBeEnabled() { $this + ->addDefaultsIfNotSet() ->treatFalseLike(array('enabled' => false)) ->treatTrueLike(array('enabled' => true)) ->treatNullLike(array('enabled' => true)) + ->beforeNormalization() + ->ifArray() + ->then(function($v) { + $v['enabled'] = isset($v['enabled']) ? $v['enabled'] : true; + return $v; + }) + ->end() ->children() ->booleanNode('enabled') ->defaultFalse() @@ -242,6 +258,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/Config/Definition/Processor.php b/src/Symfony/Component/Config/Definition/Processor.php index 058c3bed34ec..025e69378f94 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(); } } diff --git a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php index 27445ea339f2..6d241cad5725 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 4510da5df042..06dacf2ab8cb 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; @@ -62,7 +63,10 @@ public function providePrototypeNodeSpecificCalls() public function testConcreteNodeSpecificOption() { $node = new ArrayNodeDefinition('root'); - $node->addDefaultsIfNotSet()->prototype('array'); + $node + ->addDefaultsIfNotSet() + ->prototype('array') + ; $node->getNode(); } @@ -149,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); diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 686bd5656f86..4839bd8bee7b 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 diff --git a/src/Symfony/Component/DependencyInjection/Extension/Extension.php b/src/Symfony/Component/DependencyInjection/Extension/Extension.php index e5c51a7b5892..bc1eaa1ffac8 100644 --- a/src/Symfony/Component/DependencyInjection/Extension/Extension.php +++ b/src/Symfony/Component/DependencyInjection/Extension/Extension.php @@ -12,6 +12,8 @@ namespace Symfony\Component\DependencyInjection\Extension; 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; @@ -62,26 +64,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 +99,28 @@ 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); + } + + /** + * @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 000000000000..e35bbd5d3c8a --- /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()); + } +}