YamlReferenceDumper::writeNode()   F
last analyzed

Complexity

Conditions 33
Paths > 20000

Size

Total Lines 98
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 56
dl 0
loc 98
rs 0
c 0
b 0
f 0
cc 33
nc 76032
nop 4

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <[email protected]>
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\Dumper;
13
14
use Symfony\Component\Config\Definition\ArrayNode;
15
use Symfony\Component\Config\Definition\BaseNode;
16
use Symfony\Component\Config\Definition\ConfigurationInterface;
17
use Symfony\Component\Config\Definition\EnumNode;
18
use Symfony\Component\Config\Definition\NodeInterface;
19
use Symfony\Component\Config\Definition\PrototypedArrayNode;
20
use Symfony\Component\Config\Definition\ScalarNode;
21
use Symfony\Component\Yaml\Inline;
22
23
/**
24
 * Dumps a Yaml reference configuration for the given configuration/node instance.
25
 *
26
 * @author Kevin Bond <[email protected]>
27
 */
28
class YamlReferenceDumper
29
{
30
    private ?string $reference = null;
31
32
    public function dump(ConfigurationInterface $configuration): string
33
    {
34
        return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree());
35
    }
36
37
    public function dumpAtPath(ConfigurationInterface $configuration, string $path): string
38
    {
39
        $rootNode = $node = $configuration->getConfigTreeBuilder()->buildTree();
40
41
        foreach (explode('.', $path) as $step) {
42
            if (!$node instanceof ArrayNode) {
43
                throw new \UnexpectedValueException(\sprintf('Unable to find node at path "%s.%s".', $rootNode->getName(), $path));
44
            }
45
46
            /** @var NodeInterface[] $children */
47
            $children = $node instanceof PrototypedArrayNode ? $this->getPrototypeChildren($node) : $node->getChildren();
48
49
            foreach ($children as $child) {
50
                if ($child->getName() === $step) {
51
                    $node = $child;
52
53
                    continue 2;
54
                }
55
            }
56
57
            throw new \UnexpectedValueException(\sprintf('Unable to find node at path "%s.%s".', $rootNode->getName(), $path));
58
        }
59
60
        return $this->dumpNode($node);
61
    }
62
63
    public function dumpNode(NodeInterface $node): string
64
    {
65
        $this->reference = '';
66
        $this->writeNode($node);
67
        $ref = $this->reference;
68
        $this->reference = null;
69
70
        return $ref;
71
    }
72
73
    private function writeNode(NodeInterface $node, ?NodeInterface $parentNode = null, int $depth = 0, bool $prototypedArray = false): void
74
    {
75
        $comments = [];
76
        $default = '';
77
        $defaultArray = null;
78
        $children = null;
79
        $example = null;
80
        if ($node instanceof BaseNode) {
81
            $example = $node->getExample();
82
        }
83
84
        // defaults
85
        if ($node instanceof ArrayNode) {
86
            $children = $node->getChildren();
87
88
            if ($node instanceof PrototypedArrayNode) {
89
                $children = $this->getPrototypeChildren($node);
90
            }
91
92
            if (!$children && !($node->hasDefaultValue() && \count($defaultArray = $node->getDefaultValue()))) {
93
                $default = '[]';
94
            }
95
        } elseif ($node instanceof EnumNode) {
96
            $comments[] = 'One of '.$node->getPermissibleValues('; ');
97
            $default = $node->hasDefaultValue() ? Inline::dump($node->getDefaultValue()) : '~';
98
        } else {
99
            $default = '~';
100
101
            if ($node->hasDefaultValue()) {
102
                $default = $node->getDefaultValue();
103
104
                if (\is_array($default)) {
105
                    if (\count($defaultArray = $node->getDefaultValue())) {
106
                        $default = '';
107
                    } elseif (!\is_array($example)) {
108
                        $default = '[]';
109
                    }
110
                } else {
111
                    $default = Inline::dump($default);
112
                }
113
            }
114
        }
115
116
        // required?
117
        if ($node->isRequired()) {
118
            $comments[] = 'Required';
119
        }
120
121
        // deprecated?
122
        if ($node instanceof BaseNode && $node->isDeprecated()) {
123
            $deprecation = $node->getDeprecation($node->getName(), $parentNode ? $parentNode->getPath() : $node->getPath());
124
            $comments[] = \sprintf('Deprecated (%s)', ($deprecation['package'] || $deprecation['version'] ? "Since {$deprecation['package']} {$deprecation['version']}: " : '').$deprecation['message']);
125
        }
126
127
        // example
128
        if ($example && !\is_array($example)) {
129
            $comments[] = 'Example: '.Inline::dump($example);
130
        }
131
132
        $default = '' != (string) $default ? ' '.$default : '';
0 ignored issues
show
Bug introduced by
Are you sure $default of type array|mixed|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

132
        $default = '' != (string) $default ? ' './** @scrutinizer ignore-type */ $default : '';
Loading history...
133
        $comments = \count($comments) ? '# '.implode(', ', $comments) : '';
134
135
        $key = $prototypedArray ? '-' : $node->getName().':';
136
        $text = rtrim(\sprintf('%-21s%s %s', $key, $default, $comments), ' ');
137
138
        if ($node instanceof BaseNode && $info = $node->getInfo()) {
139
            $this->writeLine('');
140
            // indenting multi-line info
141
            $info = str_replace("\n", \sprintf("\n%".($depth * 4).'s# ', ' '), $info);
142
            $this->writeLine('# '.$info, $depth * 4);
143
        }
144
145
        $this->writeLine($text, $depth * 4);
146
147
        // output defaults
148
        if ($defaultArray) {
149
            $this->writeLine('');
150
151
            $message = \count($defaultArray) > 1 ? 'Defaults' : 'Default';
152
153
            $this->writeLine('# '.$message.':', $depth * 4 + 4);
154
155
            $this->writeArray($defaultArray, $depth + 1);
156
        }
157
158
        if (\is_array($example)) {
159
            $this->writeLine('');
160
161
            $message = \count($example) > 1 ? 'Examples' : 'Example';
162
163
            $this->writeLine('# '.$message.':', $depth * 4 + 4);
164
165
            $this->writeArray(array_map(Inline::dump(...), $example), $depth + 1, true);
166
        }
167
168
        if ($children) {
169
            foreach ($children as $childNode) {
170
                $this->writeNode($childNode, $node, $depth + 1, $node instanceof PrototypedArrayNode && !$node->getKeyAttribute());
171
            }
172
        }
173
    }
174
175
    /**
176
     * Outputs a single config reference line.
177
     */
178
    private function writeLine(string $text, int $indent = 0): void
179
    {
180
        $indent = \strlen($text) + $indent;
181
        $format = '%'.$indent.'s';
182
183
        $this->reference .= \sprintf($format, $text)."\n";
184
    }
185
186
    private function writeArray(array $array, int $depth, bool $asComment = false): void
187
    {
188
        $isIndexed = array_is_list($array);
0 ignored issues
show
Bug introduced by
The function array_is_list was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

188
        $isIndexed = /** @scrutinizer ignore-call */ array_is_list($array);
Loading history...
189
190
        foreach ($array as $key => $value) {
191
            if (\is_array($value)) {
192
                $val = '';
193
            } else {
194
                $val = $value;
195
            }
196
197
            $prefix = $asComment ? '# ' : '';
198
199
            if ($isIndexed) {
200
                $this->writeLine($prefix.'- '.$val, $depth * 4);
201
            } else {
202
                $this->writeLine(\sprintf('%s%-20s %s', $prefix, $key.':', $val), $depth * 4);
203
            }
204
205
            if (\is_array($value)) {
206
                $this->writeArray($value, $depth + 1, $asComment);
207
            }
208
        }
209
    }
210
211
    private function getPrototypeChildren(PrototypedArrayNode $node): array
212
    {
213
        $prototype = $node->getPrototype();
214
        $key = $node->getKeyAttribute();
215
216
        // Do not expand prototype if it isn't an array node nor uses attribute as key
217
        if (!$key && !$prototype instanceof ArrayNode) {
218
            return $node->getChildren();
219
        }
220
221
        if ($prototype instanceof ArrayNode) {
222
            $keyNode = new ArrayNode($key, $node);
223
            $children = $prototype->getChildren();
224
225
            if ($prototype instanceof PrototypedArrayNode && $prototype->getKeyAttribute()) {
226
                $children = $this->getPrototypeChildren($prototype);
227
            }
228
229
            // add children
230
            foreach ($children as $childNode) {
231
                $keyNode->addChild($childNode);
232
            }
233
        } else {
234
            $keyNode = new ScalarNode($key, $node);
235
        }
236
237
        $info = 'Prototype';
238
        if (null !== $prototype->getInfo()) {
239
            $info .= ': '.$prototype->getInfo();
240
        }
241
        $keyNode->setInfo($info);
242
243
        return [$key => $keyNode];
244
    }
245
}
246