Skip to content

Commit ec0306c

Browse files
committed
[Config] Introduce NodeFinder to ease configuration tree manipulation for better reusability
- added class and methods docs - fixed code style - finalzed tests
1 parent 8e5b657 commit ec0306c

File tree

3 files changed

+166
-0
lines changed

3 files changed

+166
-0
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ public function __construct(?string $name, NodeParentInterface $parent = null)
4444
$this->name = $name;
4545
}
4646

47+
public function getName(): string
48+
{
49+
return $this->name;
50+
}
51+
4752
/**
4853
* Sets the parent node.
4954
*
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.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\Component\Config\Definition\Builder;
13+
14+
/**
15+
* This class finds nodes in a node tree.
16+
*
17+
* @author Jan Schädlich <jan.schaedlich@sensiolabs.de>
18+
*/
19+
class NodeFinder
20+
{
21+
/**
22+
* Finds a node defined by $nodePath within the given $rootNode.
23+
*
24+
* @param string $nodePath The path of the node to find. e.g "doctrine.orm.mappings"
25+
*/
26+
public function find(string $nodePath, NodeDefinition $rootNode): NodeDefinition
27+
{
28+
if (!$rootNode instanceof ArrayNodeDefinition) {
29+
if ($nodePath !== $rootNode->getName()) {
30+
throw new \RuntimeException(sprintf('Node with name "%s" does not exist in the given root node "%s"!', $nodePath, $rootNode->getName()));
31+
}
32+
33+
return $rootNode;
34+
}
35+
36+
$explodedNodePaths = explode('.', $nodePath);
37+
38+
if (!\array_key_exists($explodedNodePaths[0], $rootNode->getChildNodeDefinitions())) {
39+
throw new \RuntimeException(sprintf('Node with name "%s" does not exist in the given root node "%s"!', $explodedNodePaths[0], $rootNode->getName()));
40+
}
41+
42+
$node = $rootNode->getChildNodeDefinitions()[$explodedNodePaths[0]];
43+
44+
if (1 === \count($explodedNodePaths)) {
45+
return $node;
46+
}
47+
48+
array_shift($explodedNodePaths);
49+
50+
return $this->find(implode('.', $explodedNodePaths), $node);
51+
}
52+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.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\Component\Config\Tests\Definition\Builder;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
16+
use Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition;
17+
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
18+
use Symfony\Component\Config\Definition\Builder\NodeFinder;
19+
use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition;
20+
21+
/**
22+
* @author Jan Schädlich <jan.schaedlich@sensiolabs.de>
23+
*/
24+
class NodeFinderTest extends TestCase
25+
{
26+
public function testItShouldFindRootNode()
27+
{
28+
$rootNode = new ScalarNodeDefinition('root');
29+
30+
$nodeFinder = new NodeFinder();
31+
$foundNode = $nodeFinder->find('root', $rootNode);
32+
33+
$this->assertSame('root', $foundNode->getName());
34+
}
35+
36+
public function testItShouldThrowExceptionIfNodeDoesNotExistInScalarRootNode()
37+
{
38+
$this->expectException(\RuntimeException::class);
39+
$this->expectExceptionMessage('Node with name "child" does not exist in the given root node "root"!');
40+
41+
$rootNode = new ScalarNodeDefinition('root');
42+
43+
$nodeFinder = new NodeFinder();
44+
$nodeFinder->find('child', $rootNode);
45+
}
46+
47+
public function testItShouldThrowExceptionIfNodeDoesNotExistInArrayRootNode()
48+
{
49+
$this->expectException(\RuntimeException::class);
50+
$this->expectExceptionMessage('Node with name "child" does not exist in the given root node "root"!');
51+
52+
$rootNode = new ArrayNodeDefinition('root');
53+
$rootNode
54+
->children()
55+
->arrayNode('social_media_channels')->end()
56+
->end()
57+
;
58+
59+
$nodeFinder = new NodeFinder();
60+
$nodeFinder->find('child', $rootNode);
61+
}
62+
63+
public function testItShouldHandleComplexConfigurationProbably()
64+
{
65+
$rootNode = new ArrayNodeDefinition('root');
66+
$rootNode
67+
->children()
68+
->arrayNode('social_media_channels')
69+
->children()
70+
->booleanNode('enable')->end()
71+
->arrayNode('twitter')->end()
72+
->arrayNode('facebook')->end()
73+
->arrayNode('instagram')
74+
->children()
75+
->booleanNode('enable')->end()
76+
->arrayNode('accounts')->end()
77+
->end()
78+
->end()
79+
->end()
80+
->end()
81+
;
82+
83+
$nodeFinder = new NodeFinder();
84+
85+
$socialMediaChannelsNode = $nodeFinder->find('social_media_channels', $rootNode);
86+
$socialMediaChannelsEnableNode = $nodeFinder->find('social_media_channels.enable', $rootNode);
87+
88+
$twitterNode = $nodeFinder->find('social_media_channels.twitter', $rootNode);
89+
$facebookNode = $nodeFinder->find('social_media_channels.facebook', $rootNode);
90+
$instagramNode = $nodeFinder->find('social_media_channels.instagram', $rootNode);
91+
$instagramEnableNode = $nodeFinder->find('social_media_channels.instagram.enable', $rootNode);
92+
$instagramAccountsNode = $nodeFinder->find('social_media_channels.instagram.accounts', $rootNode);
93+
94+
$this->assertNode($socialMediaChannelsNode, 'social_media_channels', ArrayNodeDefinition::class);
95+
$this->assertNode($socialMediaChannelsEnableNode, 'enable', BooleanNodeDefinition::class);
96+
97+
$this->assertNode($twitterNode, 'twitter', ArrayNodeDefinition::class);
98+
$this->assertNode($facebookNode, 'facebook', ArrayNodeDefinition::class);
99+
$this->assertNode($instagramNode, 'instagram', ArrayNodeDefinition::class);
100+
$this->assertNode($instagramEnableNode, 'enable', BooleanNodeDefinition::class);
101+
$this->assertNode($instagramAccountsNode, 'accounts', ArrayNodeDefinition::class);
102+
}
103+
104+
private function assertNode(NodeDefinition $actualNode, string $expectedName, string $expectedType): void
105+
{
106+
$this->assertSame($expectedName, $actualNode->getName());
107+
$this->assertInstanceOf($expectedType, $actualNode);
108+
}
109+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy