Skip to content

Commit f849353

Browse files
committed
Merge remote branch 'weaverryan/config_definition_changes'
* weaverryan/config_definition_changes: [SecurityBundle] Allowing the main Configuration tree to allow "factories" without a validation exception. [SecurityBundle] Removing an old configuration key in a test - caught by the Configuration validation. [SecurityBundle] Adding the ignoreExtraFields option to the factories tree so that there aren't validation errors on all the other unknown fields. [Config] Adding an ignoreExtraKeys options, which allows you to let options simply be ignore without throwing a validation exception. [Config] Reverting some meaningless changes that are no longer needed to minimize the true diff of the changes. Increasing the test precision. [Config] Reverting the preventExtraKeys option. This is a revert of functionality that would have allowed "unnamed" children to be added to an array node. [Config] Moving the removal of the key attribute on ArrayNode onto the setKeyAttribute() method per Johannes. [Config] Renaming the key attribute removal property for consistency per Johannes' recommendation. Also fixing a PHPDoc typo per Stof. [SwiftmailerBundle] Removing unnecessary "enabled" key in a test. [FrameworkBundle] Removing the "namespace" key when it's defined in its own weird location in XML. This prevents that key, which we move in this same location, from looking like an invalid option during validation. [Config] Renaming NodeBuilder::nodeBuilder() to NodeBuilder::builder() due to the fact that PHP can get confused when you have a __construct() method *and* a method that has the same name as the class (looks like two constructors to PHP). [Config] Making the option to remove a key attribute optional. [Config] Renaming the NodeBuilder::addNodeBuilder() to simply NodeBuilder::nodeBuilder() to be consistent with the other names: node(), arrayNode(). [Config] Making changes per the recent movement of the Config builder into the Config component. [DependencyInjection] Renaming allowUnnamedChildren to preventExtraKeys. Also moved the place where validation of extra configs occurs. [DependencyInjection] Being sure to remove XML-remapped singular options and key attribute options after processing. [DependencyInjection] Adding a NodeBuilder::addNodeBuilder() method that helps achieve a fluid interface when a pre-built NodeBuilder needs to be added. [DependencyInjection] Initial implementation of an allowUnnamedChildren method on NodeBuilder. Also added an "extra field" exception.
2 parents 0834bf4 + c9406b6 commit f849353

File tree

10 files changed

+303
-9
lines changed

10 files changed

+303
-9
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ private function addValidationSection(NodeBuilder $rootNode)
195195
->ifTrue(function($v) { return is_array($v) && !empty($v['annotations']) && !empty($v['namespace']); })
196196
->then(function($v){
197197
$v['annotations'] = array('namespace' => $v['namespace']);
198+
unset($v['namespace']);
198199
return $v;
199200
})
200201
->end()

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Configuration.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public function getFactoryConfigTree()
2323

2424
return $tb
2525
->root('security', 'array')
26+
->ignoreExtraKeys()
2627
->fixXmlConfig('factory', 'factories')
2728
->arrayNode('factories')
2829
->prototype('scalar')->end()
@@ -39,6 +40,12 @@ public function getMainConfigTree(array $factories)
3940
$rootNode
4041
->scalarNode('access_denied_url')->defaultNull()->end()
4142
->scalarNode('session_fixation_strategy')->cannotBeEmpty()->defaultValue('migrate')->end()
43+
44+
// add a faux-entry for factories, so that no validation error is thrown
45+
->fixXmlConfig('factory', 'factories')
46+
->arrayNode('factories')
47+
->ignoreExtraKeys()
48+
->end()
4249
;
4350

4451
$this->addAclSection($rootNode);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
13+
14+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Configuration;
15+
use Symfony\Component\Config\Definition\Processor;
16+
17+
class ConfigurationTest extends \PHPUnit_Framework_TestCase
18+
{
19+
/**
20+
* The minimal, required config needed to not have any required validation
21+
* issues.
22+
*
23+
* @var array
24+
*/
25+
protected static $minimalConfig = array(
26+
'providers' => array(
27+
'stub' => array(),
28+
),
29+
'firewalls' => array(
30+
'stub' => array(),
31+
),
32+
);
33+
34+
/**
35+
* Test that the main tree is OK to be passed a factory or factories
36+
* key, without throwing any validation errors.
37+
*/
38+
public function testMainConfigTreeWithFactories()
39+
{
40+
$config = array_merge(self::$minimalConfig, array(
41+
'factory' => array('foo' => 'bar'),
42+
'factories' => array('lorem' => 'ipsum'),
43+
));
44+
45+
$configuration = new Configuration();
46+
$processor = new Processor();
47+
$tree = $configuration->getMainConfigTree(array());
48+
$config = $processor->process($tree, array($config));
49+
50+
$this->assertFalse(array_key_exists('factory', $config), 'The factory key is silently removed without an exception');
51+
$this->assertEquals(array(), $config['factories'], 'The factories key is jsut an empty array');
52+
}
53+
}

src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ security:
1818
users:
1919
foo: { password: foo, roles: 'ROLE_USER, ROLE_ADMIN' }
2020
basic:
21-
password_encoder: sha1
2221
users:
2322
foo: { password: 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33, roles: ROLE_SUPER_ADMIN }
2423
bar: { password: 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33, roles: [ROLE_USER, ROLE_ADMIN] }

src/Symfony/Bundle/SwiftmailerBundle/Tests/DependencyInjection/SwiftmailerExtensionTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public function testSpool()
3939
$container = new ContainerBuilder();
4040
$loader = new SwiftmailerExtension();
4141

42-
$loader->load(array(array('spool' => array ('enabled' => true))), $container);
42+
$loader->load(array(array('spool' => array())), $container);
4343
$this->assertEquals('swiftmailer.transport.spool', (string) $container->getAlias('swiftmailer.transport'));
4444
$this->assertEquals('swiftmailer.transport.smtp', (string) $container->getAlias('swiftmailer.transport.real'));
4545
}

src/Symfony/Component/Config/Definition/ArrayNode.php

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
2828
protected $children;
2929
protected $prototype;
3030
protected $keyAttribute;
31+
protected $removeKeyAttribute;
3132
protected $allowFalse;
3233
protected $allowNewKeys;
3334
protected $addIfNotSet;
3435
protected $minNumberOfElements;
3536
protected $performDeepMerging;
3637
protected $defaultValue;
38+
protected $ignoreExtraKeys;
3739

3840
/**
3941
* Constructor.
@@ -47,6 +49,7 @@ public function __construct($name, NodeInterface $parent = null)
4749

4850
$this->children = array();
4951
$this->xmlRemappings = array();
52+
$this->removeKeyAttribute = true;
5053
$this->allowFalse = false;
5154
$this->addIfNotSet = false;
5255
$this->allowNewKeys = true;
@@ -83,12 +86,25 @@ public function setMinNumberOfElements($number)
8386
* This is only relevant for XML configurations, and only in combination
8487
* with a prototype based node.
8588
*
86-
* @param string $attribute
89+
* For example, if "id" is the keyAttribute, then:
90+
*
91+
* array('id' => 'my_name', 'foo' => 'bar')
92+
*
93+
* becomes
94+
*
95+
* 'id' => array('foo' => 'bar')
96+
*
97+
* If $remove is false, the resulting array will still have the
98+
* "'id' => 'my_name'" item in it.
99+
*
100+
* @param string $attribute The name of the attribute to use as a key
101+
* @param Boolean $remove Whether or not to remove the key
87102
* @return void
88103
*/
89-
public function setKeyAttribute($attribute)
104+
public function setKeyAttribute($attribute, $remove = true)
90105
{
91106
$this->keyAttribute = $attribute;
107+
$this->removeKeyAttribute = $remove;
92108
}
93109

94110
/**
@@ -129,13 +145,23 @@ public function setAllowNewKeys($allow)
129145
/**
130146
* Sets if deep merging should occur.
131147
*
132-
* @param boolean $boolean
148+
* @param Boolean $boolean
133149
*/
134150
public function setPerformDeepMerging($boolean)
135151
{
136152
$this->performDeepMerging = (Boolean) $boolean;
137153
}
138154

155+
/**
156+
* Whether extra keys should just be ignore without an exception.
157+
*
158+
* @param Boolean $boolean To allow extra keys
159+
*/
160+
public function setIgnoreExtraKeys($boolean)
161+
{
162+
$this->ignoreExtraKeys = (Boolean) $boolean;
163+
}
164+
139165
/**
140166
* Sets the node Name.
141167
*
@@ -348,6 +374,7 @@ protected function normalizeValue($value)
348374
}
349375

350376
$value[$plural] = Extension::normalizeConfig($value, $singular, $plural);
377+
unset($value[$singular]);
351378
}
352379

353380
if (null !== $this->prototype) {
@@ -362,6 +389,11 @@ protected function normalizeValue($value)
362389
));
363390
} else if (isset($v[$this->keyAttribute])) {
364391
$k = $v[$this->keyAttribute];
392+
393+
// remove the key attribute if configured to
394+
if ($this->removeKeyAttribute) {
395+
unset($v[$this->keyAttribute]);
396+
}
365397
}
366398

367399
if (array_key_exists($k, $normalized)) {
@@ -391,6 +423,14 @@ protected function normalizeValue($value)
391423
}
392424

393425
$normalized[$name] = $child->normalize($value[$name]);
426+
unset($value[$name]);
427+
}
428+
429+
// if extra fields are present, throw exception
430+
if (count($value) && !$this->ignoreExtraKeys) {
431+
$msg = sprintf('Unrecognized options "%s" under "%s"', implode(', ', array_keys($value)), $this->getPath());
432+
433+
throw new InvalidConfigurationException($msg);
394434
}
395435

396436
return $normalized;

src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class NodeBuilder
2424
public $name;
2525
public $type;
2626
public $key;
27+
public $removeKeyItem;
2728
public $parent;
2829
public $children;
2930
public $prototype;
@@ -42,6 +43,7 @@ class NodeBuilder
4243
public $trueEquivalent;
4344
public $falseEquivalent;
4445
public $performDeepMerging;
46+
public $ignoreExtraKeys;
4547

4648
/**
4749
* Constructor
@@ -99,6 +101,29 @@ public function node($name, $type)
99101
return $this->children[$name] = $node;
100102
}
101103

104+
/**
105+
* Add a NodeBuilder instance directly.
106+
*
107+
* This helps achieve a fluid interface when a method on your Configuration
108+
* class returns a pre-build NodeBuilder instance on your behalf:
109+
*
110+
* $root = new NodeBuilder();
111+
* ->node('foo', 'scalar')
112+
* ->addNodeBuilder($this->getBarNodeBuilder())
113+
* ->node('baz', 'scalar')
114+
* ;
115+
*
116+
* @return Symfony\Component\DependencyInjection\Configuration\Builder\NodeBuilder This builder node
117+
*/
118+
public function builder(NodeBuilder $node)
119+
{
120+
$node->parent = $this;
121+
122+
$this->children[$node->name] = $node;
123+
124+
return $this;
125+
}
126+
102127
/**
103128
* Creates a child array node
104129
*
@@ -314,13 +339,33 @@ public function fixXmlConfig($singular, $plural = null)
314339
/**
315340
* Sets an attribute to use as key.
316341
*
317-
* @param string $name
342+
* This is useful when you have an indexed array that should be an
343+
* associative array. You can select an item from within the array
344+
* to be the key of the particular item. For example, if "id" is the
345+
* "key", then:
346+
*
347+
* array(
348+
* array('id' => 'my_name', 'foo' => 'bar'),
349+
* )
350+
*
351+
* becomes
352+
*
353+
* array(
354+
* 'id' => array('foo' => 'bar'),
355+
* )
356+
*
357+
* If you'd like "'id' => 'my_name'" to still be present in the resulting
358+
* array, then you can set the second argument of this method to false.
359+
*
360+
* @param string $name The name of the key
361+
* @param Boolean $removeKeyItem Whether or not the key item should be removed.
318362
*
319363
* @return Symfony\Component\Config\Definition\Builder\NodeBuilder
320364
*/
321-
public function useAttributeAsKey($name)
365+
public function useAttributeAsKey($name, $removeKeyItem = true)
322366
{
323367
$this->key = $name;
368+
$this->removeKeyItem = $removeKeyItem;
324369

325370
return $this;
326371
}
@@ -440,4 +485,21 @@ public function end()
440485
{
441486
return $this->parent;
442487
}
488+
489+
/**
490+
* Allows extra config keys to be specified under an array without
491+
* throwing an exception.
492+
*
493+
* Those config values are simply ignored. This should be used only
494+
* in special cases where you want to send an entire configuration
495+
* array through a special tree that processes only part of the array.
496+
*
497+
* @return Symfony\Component\Config\Definition\Builder\NodeBuilder
498+
*/
499+
public function ignoreExtraKeys()
500+
{
501+
$this->ignoreExtraKeys = true;
502+
503+
return $this;
504+
}
443505
}

src/Symfony/Component/Config/Definition/Builder/TreeBuilder.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,10 @@ protected function createArrayConfigNode(NodeBuilder $node)
163163
$configNode->addEquivalentValue(false, $node->falseEquivalent);
164164
$configNode->setPerformDeepMerging($node->performDeepMerging);
165165
$configNode->setRequired($node->required);
166+
$configNode->setIgnoreExtraKeys($node->ignoreExtraKeys);
166167

167168
if (null !== $node->key) {
168-
$configNode->setKeyAttribute($node->key);
169+
$configNode->setKeyAttribute($node->key, $node->removeKeyItem);
169170
}
170171

171172
if (true === $node->atLeastOne) {

0 commit comments

Comments
 (0)