bug #40603 [Config] Fixed support for nodes not extending BaseNode (Nyholm)

This PR was merged into the 4.4 branch.

Discussion
----------

[Config] Fixed support for nodes not extending BaseNode

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       |
| License       | MIT
| Doc PR        |

Too many times we assume that a `NodeInterface` extends `BaseNode`. This PR adds a small test with a new `CustomNodeDefinition` and `CustomNode` that implements `NodeInterface`.

Commits
-------

68276562c3 Fixed support for nodes not extending BaseNode
This commit is contained in:
Nicolas Grekas 2021-04-01 12:23:12 +02:00
commit bd90627814
9 changed files with 121 additions and 46 deletions

View File

@ -68,7 +68,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
/**
* Retrieves the children of this node.
*
* @return array The children
* @return array<string, NodeInterface>
*/
public function getChildren()
{

View File

@ -127,7 +127,9 @@ abstract class NodeDefinition implements NodeParentInterface
}
$node = $this->createNode();
$node->setAttributes($this->attributes);
if ($node instanceof BaseNode) {
$node->setAttributes($this->attributes);
}
return $node;
}

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Config\Definition\Dumper;
use Symfony\Component\Config\Definition\ArrayNode;
use Symfony\Component\Config\Definition\BaseNode;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\EnumNode;
use Symfony\Component\Config\Definition\NodeInterface;
@ -126,50 +127,52 @@ class XmlReferenceDumper
// get attributes and elements
foreach ($children as $child) {
if (!$child instanceof ArrayNode) {
// get attributes
// metadata
$name = str_replace('_', '-', $child->getName());
$value = '%%%%not_defined%%%%'; // use a string which isn't used in the normal world
// comments
$comments = [];
if ($info = $child->getInfo()) {
$comments[] = $info;
}
if ($example = $child->getExample()) {
$comments[] = 'Example: '.$example;
}
if ($child->isRequired()) {
$comments[] = 'Required';
}
if ($child->isDeprecated()) {
$comments[] = sprintf('Deprecated (%s)', $child->getDeprecationMessage($child->getName(), $node->getPath()));
}
if ($child instanceof EnumNode) {
$comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues()));
}
if (\count($comments)) {
$rootAttributeComments[$name] = implode(";\n", $comments);
}
// default values
if ($child->hasDefaultValue()) {
$value = $child->getDefaultValue();
}
// append attribute
$rootAttributes[$name] = $value;
} else {
if ($child instanceof ArrayNode) {
// get elements
$rootChildren[] = $child;
continue;
}
// get attributes
// metadata
$name = str_replace('_', '-', $child->getName());
$value = '%%%%not_defined%%%%'; // use a string which isn't used in the normal world
// comments
$comments = [];
if ($child instanceof BaseNode && $info = $child->getInfo()) {
$comments[] = $info;
}
if ($child instanceof BaseNode && $example = $child->getExample()) {
$comments[] = 'Example: '.$example;
}
if ($child->isRequired()) {
$comments[] = 'Required';
}
if ($child instanceof BaseNode && $child->isDeprecated()) {
$comments[] = sprintf('Deprecated (%s)', $child->getDeprecationMessage($child->getName(), $node->getPath()));
}
if ($child instanceof EnumNode) {
$comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues()));
}
if (\count($comments)) {
$rootAttributeComments[$name] = implode(";\n", $comments);
}
// default values
if ($child->hasDefaultValue()) {
$value = $child->getDefaultValue();
}
// append attribute
$rootAttributes[$name] = $value;
}
}

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Config\Definition\Dumper;
use Symfony\Component\Config\Definition\ArrayNode;
use Symfony\Component\Config\Definition\BaseNode;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\EnumNode;
use Symfony\Component\Config\Definition\NodeInterface;
@ -76,7 +77,10 @@ class YamlReferenceDumper
$default = '';
$defaultArray = null;
$children = null;
$example = $node->getExample();
$example = null;
if ($node instanceof BaseNode) {
$example = $node->getExample();
}
// defaults
if ($node instanceof ArrayNode) {
@ -123,7 +127,7 @@ class YamlReferenceDumper
}
// deprecated?
if ($node->isDeprecated()) {
if ($node instanceof BaseNode && $node->isDeprecated()) {
$comments[] = sprintf('Deprecated (%s)', $node->getDeprecationMessage($node->getName(), $parentNode ? $parentNode->getPath() : $node->getPath()));
}
@ -138,7 +142,7 @@ class YamlReferenceDumper
$key = $prototypedArray ? '-' : $node->getName().':';
$text = rtrim(sprintf('%-21s%s %s', $key, $default, $comments), ' ');
if ($info = $node->getInfo()) {
if ($node instanceof BaseNode && $info = $node->getInfo()) {
$this->writeLine('');
// indenting multi-line info
$info = str_replace("\n", sprintf("\n%".($depth * 4).'s# ', ' '), $info);

View File

@ -57,6 +57,7 @@ class XmlReferenceDumperTest extends TestCase
node-with-a-looong-name=""
enum-with-default="this"
enum=""
custom-node="true"
>
<!-- some info -->

View File

@ -137,6 +137,7 @@ acme_root:
# Prototype
name: []
custom_node: true
EOL;
}

View File

@ -0,0 +1,49 @@
<?php
namespace Symfony\Component\Config\Tests\Fixtures\Configuration;
use Symfony\Component\Config\Definition\NodeInterface;
class CustomNode implements NodeInterface
{
public function getName()
{
return 'custom_node';
}
public function getPath()
{
return 'custom';
}
public function isRequired()
{
return false;
}
public function hasDefaultValue()
{
return true;
}
public function getDefaultValue()
{
return true;
}
public function normalize($value)
{
return $value;
}
public function merge($leftSide, $rightSide)
{
return array_merge($leftSide, $rightSide);
}
public function finalize($value)
{
return $value;
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace Symfony\Component\Config\Tests\Fixtures\Configuration;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
class CustomNodeDefinition extends NodeDefinition
{
protected function createNode()
{
return new CustomNode();
}
}

View File

@ -93,6 +93,7 @@ class ExampleConfiguration implements ConfigurationInterface
->end()
->end()
->end()
->append(new CustomNodeDefinition('acme'))
->end()
;