Skip to content

Commit f36eb1d

Browse files
committed
NeonAdapter: processing of 'prevent merging' and 'entity to statement' moved to visitors
1 parent decab76 commit f36eb1d

File tree

1 file changed

+90
-45
lines changed

1 file changed

+90
-45
lines changed

src/DI/Config/Adapters/NeonAdapter.php

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

2930

3031
/**
@@ -41,61 +42,23 @@ public function load(string $file): array
4142
$decoder = new Neon\Decoder;
4243
$node = $decoder->parseToNode($input);
4344
$traverser = new Neon\Traverser;
45+
$node = $traverser->traverse($node, $this->deprecatedQuestionMarkVisitor(...));
4446
$node = $traverser->traverse($node, $this->firstClassCallableVisitor(...));
4547
$node = $traverser->traverse($node, $this->removeUnderscoreVisitor(...));
4648
$node = $traverser->traverse($node, $this->convertAtSignVisitor(...));
4749
$node = $traverser->traverse($node, $this->deprecatedParametersVisitor(...));
4850
$node = $traverser->traverse($node, $this->resolveConstantsVisitor(...));
49-
return $this->process((array) $node->toValue());
51+
$node = $traverser->traverse($node, $this->preventMergingVisitor(...));
52+
$this->connectParentsVisitor($traverser, $node);
53+
$node = $traverser->traverse($node, leave: $this->entityToExpressionVisitor(...));
54+
return (array) $node->toValue();
5055
}
5156

5257

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

10164

@@ -165,6 +128,71 @@ private function firstClassCallableVisitor(Node $node): void
165128
}
166129

167130

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

0 commit comments

Comments
 (0)