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