Skip to content

Commit 29d4397

Browse files
committed
NeonAdapter: processing of 'prevent merging' and 'entity to statement' moved to visitors
1 parent 41ae2b4 commit 29d4397

File tree

2 files changed

+91
-46
lines changed

2 files changed

+91
-46
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"php": "8.1 - 8.4",
1919
"ext-tokenizer": "*",
2020
"ext-ctype": "*",
21-
"nette/neon": "^3.3 || ^4.0",
21+
"nette/neon": "^3.3.3 || ^4.0",
2222
"nette/php-generator": "^4.1.6",
2323
"nette/robot-loader": "^4.0",
2424
"nette/schema": "^1.2.5",

src/DI/Config/Adapters/NeonAdapter.php

Lines changed: 90 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ final class NeonAdapter implements Nette\DI\Config\Adapter
2424
{
2525
private const PreventMergingSuffix = '!';
2626
private string $file;
27+
private \WeakMap $parents;
2728

2829

2930
/**
@@ -40,61 +41,23 @@ public function load(string $file): array
4041
$decoder = new Neon\Decoder;
4142
$node = $decoder->parseToNode($input);
4243
$traverser = new Neon\Traverser;
44+
$node = $traverser->traverse($node, $this->deprecatedQuestionMarkVisitor(...));
4345
$node = $traverser->traverse($node, $this->firstClassCallableVisitor(...));
4446
$node = $traverser->traverse($node, $this->removeUnderscoreVisitor(...));
4547
$node = $traverser->traverse($node, $this->convertAtSignVisitor(...));
4648
$node = $traverser->traverse($node, $this->deprecatedParametersVisitor(...));
4749
$node = $traverser->traverse($node, $this->resolveConstantsVisitor(...));
48-
return $this->process((array) $node->toValue());
50+
$node = $traverser->traverse($node, $this->preventMergingVisitor(...));
51+
$this->connectParentsVisitor($traverser, $node);
52+
$node = $traverser->traverse($node, leave: $this->entityToExpressionVisitor(...));
53+
return (array) $node->toValue();
4954
}
5055

5156

52-
/** @throws Nette\InvalidStateException */
57+
/** @deprecated */
5358
public function process(array $arr): array
5459
{
55-
$res = [];
56-
foreach ($arr as $key => $val) {
57-
if (is_string($key) && str_ends_with($key, self::PreventMergingSuffix)) {
58-
if (!is_array($val) && $val !== null) {
59-
throw new Nette\DI\InvalidConfigurationException(sprintf(
60-
"Replacing operator is available only for arrays, item '%s' is not array (used in '%s')",
61-
$key,
62-
$this->file,
63-
));
64-
}
65-
66-
$key = substr($key, 0, -1);
67-
$val[DI\Config\Helpers::PREVENT_MERGING] = true;
68-
}
69-
70-
if (is_array($val)) {
71-
$val = $this->process($val);
72-
73-
} elseif ($val instanceof Neon\Entity) {
74-
if ($val->value === Neon\Neon::CHAIN) {
75-
$tmp = null;
76-
foreach ($this->process($val->attributes) as $st) {
77-
$tmp = new Statement(
78-
$tmp === null ? $st->getEntity() : [$tmp, ltrim(implode('::', (array) $st->getEntity()), ':')],
79-
$st->arguments,
80-
);
81-
}
82-
83-
$val = $tmp;
84-
} else {
85-
$tmp = $this->process([$val->value]);
86-
if (is_string($tmp[0]) && str_contains($tmp[0], '?')) {
87-
throw new Nette\DI\InvalidConfigurationException("Operator ? is deprecated in config file (used in '$this->file')");
88-
}
89-
90-
$val = new Statement($tmp[0], $this->process($val->attributes));
91-
}
92-
}
93-
94-
$res[$key] = $val;
95-
}
96-
97-
return $res;
60+
return $arr;
9861
}
9962

10063

@@ -164,6 +127,71 @@ private function firstClassCallableVisitor(Node $node): void
164127
}
165128

166129

130+
private function preventMergingVisitor(Node $node): void
131+
{
132+
if ($node instanceof Node\ArrayItemNode
133+
&& $node->key instanceof Node\LiteralNode
134+
&& is_string($node->key->value)
135+
&& str_ends_with($node->key->value, self::PreventMergingSuffix)
136+
) {
137+
if ($node->value instanceof Node\LiteralNode && $node->value->value === null) {
138+
$node->value = new Node\InlineArrayNode('[');
139+
} elseif (!$node->value instanceof Node\ArrayNode) {
140+
throw new Nette\DI\InvalidConfigurationException(sprintf(
141+
"Replacing operator is available only for arrays, item '%s' is not array (used in '%s')",
142+
$node->key->value,
143+
$this->file,
144+
));
145+
}
146+
147+
$node->key->value = substr($node->key->value, 0, -1);
148+
$node->value->items[] = $item = new Node\ArrayItemNode;
149+
$item->key = new Node\LiteralNode(DI\Config\Helpers::PREVENT_MERGING);
150+
$item->value = new Node\LiteralNode(true);
151+
}
152+
}
153+
154+
155+
private function deprecatedQuestionMarkVisitor(Node $node): void
156+
{
157+
if ($node instanceof Node\EntityNode
158+
&& ($node->value instanceof Node\LiteralNode || $node->value instanceof Node\StringNode)
159+
&& is_string($node->value->value)
160+
&& str_contains($node->value->value, '?')
161+
) {
162+
throw new Nette\DI\InvalidConfigurationException("Operator ? is deprecated in config file (used in '$this->file')");
163+
}
164+
}
165+
166+
167+
private function entityToExpressionVisitor(Node $node): Node
168+
{
169+
if ($node instanceof Node\EntityChainNode) {
170+
return new Node\LiteralNode($this->buildExpression($node->chain));
171+
172+
} elseif (
173+
$node instanceof Node\EntityNode
174+
&& !$this->parents[$node] instanceof Node\EntityChainNode
175+
) {
176+
return new Node\LiteralNode($this->buildExpression([$node]));
177+
178+
} else {
179+
return $node;
180+
}
181+
}
182+
183+
184+
private function buildExpression(array $chain): Statement
185+
{
186+
$node = array_pop($chain);
187+
$entity = $node->toValue();
188+
return new Statement(
189+
$chain ? [$this->buildExpression($chain), ltrim($entity->value, ':')] : $entity->value,
190+
$entity->attributes,
191+
);
192+
}
193+
194+
167195
private function removeUnderscoreVisitor(Node $node): void
168196
{
169197
if (!$node instanceof Node\EntityNode) {
@@ -240,4 +268,21 @@ private function resolveConstantsVisitor(Node $node): void
240268
}
241269
}
242270
}
271+
272+
273+
private function connectParentsVisitor(Neon\Traverser $traverser, Node $node): void
274+
{
275+
$this->parents = new \WeakMap;
276+
$stack = [];
277+
$traverser->traverse(
278+
$node,
279+
enter: function (Node $node) use (&$stack) {
280+
$this->parents[$node] = end($stack);
281+
$stack[] = $node;
282+
},
283+
leave: function () use (&$stack) {
284+
array_pop($stack);
285+
},
286+
);
287+
}
243288
}

0 commit comments

Comments
 (0)