-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Config] Improve prototype nodes usability, error messages, extensibility #3403
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bca2b0e
cba2c33
675e5eb
bc122bd
4feba09
b269e27
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition | |
protected $key; | ||
protected $removeKeyItem; | ||
protected $addDefaults; | ||
protected $addDefaultChildren; | ||
protected $nodeBuilder; | ||
|
||
/** | ||
|
@@ -42,6 +43,7 @@ public function __construct($name, NodeParentInterface $parent = null) | |
|
||
$this->children = array(); | ||
$this->addDefaults = false; | ||
$this->addDefaultChildren = false; | ||
$this->allowNewKeys = true; | ||
$this->atLeastOne = false; | ||
$this->allowEmptyValue = true; | ||
|
@@ -98,6 +100,22 @@ public function addDefaultsIfNotSet() | |
return $this; | ||
} | ||
|
||
/** | ||
* Adds children with a default value when none are defined. | ||
* | ||
* @param integer|string|array|null $children The number of children|The child name|The children names to be added | ||
* | ||
* This method is applicable to prototype nodes only. | ||
* | ||
* @return ArrayNodeDefinition | ||
*/ | ||
public function addDefaultChildrenIfNoneSet($children = null) | ||
{ | ||
$this->addDefaultChildren = $children; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Requires the node to have at least one element. | ||
* | ||
|
@@ -260,40 +278,21 @@ protected function getNodeBuilder() | |
protected function createNode() | ||
{ | ||
if (null === $this->prototype) { | ||
if (null !== $this->key) { | ||
throw new InvalidDefinitionException( | ||
sprintf('%s::useAttributeAsKey() is not applicable to concrete nodes.', __CLASS__) | ||
); | ||
} | ||
|
||
if (true === $this->atLeastOne) { | ||
throw new InvalidDefinitionException( | ||
sprintf('%s::requiresAtLeastOneElement() is not applicable to concrete nodes.', __CLASS__) | ||
); | ||
} | ||
$node = new ArrayNode($this->name, $this->parent); | ||
|
||
if ($this->default) { | ||
throw new InvalidDefinitionException( | ||
sprintf('%s::defaultValue() is not applicable to concrete nodes.', __CLASS__) | ||
); | ||
} | ||
$this->validateConcreteNode($node); | ||
|
||
$node = new ArrayNode($this->name, $this->parent); | ||
$node->setAddIfNotSet($this->addDefaults); | ||
|
||
foreach ($this->children as $child) { | ||
$child->parent = $node; | ||
$node->addChild($child->getNode()); | ||
} | ||
} else { | ||
if ($this->addDefaults) { | ||
throw new InvalidDefinitionException( | ||
sprintf('%s::addDefaultsIfNotSet() is not applicable to prototype nodes.', __CLASS__) | ||
); | ||
} | ||
|
||
$node = new PrototypedArrayNode($this->name, $this->parent); | ||
|
||
$this->validatePrototypeNode($node); | ||
|
||
if (null !== $this->key) { | ||
$node->setKeyAttribute($this->key, $this->removeKeyItem); | ||
} | ||
|
@@ -306,6 +305,13 @@ protected function createNode() | |
$node->setDefaultValue($this->defaultValue); | ||
} | ||
|
||
if (false !== $this->addDefaultChildren) { | ||
$node->setAddChildrenIfNoneSet($this->addDefaultChildren); | ||
if ($this->prototype instanceof static && null === $this->prototype->prototype) { | ||
$this->prototype->addDefaultsIfNotSet(); | ||
} | ||
} | ||
|
||
$this->prototype->parent = $node; | ||
$node->setPrototype($this->prototype->getNode()); | ||
} | ||
|
@@ -335,4 +341,77 @@ protected function createNode() | |
return $node; | ||
} | ||
|
||
/** | ||
* Validate the confifuration of a concrete node. | ||
* | ||
* @param NodeInterface $node The related node | ||
* | ||
* @throws InvalidDefinitionException When an error is detected in the configuration | ||
*/ | ||
protected function validateConcreteNode(ArrayNode $node) | ||
{ | ||
$path = $node->getPath(); | ||
|
||
if (null !== $this->key) { | ||
throw new InvalidDefinitionException( | ||
sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s"', $path) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicb What do you think about moving those messages into that exception ? (kinda like Doctrine2 does) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you have some example ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be useful (I don't have a strong opinion here).
I think there are already some inconsistencies in the way excs are managed in this component (SPL vs component specific). This might be the subject for an other PR ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yea, this was moreover look-up after at your last commits with new "errors" along the DI component ;-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @stloyd Sf2 never uses the Doctrine way to create the exceptions There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @stof And ? There is no info that Sf2 must not use Doctrine way to create the exceptions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, Symfony must not use the Doctrine way of creating exceptions. |
||
); | ||
} | ||
|
||
if (true === $this->atLeastOne) { | ||
throw new InvalidDefinitionException( | ||
sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s"', $path) | ||
); | ||
} | ||
|
||
if ($this->default) { | ||
throw new InvalidDefinitionException( | ||
sprintf('->defaultValue() is not applicable to concrete nodes at path "%s"', $path) | ||
); | ||
} | ||
|
||
if (false !== $this->addDefaultChildren) { | ||
throw new InvalidDefinitionException( | ||
sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s"', $path) | ||
); | ||
} | ||
} | ||
|
||
/** | ||
* Validate the configuration of a prototype node. | ||
* | ||
* @param NodeInterface $node The related node | ||
* | ||
* @throws InvalidDefinitionException When an error is detected in the configuration | ||
*/ | ||
protected function validatePrototypeNode(PrototypedArrayNode $node) | ||
{ | ||
$path = $node->getPath(); | ||
|
||
if ($this->addDefaults) { | ||
throw new InvalidDefinitionException( | ||
sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s"', $path) | ||
); | ||
} | ||
|
||
if (false !== $this->addDefaultChildren) { | ||
if ($this->default) { | ||
throw new InvalidDefinitionException( | ||
sprintf('A default value and default children might not be used together at path "%s"', $path) | ||
); | ||
} | ||
|
||
if (null !== $this->key && (null === $this->addDefaultChildren || is_integer($this->addDefaultChildren) && $this->addDefaultChildren > 0)) { | ||
throw new InvalidDefinitionException( | ||
sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s"', $path) | ||
); | ||
} | ||
|
||
if (null === $this->key && (is_string($this->addDefaultChildren) || is_array($this->addDefaultChildren))) { | ||
throw new InvalidDefinitionException( | ||
sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s"', $path) | ||
); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method seems to miss a check whether "addDefaultChildrenIfNotSet()" is called as it makes only sense for arrays which contain a prototype, no?