12
12
namespace Symfony \Component \Config \Builder ;
13
13
14
14
use Symfony \Component \Config \Definition \ArrayNode ;
15
+ use Symfony \Component \Config \Definition \BaseNode ;
15
16
use Symfony \Component \Config \Definition \BooleanNode ;
17
+ use Symfony \Component \Config \Definition \Builder \ExprBuilder ;
16
18
use Symfony \Component \Config \Definition \ConfigurationInterface ;
17
19
use Symfony \Component \Config \Definition \EnumNode ;
18
20
use Symfony \Component \Config \Definition \Exception \InvalidConfigurationException ;
@@ -90,6 +92,7 @@ private function writeClasses(): void
90
92
$ this ->buildConstructor ($ class );
91
93
$ this ->buildToArray ($ class );
92
94
$ this ->buildSetExtraKey ($ class );
95
+ $ this ->buildSetScalarValue ($ class );
93
96
94
97
file_put_contents ($ this ->getFullPath ($ class ), $ class ->build ());
95
98
}
@@ -127,23 +130,42 @@ private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $n
127
130
{
128
131
$ childClass = new ClassBuilder ($ namespace , $ node ->getName ());
129
132
$ childClass ->setAllowExtraKeys ($ node ->shouldIgnoreExtraKeys ());
133
+ $ childClass ->setValuesTypeHint ($ nodeType = $ this ->getParameterType ($ node ));
130
134
$ class ->addRequire ($ childClass );
131
135
$ this ->classes [] = $ childClass ;
132
136
133
137
$ property = $ class ->addProperty ($ node ->getName (), $ childClass ->getFqcn ());
138
+
134
139
$ body = '
135
- public function NAME(array $value = []): CLASS
140
+ public function NAME(PARAM_TYPE $value = []): RETURN_TYPEHINT
136
141
{
137
142
if (null === $this->PROPERTY) {
138
143
$this->PROPERTY = new CLASS($value);
139
144
} elseif ([] !== $value) {
140
145
throw new InvalidConfigurationException( \'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME(). \');
141
146
}
142
-
147
+ ' ;
148
+ if ('array ' === $ nodeType ) {
149
+ $ body .= '
143
150
return $this->PROPERTY;
144
151
} ' ;
152
+ } else {
153
+ $ body .= '
154
+ if (\is_array($value)) {
155
+ return $this->PROPERTY;
156
+ }
157
+
158
+ return $this;
159
+ } ' ;
160
+ }
161
+
145
162
$ class ->addUse (InvalidConfigurationException::class);
146
- $ class ->addMethod ($ node ->getName (), $ body , ['PROPERTY ' => $ property ->getName (), 'CLASS ' => $ childClass ->getFqcn ()]);
163
+ $ class ->addMethod ($ node ->getName (), $ body , [
164
+ 'PROPERTY ' => $ property ->getName (),
165
+ 'CLASS ' => $ childClass ->getFqcn (),
166
+ 'RETURN_TYPEHINT ' => 'array ' === $ nodeType ? $ childClass ->getFqcn () : 'self| ' .$ childClass ->getFqcn (),
167
+ 'PARAM_TYPE ' => $ nodeType ,
168
+ ]);
147
169
148
170
$ this ->buildNode ($ node , $ childClass , $ this ->getSubNamespace ($ childClass ));
149
171
}
@@ -174,39 +196,53 @@ private function handlePrototypedArrayNode(PrototypedArrayNode $node, ClassBuild
174
196
$ prototype = $ node ->getPrototype ();
175
197
$ methodName = $ name ;
176
198
177
- $ parameterType = $ this ->getParameterType ($ prototype );
178
- if (null !== $ parameterType || $ prototype instanceof ScalarNode) {
199
+ $ nodeType = $ this ->getParameterType ($ node );
200
+ $ prototypeType = $ this ->getParameterType ($ prototype );
201
+
202
+ $ isObject = $ prototype instanceof ArrayNode && (!$ prototype instanceof PrototypedArrayNode || !$ prototype ->getPrototype () instanceof ScalarNode);
203
+ if (!$ isObject ) {
179
204
$ class ->addUse (ParamConfigurator::class);
180
205
$ property = $ class ->addProperty ($ node ->getName ());
181
206
if (null === $ key = $ node ->getKeyAttribute ()) {
182
207
// This is an array of values; don't use singular name
208
+ $ nodeTypeWithoutArray = implode ('| ' , array_filter (explode ('| ' , $ nodeType ), fn ($ type ) => $ type !== 'array ' ));
183
209
$ body = '
184
210
/**
185
- * @param ParamConfigurator|list<ParamConfigurator|TYPE> $value
211
+ * @param ParamConfigurator|list<ParamConfigurator|PROTOTYPE_TYPE>EXTRA_TYPE $value
186
212
*
187
213
* @return $this
188
214
*/
189
- public function NAME(ParamConfigurator|array $value): static
215
+ public function NAME(PARAM_TYPE $value): static
190
216
{
191
217
$this->PROPERTY = $value;
192
218
193
219
return $this;
194
220
} ' ;
195
221
196
- $ class ->addMethod ($ node ->getName (), $ body , ['PROPERTY ' => $ property ->getName (), 'TYPE ' => '' === $ parameterType ? 'mixed ' : $ parameterType ]);
222
+ $ class ->addMethod ($ node ->getName (), $ body , [
223
+ 'PROPERTY ' => $ property ->getName (),
224
+ 'PROTOTYPE_TYPE ' => $ prototypeType ,
225
+ 'EXTRA_TYPE ' => $ nodeTypeWithoutArray ? '| ' .$ nodeTypeWithoutArray : '' ,
226
+ 'PARAM_TYPE ' => $ nodeType === 'mixed ' ? 'mixed ' : 'ParamConfigurator| ' .$ nodeType ,
227
+ ]);
197
228
} else {
198
229
$ body = '
199
230
/**
200
231
* @return $this
201
232
*/
202
- public function NAME(string $VAR, TYPE $VALUE): static
233
+ public function NAME(string $VAR, PARAM_TYPE $VALUE): static
203
234
{
204
235
$this->PROPERTY[$VAR] = $VALUE;
205
236
206
237
return $this;
207
238
} ' ;
208
239
209
- $ class ->addMethod ($ methodName , $ body , ['PROPERTY ' => $ property ->getName (), 'TYPE ' => '' === $ parameterType ? 'mixed ' : 'ParamConfigurator| ' .$ parameterType , 'VAR ' => '' === $ key ? 'key ' : $ key , 'VALUE ' => 'value ' === $ key ? 'data ' : 'value ' ]);
240
+ $ class ->addMethod ($ methodName , $ body , [
241
+ 'PROPERTY ' => $ property ->getName (),
242
+ 'VAR ' => '' === $ key ? 'key ' : $ key ,
243
+ 'VALUE ' => 'value ' === $ key ? 'data ' : 'value ' ,
244
+ 'PARAM_TYPE ' => $ prototypeType === 'mixed ' ? 'mixed ' : 'ParamConfigurator| ' .$ prototypeType ,
245
+ ]);
210
246
}
211
247
212
248
return ;
@@ -216,20 +252,40 @@ public function NAME(string $VAR, TYPE $VALUE): static
216
252
if ($ prototype instanceof ArrayNode) {
217
253
$ childClass ->setAllowExtraKeys ($ prototype ->shouldIgnoreExtraKeys ());
218
254
}
255
+ $ childClass ->setValuesTypeHint ($ nodeType );
256
+
219
257
$ class ->addRequire ($ childClass );
220
258
$ this ->classes [] = $ childClass ;
221
259
$ property = $ class ->addProperty ($ node ->getName (), $ childClass ->getFqcn ().'[] ' );
222
260
223
261
if (null === $ key = $ node ->getKeyAttribute ()) {
224
262
$ body = '
225
- public function NAME(array $value = []): CLASS
226
- {
263
+ public function NAME(PARAM_TYPE $value = []): RETURN_TYPEHINT
264
+ { ' ;
265
+ if ('array ' === $ nodeType ) {
266
+ $ body .= '
227
267
return $this->PROPERTY[] = new CLASS($value);
228
268
} ' ;
229
- $ class ->addMethod ($ methodName , $ body , ['PROPERTY ' => $ property ->getName (), 'CLASS ' => $ childClass ->getFqcn ()]);
269
+ } else {
270
+ $ body .= '
271
+ $this->PROPERTY[] = $p = new CLASS($value);
272
+ if (\is_array($value)) {
273
+ return $p;
274
+ }
275
+
276
+ return $this;
277
+ } ' ;
278
+ }
279
+
280
+ $ class ->addMethod ($ methodName , $ body , [
281
+ 'PROPERTY ' => $ property ->getName (),
282
+ 'CLASS ' => $ childClass ->getFqcn (),
283
+ 'RETURN_TYPEHINT ' => 'array ' === $ nodeType ? $ childClass ->getFqcn () : 'self| ' .$ childClass ->getFqcn (),
284
+ 'PARAM_TYPE ' => $ nodeType ,
285
+ ]);
230
286
} else {
231
287
$ body = '
232
- public function NAME(string $VAR, array $VALUE = []): CLASS
288
+ public function NAME(string $VAR, PARAM_TYPE $VALUE = []): CLASS
233
289
{
234
290
if (!isset($this->PROPERTY[$VAR])) {
235
291
return $this->PROPERTY[$VAR] = new CLASS($value);
@@ -241,7 +297,13 @@ public function NAME(string $VAR, array $VALUE = []): CLASS
241
297
throw new InvalidConfigurationException( \'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME(). \');
242
298
} ' ;
243
299
$ class ->addUse (InvalidConfigurationException::class);
244
- $ class ->addMethod ($ methodName , $ body , ['PROPERTY ' => $ property ->getName (), 'CLASS ' => $ childClass ->getFqcn (), 'VAR ' => '' === $ key ? 'key ' : $ key , 'VALUE ' => 'value ' === $ key ? 'data ' : 'value ' ]);
300
+ $ class ->addMethod ($ methodName , $ body , [
301
+ 'PROPERTY ' => $ property ->getName (),
302
+ 'CLASS ' => $ childClass ->getFqcn (),
303
+ 'VAR ' => '' === $ key ? 'key ' : $ key ,
304
+ 'VALUE ' => 'value ' === $ key ? 'data ' : 'value ' ,
305
+ 'PARAM_TYPE ' => $ nodeType ,
306
+ ]);
245
307
}
246
308
247
309
$ this ->buildNode ($ prototype , $ childClass , $ namespace .'\\' .$ childClass ->getName ());
@@ -267,35 +329,48 @@ public function NAME($value): static
267
329
$ class ->addMethod ($ node ->getName (), $ body , ['PROPERTY ' => $ property ->getName (), 'COMMENT ' => $ comment ]);
268
330
}
269
331
270
- private function getParameterType (NodeInterface $ node ): ? string
332
+ private function getParameterType (NodeInterface $ node ): string
271
333
{
334
+ $ paramTypes = [];
335
+ if ($ node instanceof BaseNode) {
336
+ $ types = $ node ->getNormalizedTypes ();
337
+ if (\in_array (ExprBuilder::TYPE_ANY , $ types , true )) {
338
+ return 'mixed ' ;
339
+ }
340
+
341
+ if (\in_array (ExprBuilder::TYPE_STRING , $ types , true )) {
342
+ $ paramTypes [] = 'string ' ;
343
+ }
344
+ if (\in_array (ExprBuilder::TYPE_NULL , $ types , true )) {
345
+ $ paramTypes [] = 'null ' ;
346
+ }
347
+ }
348
+
272
349
if ($ node instanceof BooleanNode) {
273
- return 'bool ' ;
350
+ $ paramTypes [] = 'bool ' ;
274
351
}
275
352
276
353
if ($ node instanceof IntegerNode) {
277
- return 'int ' ;
354
+ $ paramTypes [] = 'int ' ;
278
355
}
279
356
280
357
if ($ node instanceof FloatNode) {
281
- return 'float ' ;
358
+ $ paramTypes [] = 'float ' ;
282
359
}
283
360
284
361
if ($ node instanceof EnumNode) {
285
- return ' ' ;
362
+ $ paramTypes [] = ' mixed ' ;
286
363
}
287
364
288
- if ($ node instanceof PrototypedArrayNode && $ node ->getPrototype () instanceof ScalarNode) {
289
- // This is just an array of variables
290
- return 'array ' ;
365
+ if ($ node instanceof ArrayNode) {
366
+ $ paramTypes [] = 'array ' ;
291
367
}
292
368
293
369
if ($ node instanceof VariableNode) {
294
- // mixed
295
- return '' ;
370
+ $ paramTypes [] = 'mixed ' ;
296
371
}
297
372
298
- return null ;
373
+ return implode ( ' | ' , $ paramTypes ) ;
299
374
}
300
375
301
376
private function getComment (VariableNode $ node ): string
@@ -319,7 +394,7 @@ private function getComment(VariableNode $node): string
319
394
}, $ node ->getValues ())))."\n" ;
320
395
} else {
321
396
$ parameterType = $ this ->getParameterType ($ node );
322
- if (null === $ parameterType || '' === $ parameterType ) {
397
+ if (null === $ parameterType ) {
323
398
$ parameterType = 'mixed ' ;
324
399
}
325
400
$ comment .= ' * @param ParamConfigurator| ' .$ parameterType .' $value ' ."\n" ;
@@ -356,7 +431,15 @@ private function getSingularName(PrototypedArrayNode $node): string
356
431
357
432
private function buildToArray (ClassBuilder $ class ): void
358
433
{
359
- $ body = '$output = []; ' ;
434
+ $ body = '' ;
435
+ if ($ class ->shouldAllowScalaraValues ()) {
436
+ $ body = 'if ($this->_value !== []) {
437
+ return $this->_value;
438
+ }
439
+
440
+ ' ;
441
+ }
442
+ $ body .= '$output = []; ' ;
360
443
foreach ($ class ->getProperties () as $ p ) {
361
444
$ code = '$this->PROPERTY ' ;
362
445
if (null !== $ p ->getType ()) {
@@ -374,9 +457,10 @@ private function buildToArray(ClassBuilder $class): void
374
457
}
375
458
376
459
$ extraKeys = $ class ->shouldAllowExtraKeys () ? ' + $this->_extraKeys ' : '' ;
460
+ $ nodeType = $ class ->getValuesTypeHint ();
377
461
378
462
$ class ->addMethod ('toArray ' , '
379
- public function NAME(): array
463
+ public function NAME() ' .( $ nodeType ? ' : ' . $ nodeType : '' ). '
380
464
{
381
465
' .$ body .'
382
466
@@ -418,10 +502,21 @@ private function buildConstructor(ClassBuilder $class): void
418
502
$ class ->addUse (InvalidConfigurationException::class);
419
503
}
420
504
505
+ if ($ class ->shouldAllowScalaraValues ()) {
506
+ $ body = '
507
+ if (!\is_array($value)) {
508
+ $this->_value = $value;
509
+
510
+ return;
511
+ }
512
+ $this->_value = [];
513
+ ' .$ body ;
514
+ }
515
+
516
+ $ nodeType = $ class ->getValuesTypeHint ();
421
517
$ class ->addMethod ('__construct ' , '
422
- public function __construct(array $value = [])
423
- {
424
- ' .$ body .'
518
+ public function __construct( ' .($ nodeType ).' $value = [])
519
+ { ' .$ body .'
425
520
} ' );
426
521
}
427
522
@@ -453,6 +548,15 @@ public function NAME(string $key, mixed $value): static
453
548
} ' );
454
549
}
455
550
551
+ private function buildSetScalarValue (ClassBuilder $ class ): void
552
+ {
553
+ if (!$ class ->shouldAllowScalaraValues ()) {
554
+ return ;
555
+ }
556
+
557
+ $ class ->addProperty ('_value ' );
558
+ }
559
+
456
560
private function getSubNamespace (ClassBuilder $ rootClass ): string
457
561
{
458
562
return sprintf ('%s \\%s ' , $ rootClass ->getNamespace (), substr ($ rootClass ->getName (), 0 , -6 ));
0 commit comments