@@ -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