Total Complexity | 88 |
Total Lines | 564 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like ConfigBuilderGenerator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ConfigBuilderGenerator, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
34 | class ConfigBuilderGenerator implements ConfigBuilderGeneratorInterface |
||
35 | { |
||
36 | /** |
||
37 | * @var ClassBuilder[] |
||
38 | */ |
||
39 | private array $classes = []; |
||
40 | |||
41 | public function __construct( |
||
42 | private string $outputDir, |
||
43 | ) { |
||
44 | } |
||
45 | |||
46 | /** |
||
47 | * @return \Closure that will return the root config class |
||
48 | */ |
||
49 | public function build(ConfigurationInterface $configuration): \Closure |
||
50 | { |
||
51 | $this->classes = []; |
||
52 | |||
53 | $rootNode = $configuration->getConfigTreeBuilder()->buildTree(); |
||
54 | $rootClass = new ClassBuilder('Symfony\\Config', $rootNode->getName()); |
||
55 | |||
56 | $path = $this->getFullPath($rootClass); |
||
57 | if (!is_file($path)) { |
||
58 | // Generate the class if the file not exists |
||
59 | $this->classes[] = $rootClass; |
||
60 | $this->buildNode($rootNode, $rootClass, $this->getSubNamespace($rootClass)); |
||
61 | $rootClass->addImplements(ConfigBuilderInterface::class); |
||
62 | $rootClass->addMethod('getExtensionAlias', ' |
||
63 | public function NAME(): string |
||
64 | { |
||
65 | return \'ALIAS\'; |
||
66 | }', ['ALIAS' => $rootNode->getPath()]); |
||
67 | |||
68 | $this->writeClasses(); |
||
69 | } |
||
70 | |||
71 | return function () use ($path, $rootClass) { |
||
72 | require_once $path; |
||
73 | $className = $rootClass->getFqcn(); |
||
74 | |||
75 | return new $className(); |
||
76 | }; |
||
77 | } |
||
78 | |||
79 | private function getFullPath(ClassBuilder $class): string |
||
80 | { |
||
81 | $directory = $this->outputDir.\DIRECTORY_SEPARATOR.$class->getDirectory(); |
||
82 | if (!is_dir($directory)) { |
||
83 | @mkdir($directory, 0777, true); |
||
|
|||
84 | } |
||
85 | |||
86 | return $directory.\DIRECTORY_SEPARATOR.$class->getFilename(); |
||
87 | } |
||
88 | |||
89 | private function writeClasses(): void |
||
90 | { |
||
91 | foreach ($this->classes as $class) { |
||
92 | $this->buildConstructor($class); |
||
93 | $this->buildToArray($class); |
||
94 | if ($class->getProperties()) { |
||
95 | $class->addProperty('_usedProperties', null, '[]'); |
||
96 | } |
||
97 | $this->buildSetExtraKey($class); |
||
98 | |||
99 | file_put_contents($this->getFullPath($class), $class->build()); |
||
100 | } |
||
101 | |||
102 | $this->classes = []; |
||
103 | } |
||
104 | |||
105 | private function buildNode(NodeInterface $node, ClassBuilder $class, string $namespace): void |
||
106 | { |
||
107 | if (!$node instanceof ArrayNode) { |
||
108 | throw new \LogicException('The node was expected to be an ArrayNode. This Configuration includes an edge case not supported yet.'); |
||
109 | } |
||
110 | |||
111 | foreach ($node->getChildren() as $child) { |
||
112 | match (true) { |
||
113 | $child instanceof ScalarNode => $this->handleScalarNode($child, $class), |
||
114 | $child instanceof PrototypedArrayNode => $this->handlePrototypedArrayNode($child, $class, $namespace), |
||
115 | $child instanceof VariableNode => $this->handleVariableNode($child, $class), |
||
116 | $child instanceof ArrayNode => $this->handleArrayNode($child, $class, $namespace), |
||
117 | default => throw new \RuntimeException(\sprintf('Unknown node "%s".', $child::class)), |
||
118 | }; |
||
119 | } |
||
120 | } |
||
121 | |||
122 | private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $namespace): void |
||
123 | { |
||
124 | $childClass = new ClassBuilder($namespace, $node->getName()); |
||
125 | $childClass->setAllowExtraKeys($node->shouldIgnoreExtraKeys()); |
||
126 | $class->addRequire($childClass); |
||
127 | $this->classes[] = $childClass; |
||
128 | |||
129 | $hasNormalizationClosures = $this->hasNormalizationClosures($node); |
||
130 | $comment = $this->getComment($node); |
||
131 | if ($hasNormalizationClosures) { |
||
132 | $comment = \sprintf(" * @template TValue\n * @param TValue \$value\n%s", $comment); |
||
133 | $comment .= \sprintf(' * @return %s|$this'."\n", $childClass->getFqcn()); |
||
134 | $comment .= \sprintf(' * @psalm-return (TValue is array ? %s : static)'."\n ", $childClass->getFqcn()); |
||
135 | } |
||
136 | if ('' !== $comment) { |
||
137 | $comment = "/**\n$comment*/\n"; |
||
138 | } |
||
139 | |||
140 | $property = $class->addProperty( |
||
141 | $node->getName(), |
||
142 | $this->getType($childClass->getFqcn(), $hasNormalizationClosures) |
||
143 | ); |
||
144 | $nodeTypes = $this->getParameterTypes($node); |
||
145 | $body = $hasNormalizationClosures ? ' |
||
146 | COMMENTpublic function NAME(PARAM_TYPE $value = []): CLASS|static |
||
147 | { |
||
148 | if (!\is_array($value)) { |
||
149 | $this->_usedProperties[\'PROPERTY\'] = true; |
||
150 | $this->PROPERTY = $value; |
||
151 | |||
152 | return $this; |
||
153 | } |
||
154 | |||
155 | if (!$this->PROPERTY instanceof CLASS) { |
||
156 | $this->_usedProperties[\'PROPERTY\'] = true; |
||
157 | $this->PROPERTY = new CLASS($value); |
||
158 | } elseif (0 < \func_num_args()) { |
||
159 | throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); |
||
160 | } |
||
161 | |||
162 | return $this->PROPERTY; |
||
163 | }' : ' |
||
164 | COMMENTpublic function NAME(array $value = []): CLASS |
||
165 | { |
||
166 | if (null === $this->PROPERTY) { |
||
167 | $this->_usedProperties[\'PROPERTY\'] = true; |
||
168 | $this->PROPERTY = new CLASS($value); |
||
169 | } elseif (0 < \func_num_args()) { |
||
170 | throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); |
||
171 | } |
||
172 | |||
173 | return $this->PROPERTY; |
||
174 | }'; |
||
175 | $class->addUse(InvalidConfigurationException::class); |
||
176 | $class->addMethod($node->getName(), $body, [ |
||
177 | 'COMMENT' => $comment, |
||
178 | 'PROPERTY' => $property->getName(), |
||
179 | 'CLASS' => $childClass->getFqcn(), |
||
180 | 'PARAM_TYPE' => \in_array('mixed', $nodeTypes, true) ? 'mixed' : implode('|', $nodeTypes), |
||
181 | ]); |
||
182 | |||
183 | $this->buildNode($node, $childClass, $this->getSubNamespace($childClass)); |
||
184 | } |
||
185 | |||
186 | private function handleVariableNode(VariableNode $node, ClassBuilder $class): void |
||
187 | { |
||
188 | $comment = $this->getComment($node); |
||
189 | $property = $class->addProperty($node->getName()); |
||
190 | $class->addUse(ParamConfigurator::class); |
||
191 | |||
192 | $body = ' |
||
193 | /** |
||
194 | COMMENT * |
||
195 | * @return $this |
||
196 | */ |
||
197 | public function NAME(mixed $valueDEFAULT): static |
||
198 | { |
||
199 | $this->_usedProperties[\'PROPERTY\'] = true; |
||
200 | $this->PROPERTY = $value; |
||
201 | |||
202 | return $this; |
||
203 | }'; |
||
204 | $class->addMethod($node->getName(), $body, [ |
||
205 | 'PROPERTY' => $property->getName(), |
||
206 | 'COMMENT' => $comment, |
||
207 | 'DEFAULT' => $node->hasDefaultValue() ? ' = '.var_export($node->getDefaultValue(), true) : '', |
||
208 | ]); |
||
209 | } |
||
210 | |||
211 | private function handlePrototypedArrayNode(PrototypedArrayNode $node, ClassBuilder $class, string $namespace): void |
||
212 | { |
||
213 | $name = $this->getSingularName($node); |
||
214 | $prototype = $node->getPrototype(); |
||
215 | $methodName = $name; |
||
216 | $hasNormalizationClosures = $this->hasNormalizationClosures($node) || $this->hasNormalizationClosures($prototype); |
||
217 | |||
218 | $nodeParameterTypes = $this->getParameterTypes($node); |
||
219 | $prototypeParameterTypes = $this->getParameterTypes($prototype); |
||
220 | if (!$prototype instanceof ArrayNode || ($prototype instanceof PrototypedArrayNode && $prototype->getPrototype() instanceof ScalarNode)) { |
||
221 | $class->addUse(ParamConfigurator::class); |
||
222 | $property = $class->addProperty($node->getName()); |
||
223 | if (null === $key = $node->getKeyAttribute()) { |
||
224 | // This is an array of values; don't use singular name |
||
225 | $nodeTypesWithoutArray = array_filter($nodeParameterTypes, static fn ($type) => 'array' !== $type); |
||
226 | $body = ' |
||
227 | /** |
||
228 | * @param ParamConfigurator|list<ParamConfigurator|PROTOTYPE_TYPE>EXTRA_TYPE $value |
||
229 | * |
||
230 | * @return $this |
||
231 | */ |
||
232 | public function NAME(PARAM_TYPE $value): static |
||
233 | { |
||
234 | $this->_usedProperties[\'PROPERTY\'] = true; |
||
235 | $this->PROPERTY = $value; |
||
236 | |||
237 | return $this; |
||
238 | }'; |
||
239 | |||
240 | $class->addMethod($node->getName(), $body, [ |
||
241 | 'PROPERTY' => $property->getName(), |
||
242 | 'PROTOTYPE_TYPE' => implode('|', $prototypeParameterTypes), |
||
243 | 'EXTRA_TYPE' => $nodeTypesWithoutArray ? '|'.implode('|', $nodeTypesWithoutArray) : '', |
||
244 | 'PARAM_TYPE' => \in_array('mixed', $nodeParameterTypes, true) ? 'mixed' : 'ParamConfigurator|'.implode('|', $nodeParameterTypes), |
||
245 | ]); |
||
246 | } else { |
||
247 | $body = ' |
||
248 | /** |
||
249 | * @return $this |
||
250 | */ |
||
251 | public function NAME(string $VAR, TYPE $VALUE): static |
||
252 | { |
||
253 | $this->_usedProperties[\'PROPERTY\'] = true; |
||
254 | $this->PROPERTY[$VAR] = $VALUE; |
||
255 | |||
256 | return $this; |
||
257 | }'; |
||
258 | |||
259 | $class->addMethod($methodName, $body, [ |
||
260 | 'PROPERTY' => $property->getName(), |
||
261 | 'TYPE' => \in_array('mixed', $prototypeParameterTypes, true) ? 'mixed' : 'ParamConfigurator|'.implode('|', $prototypeParameterTypes), |
||
262 | 'VAR' => '' === $key ? 'key' : $key, |
||
263 | 'VALUE' => 'value' === $key ? 'data' : 'value', |
||
264 | ]); |
||
265 | } |
||
266 | |||
267 | return; |
||
268 | } |
||
269 | |||
270 | $childClass = new ClassBuilder($namespace, $name); |
||
271 | if ($prototype instanceof ArrayNode) { |
||
272 | $childClass->setAllowExtraKeys($prototype->shouldIgnoreExtraKeys()); |
||
273 | } |
||
274 | $class->addRequire($childClass); |
||
275 | $this->classes[] = $childClass; |
||
276 | |||
277 | $property = $class->addProperty( |
||
278 | $node->getName(), |
||
279 | $this->getType($childClass->getFqcn().'[]', $hasNormalizationClosures) |
||
280 | ); |
||
281 | |||
282 | $comment = $this->getComment($node); |
||
283 | if ($hasNormalizationClosures) { |
||
284 | $comment = \sprintf(" * @template TValue\n * @param TValue \$value\n%s", $comment); |
||
285 | $comment .= \sprintf(' * @return %s|$this'."\n", $childClass->getFqcn()); |
||
286 | $comment .= \sprintf(' * @psalm-return (TValue is array ? %s : static)'."\n ", $childClass->getFqcn()); |
||
287 | } |
||
288 | if ('' !== $comment) { |
||
289 | $comment = "/**\n$comment*/\n"; |
||
290 | } |
||
291 | |||
292 | if (null === $key = $node->getKeyAttribute()) { |
||
293 | $body = $hasNormalizationClosures ? ' |
||
294 | COMMENTpublic function NAME(PARAM_TYPE $value = []): CLASS|static |
||
295 | { |
||
296 | $this->_usedProperties[\'PROPERTY\'] = true; |
||
297 | if (!\is_array($value)) { |
||
298 | $this->PROPERTY[] = $value; |
||
299 | |||
300 | return $this; |
||
301 | } |
||
302 | |||
303 | return $this->PROPERTY[] = new CLASS($value); |
||
304 | }' : ' |
||
305 | COMMENTpublic function NAME(array $value = []): CLASS |
||
306 | { |
||
307 | $this->_usedProperties[\'PROPERTY\'] = true; |
||
308 | |||
309 | return $this->PROPERTY[] = new CLASS($value); |
||
310 | }'; |
||
311 | $class->addMethod($methodName, $body, [ |
||
312 | 'COMMENT' => $comment, |
||
313 | 'PROPERTY' => $property->getName(), |
||
314 | 'CLASS' => $childClass->getFqcn(), |
||
315 | 'PARAM_TYPE' => \in_array('mixed', $nodeParameterTypes, true) ? 'mixed' : implode('|', $nodeParameterTypes), |
||
316 | ]); |
||
317 | } else { |
||
318 | $body = $hasNormalizationClosures ? ' |
||
319 | COMMENTpublic function NAME(string $VAR, PARAM_TYPE $VALUE = []): CLASS|static |
||
320 | { |
||
321 | if (!\is_array($VALUE)) { |
||
322 | $this->_usedProperties[\'PROPERTY\'] = true; |
||
323 | $this->PROPERTY[$VAR] = $VALUE; |
||
324 | |||
325 | return $this; |
||
326 | } |
||
327 | |||
328 | if (!isset($this->PROPERTY[$VAR]) || !$this->PROPERTY[$VAR] instanceof CLASS) { |
||
329 | $this->_usedProperties[\'PROPERTY\'] = true; |
||
330 | $this->PROPERTY[$VAR] = new CLASS($VALUE); |
||
331 | } elseif (1 < \func_num_args()) { |
||
332 | throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); |
||
333 | } |
||
334 | |||
335 | return $this->PROPERTY[$VAR]; |
||
336 | }' : ' |
||
337 | COMMENTpublic function NAME(string $VAR, array $VALUE = []): CLASS |
||
338 | { |
||
339 | if (!isset($this->PROPERTY[$VAR])) { |
||
340 | $this->_usedProperties[\'PROPERTY\'] = true; |
||
341 | $this->PROPERTY[$VAR] = new CLASS($VALUE); |
||
342 | } elseif (1 < \func_num_args()) { |
||
343 | throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'); |
||
344 | } |
||
345 | |||
346 | return $this->PROPERTY[$VAR]; |
||
347 | }'; |
||
348 | $class->addUse(InvalidConfigurationException::class); |
||
349 | $class->addMethod($methodName, str_replace('$value', '$VAR', $body), [ |
||
350 | 'COMMENT' => $comment, 'PROPERTY' => $property->getName(), |
||
351 | 'CLASS' => $childClass->getFqcn(), |
||
352 | 'VAR' => '' === $key ? 'key' : $key, |
||
353 | 'VALUE' => 'value' === $key ? 'data' : 'value', |
||
354 | 'PARAM_TYPE' => \in_array('mixed', $prototypeParameterTypes, true) ? 'mixed' : implode('|', $prototypeParameterTypes), |
||
355 | ]); |
||
356 | } |
||
357 | |||
358 | $this->buildNode($prototype, $childClass, $namespace.'\\'.$childClass->getName()); |
||
359 | } |
||
360 | |||
361 | private function handleScalarNode(ScalarNode $node, ClassBuilder $class): void |
||
362 | { |
||
363 | $comment = $this->getComment($node); |
||
364 | $property = $class->addProperty($node->getName()); |
||
365 | $class->addUse(ParamConfigurator::class); |
||
366 | |||
367 | $body = ' |
||
368 | /** |
||
369 | COMMENT * @return $this |
||
370 | */ |
||
371 | public function NAME($value): static |
||
372 | { |
||
373 | $this->_usedProperties[\'PROPERTY\'] = true; |
||
374 | $this->PROPERTY = $value; |
||
375 | |||
376 | return $this; |
||
377 | }'; |
||
378 | |||
379 | $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'COMMENT' => $comment]); |
||
380 | } |
||
381 | |||
382 | private function getParameterTypes(NodeInterface $node): array |
||
383 | { |
||
384 | $paramTypes = []; |
||
385 | if ($node instanceof BaseNode) { |
||
386 | $types = $node->getNormalizedTypes(); |
||
387 | if (\in_array(ExprBuilder::TYPE_ANY, $types, true)) { |
||
388 | $paramTypes[] = 'mixed'; |
||
389 | } |
||
390 | if (\in_array(ExprBuilder::TYPE_STRING, $types, true)) { |
||
391 | $paramTypes[] = 'string'; |
||
392 | } |
||
393 | } |
||
394 | if ($node instanceof BooleanNode) { |
||
395 | $paramTypes[] = 'bool'; |
||
396 | } elseif ($node instanceof IntegerNode) { |
||
397 | $paramTypes[] = 'int'; |
||
398 | } elseif ($node instanceof FloatNode) { |
||
399 | $paramTypes[] = 'float'; |
||
400 | } elseif ($node instanceof EnumNode) { |
||
401 | $paramTypes[] = 'mixed'; |
||
402 | } elseif ($node instanceof ArrayNode) { |
||
403 | $paramTypes[] = 'array'; |
||
404 | } elseif ($node instanceof VariableNode) { |
||
405 | $paramTypes[] = 'mixed'; |
||
406 | } |
||
407 | |||
408 | return array_unique($paramTypes); |
||
409 | } |
||
410 | |||
411 | private function getComment(BaseNode $node): string |
||
412 | { |
||
413 | $comment = ''; |
||
414 | if ('' !== $info = (string) $node->getInfo()) { |
||
415 | $comment .= ' * '.$info."\n"; |
||
416 | } |
||
417 | |||
418 | if (!$node instanceof ArrayNode) { |
||
419 | foreach ((array) ($node->getExample() ?? []) as $example) { |
||
420 | $comment .= ' * @example '.$example."\n"; |
||
421 | } |
||
422 | |||
423 | if ('' !== $default = $node->getDefaultValue()) { |
||
424 | $comment .= ' * @default '.(null === $default ? 'null' : var_export($default, true))."\n"; |
||
425 | } |
||
426 | |||
427 | if ($node instanceof EnumNode) { |
||
428 | $comment .= \sprintf(' * @param ParamConfigurator|%s $value', implode('|', array_unique(array_map(fn ($a) => !$a instanceof \UnitEnum ? var_export($a, true) : '\\'.ltrim(var_export($a, true), '\\'), $node->getValues()))))."\n"; |
||
429 | } else { |
||
430 | $parameterTypes = $this->getParameterTypes($node); |
||
431 | $comment .= ' * @param ParamConfigurator|'.implode('|', $parameterTypes).' $value'."\n"; |
||
432 | } |
||
433 | } else { |
||
434 | foreach ((array) ($node->getExample() ?? []) as $example) { |
||
435 | $comment .= ' * @example '.json_encode($example)."\n"; |
||
436 | } |
||
437 | |||
438 | if ($node->hasDefaultValue() && [] != $default = $node->getDefaultValue()) { |
||
439 | $comment .= ' * @default '.json_encode($default)."\n"; |
||
440 | } |
||
441 | } |
||
442 | |||
443 | if ($node->isDeprecated()) { |
||
444 | $comment .= ' * @deprecated '.$node->getDeprecation($node->getName(), $node->getParent()->getName())['message']."\n"; |
||
445 | } |
||
446 | |||
447 | return $comment; |
||
448 | } |
||
449 | |||
450 | /** |
||
451 | * Pick a good singular name. |
||
452 | */ |
||
453 | private function getSingularName(PrototypedArrayNode $node): string |
||
454 | { |
||
455 | $name = $node->getName(); |
||
456 | if (!str_ends_with($name, 's')) { |
||
457 | return $name; |
||
458 | } |
||
459 | |||
460 | $parent = $node->getParent(); |
||
461 | $mappings = $parent instanceof ArrayNode ? $parent->getXmlRemappings() : []; |
||
462 | foreach ($mappings as $map) { |
||
463 | if ($map[1] === $name) { |
||
464 | $name = $map[0]; |
||
465 | break; |
||
466 | } |
||
467 | } |
||
468 | |||
469 | return $name; |
||
470 | } |
||
471 | |||
472 | private function buildToArray(ClassBuilder $class): void |
||
505 | }'); |
||
506 | } |
||
507 | |||
508 | private function buildConstructor(ClassBuilder $class): void |
||
509 | { |
||
510 | $body = ''; |
||
511 | foreach ($class->getProperties() as $p) { |
||
512 | $code = '$value[\'ORG_NAME\']'; |
||
513 | if (null !== $p->getType()) { |
||
514 | if ($p->isArray()) { |
||
515 | $code = $p->areScalarsAllowed() |
||
516 | ? 'array_map(fn ($v) => \is_array($v) ? new '.$p->getType().'($v) : $v, $value[\'ORG_NAME\'])' |
||
517 | : 'array_map(fn ($v) => new '.$p->getType().'($v), $value[\'ORG_NAME\'])' |
||
518 | ; |
||
519 | } else { |
||
520 | $code = $p->areScalarsAllowed() |
||
521 | ? '\is_array($value[\'ORG_NAME\']) ? new '.$p->getType().'($value[\'ORG_NAME\']) : $value[\'ORG_NAME\']' |
||
522 | : 'new '.$p->getType().'($value[\'ORG_NAME\'])' |
||
523 | ; |
||
524 | } |
||
525 | } |
||
526 | |||
527 | $body .= strtr(' |
||
528 | if (array_key_exists(\'ORG_NAME\', $value)) { |
||
529 | $this->_usedProperties[\'PROPERTY\'] = true; |
||
530 | $this->PROPERTY = '.$code.'; |
||
531 | unset($value[\'ORG_NAME\']); |
||
532 | } |
||
533 | ', ['PROPERTY' => $p->getName(), 'ORG_NAME' => $p->getOriginalName()]); |
||
534 | } |
||
535 | |||
536 | if ($class->shouldAllowExtraKeys()) { |
||
537 | $body .= ' |
||
538 | $this->_extraKeys = $value; |
||
539 | '; |
||
540 | } else { |
||
541 | $body .= ' |
||
542 | if ([] !== $value) { |
||
543 | throw new InvalidConfigurationException(sprintf(\'The following keys are not supported by "%s": \', __CLASS__).implode(\', \', array_keys($value))); |
||
544 | }'; |
||
545 | |||
546 | $class->addUse(InvalidConfigurationException::class); |
||
547 | } |
||
548 | |||
549 | $class->addMethod('__construct', ' |
||
550 | public function __construct(array $value = []) |
||
551 | {'.$body.' |
||
552 | }'); |
||
553 | } |
||
554 | |||
555 | private function buildSetExtraKey(ClassBuilder $class): void |
||
556 | { |
||
557 | if (!$class->shouldAllowExtraKeys()) { |
||
558 | return; |
||
559 | } |
||
560 | |||
561 | $class->addUse(ParamConfigurator::class); |
||
562 | |||
563 | $class->addProperty('_extraKeys'); |
||
564 | |||
565 | $class->addMethod('set', ' |
||
566 | /** |
||
567 | * @param ParamConfigurator|mixed $value |
||
568 | * |
||
569 | * @return $this |
||
570 | */ |
||
571 | public function NAME(string $key, mixed $value): static |
||
572 | { |
||
573 | $this->_extraKeys[$key] = $value; |
||
574 | |||
575 | return $this; |
||
576 | }'); |
||
577 | } |
||
578 | |||
579 | private function getSubNamespace(ClassBuilder $rootClass): string |
||
580 | { |
||
581 | return \sprintf('%s\\%s', $rootClass->getNamespace(), substr($rootClass->getName(), 0, -6)); |
||
582 | } |
||
583 | |||
584 | private function hasNormalizationClosures(NodeInterface $node): bool |
||
593 | } |
||
594 | |||
595 | private function getType(string $classType, bool $hasNormalizationClosures): string |
||
596 | { |
||
598 | } |
||
599 | } |
||
600 |
If you suppress an error, we recommend checking for the error condition explicitly: