Passed
Push — master ( d8ac0a...430361 )
by Mikaël
15:43 queued 14s
created

Struct::addClassElement()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1.2963

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 5
ccs 1
cts 3
cp 0.3333
crap 1.2963
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace WsdlToPhp\PackageGenerator\File;
6
7
use WsdlToPhp\PackageGenerator\Container\Model\StructAttribute as StructAttributeContainer;
8
use WsdlToPhp\PackageGenerator\Container\PhpElement\Constant as ConstantContainer;
9
use WsdlToPhp\PackageGenerator\Container\PhpElement\Property as PropertyContainer;
10
use WsdlToPhp\PackageGenerator\File\Element\PhpFunctionParameter;
11
use WsdlToPhp\PackageGenerator\File\Validation\ArrayRule;
12
use WsdlToPhp\PackageGenerator\File\Validation\ChoiceRule;
13
use WsdlToPhp\PackageGenerator\File\Validation\LengthRule;
14
use WsdlToPhp\PackageGenerator\File\Validation\MaxLengthRule;
15
use WsdlToPhp\PackageGenerator\File\Validation\MinLengthRule;
16
use WsdlToPhp\PackageGenerator\File\Validation\PatternRule;
17
use WsdlToPhp\PackageGenerator\File\Validation\Rules;
18
use WsdlToPhp\PackageGenerator\File\Validation\UnionRule;
19
use WsdlToPhp\PackageGenerator\Model\AbstractModel;
20
use WsdlToPhp\PackageGenerator\Model\Struct as StructModel;
21
use WsdlToPhp\PackageGenerator\Model\StructAttribute as StructAttributeModel;
22
use WsdlToPhp\PhpGenerator\Element\AccessRestrictedElementInterface;
23
use WsdlToPhp\PhpGenerator\Element\AssignedValueElementInterface;
24 132
use WsdlToPhp\PhpGenerator\Element\PhpAnnotation;
25
use WsdlToPhp\PhpGenerator\Element\PhpAnnotationBlock;
26 132
use WsdlToPhp\PhpGenerator\Element\PhpConstant;
27 2
use WsdlToPhp\PhpGenerator\Element\PhpMethod;
28
use WsdlToPhp\PhpGenerator\Element\PhpProperty;
29
30 130
class Struct extends AbstractModelFile
31
{
32
    public function setModel(AbstractModel $model): self
33 106
    {
34
        if (!$model instanceof StructModel) {
35 106
            throw new \InvalidArgumentException('Model must be an instance of a Struct', __LINE__);
36 104
        }
37
38
        return parent::setModel($model);
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::setModel($model) returns the type WsdlToPhp\PackageGenerator\File\AbstractModelFile which includes types incompatible with the type-hinted return WsdlToPhp\PackageGenerator\File\Struct.
Loading history...
39 106
    }
40
41
    public function getModel(): ?StructModel
42 106
    {
43
        return parent::getModel();
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::getModel() could return the type WsdlToPhp\PackageGenerator\Model\AbstractModel which includes types incompatible with the type-hinted return WsdlToPhp\PackageGenerator\Model\Struct|null. Consider adding an additional type-check to rule them out.
Loading history...
44 106
    }
45
46
    protected function addClassElement(): AbstractModelFile
47
    {
48
        $this->getFile()->addString('#[\AllowDynamicProperties]');
49
50 122
        return parent::addClassElement();
51
    }
52 122
53
    protected function defineUseStatements(): self
54
    {
55 122
        if ($this->getGenerator()->getOptionValidation()) {
56
            $this->getFile()->addUse(\InvalidArgumentException::class);
57
        }
58 122
59
        return parent::defineUseStatements();
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::defineUseStatements() returns the type WsdlToPhp\PackageGenerator\File\AbstractModelFile which includes types incompatible with the type-hinted return WsdlToPhp\PackageGenerator\File\Struct.
Loading history...
60 102
    }
61 4
62
    protected function fillClassConstants(ConstantContainer $constants): void
63 4
    {
64
    }
65
66 102
    protected function getConstantAnnotationBlock(PhpConstant $constant): ?PhpAnnotationBlock
67
    {
68 102
        return null;
69
    }
70
71 102
    protected function getModelAttributes(): StructAttributeContainer
72 102
    {
73 102
        return $this->getModel()->getProperAttributes(true);
74 102
    }
75 102
76
    protected function fillClassProperties(PropertyContainer $properties): void
77
    {
78
        /** @var StructAttributeModel $attribute */
79
        foreach ($this->getModelAttributes() as $attribute) {
80 122
            switch (true) {
81
                case $attribute->isXml():
82 102
                    $type = null;
83
84 102
                    break;
85 102
86 102
                default:
87 102
                    $type = (($attribute->isRequired() && !$attribute->isNullable()) ? '' : '?').$this->getStructAttributeTypeAsPhpType($attribute);
88 2
89
                    break;
90 102
            }
91 102
92 102
            $properties->add(
93
                new PhpProperty(
94
                    $attribute->getCleanName(),
95 102
                    $attribute->isRequired() ? AssignedValueElementInterface::NO_VALUE : null,
96
                    $this->getGenerator()->getOptionValidation() ? AccessRestrictedElementInterface::ACCESS_PROTECTED : AccessRestrictedElementInterface::ACCESS_PUBLIC,
97
                    $type
98 106
                )
99
            );
100
        }
101 106
    }
102 106
103
    protected function getPropertyAnnotationBlock(PhpProperty $property): ?PhpAnnotationBlock
104 106
    {
105
        $annotationBlock = new PhpAnnotationBlock();
106 106
        $annotationBlock->addChild(sprintf('The %s', $property->getName()));
107
        $attribute = $this->getModel()->getAttribute($property->getName());
108 106
        if (!$attribute instanceof StructAttributeModel) {
109 102
            $attribute = $this->getModel()->getAttributeByCleanName($property->getName());
110 102
        }
111 102
        if ($attribute instanceof StructAttributeModel) {
112
            $this->defineModelAnnotationsFromWsdl($annotationBlock, $attribute);
113
            $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_VAR, $this->getStructAttributeTypeGetAnnotation($attribute)));
114 106
        }
115
116
        return $annotationBlock;
117 102
    }
118
119 102
    protected function fillClassMethods(): void
120 102
    {
121 102
        $this
122 102
            ->addStructMethodConstruct()
123
            ->addStructMethodsSetAndGet()
124 102
        ;
125
    }
126
127 102
    protected function addStructMethodConstruct(): self
128
    {
129
        if (0 < count($parameters = $this->getStructMethodParametersValues())) {
130 102
            $method = new PhpMethod(self::METHOD_CONSTRUCT, $parameters);
131
            $this->addStructMethodConstructBody($method);
132 102
            $this->methods->add($method);
133 102
        }
134
135 102
        return $this;
136
    }
137
138 106
    protected function addStructMethodConstructBody(PhpMethod $method): self
139
    {
140 106
        $count = $this->getModelAttributes()->count();
141 106
        foreach ($this->getModelAttributes() as $index => $attribute) {
142 102
            if (0 === $index) {
143
                $method->addChild('$this');
144
            }
145 106
            $this->addStructMethodConstructBodyForAttribute($method, $attribute, $count - 1 === $index);
146
        }
147
148 102
        return $this;
149
    }
150
151 102
    protected function addStructMethodConstructBodyForAttribute(PhpMethod $method, StructAttributeModel $attribute, bool $isLast): self
152 102
    {
153 10
        $uniqueString = $attribute->getUniqueString($attribute->getCleanName(), 'method');
154
        $method->addChild($method->getIndentedString(sprintf('->%s($%s)%s', $attribute->getSetterName(), lcfirst($uniqueString), $isLast ? ';' : ''), 1));
155 10
156
        return $this;
157
    }
158 102
159
    protected function getStructMethodParametersValues(): array
160 102
    {
161
        $parametersValues = [];
162
        foreach ($this->getModelAttributes() as $attribute) {
163
            $parametersValues[] = $this->getStructMethodParameter($attribute);
164 102
        }
165 102
166 102
        return $parametersValues;
167
    }
168
169
    protected function getStructMethodParameter(StructAttributeModel $attribute): PhpFunctionParameter
170
    {
171
        switch (true) {
172
            case $attribute->isXml():
173
            case $attribute->isList():
174
                $type = null;
175 106
176
                break;
177 106
178
            default:
179 102
                $type = (($attribute->isRequired() && !$attribute->isNullable()) ? '' : '?').$this->getStructAttributeTypeAsPhpType($attribute);
180 102
181 102
                break;
182
        }
183
184
        try {
185 106
            $defaultValue = $attribute->getDefaultValue($this->getStructAttributeTypeAsPhpType($attribute));
186
187
            return new PhpFunctionParameter(
188 92
                lcfirst($attribute->getUniqueString($attribute->getCleanName(), 'method')),
189
                $attribute->isRequired() && !$attribute->isAChoice() ? AssignedValueElementInterface::NO_VALUE : (str_contains($type ?? '', '?') ? $defaultValue ?? null : $defaultValue),
190 92
                $type,
191 36
                $attribute
192 36
            );
193 36
        } catch (\InvalidArgumentException $exception) {
194 36
            throw new \InvalidArgumentException(sprintf('Unable to create function parameter for struct "%s" with type "%s" for attribute "%s"', $this->getModel()->getName(), var_export($this->getStructAttributeTypeAsPhpType($attribute), true), $attribute->getName()), __LINE__, $exception);
195 36
        }
196
    }
197
198 36
    protected function addStructMethodsSetAndGet(): self
199 36
    {
200 36
        foreach ($this->getModelAttributes() as $attribute) {
201
            $this
202
                ->addStructMethodGet($attribute)
203 92
                ->addStructMethodSet($attribute)
204
                ->addStructMethodAddTo($attribute)
205
            ;
206 36
        }
207
208 36
        return $this;
209 36
    }
210
211
    protected function addStructMethodAddTo(StructAttributeModel $attribute): self
212 36
    {
213 36
        if ($attribute->isArray()) {
214
            $method = new PhpMethod(sprintf('addTo%s', ucfirst($attribute->getCleanName())), [
215
                new PhpFunctionParameter(
216
                    'item',
217
                    AssignedValueElementInterface::NO_VALUE,
218
                    $this->getStructAttributeTypeAsPhpType($attribute, false),
219 36
                    $attribute
220 36
                ),
221 36
            ], self::TYPE_SELF);
222
            $this->addStructMethodAddToBody($method, $attribute);
223
            $this->methods->add($method);
224 36
        }
225
226
        return $this;
227 102
    }
228
229 102
    protected function addStructMethodAddToBody(PhpMethod $method, StructAttributeModel $attribute): self
230 102
    {
231 102
        $this->applyRules($method, $attribute, 'item', true);
232 102
233 102
        if ($attribute->nameIsClean()) {
234
            $assignment = sprintf('$this->%s[] = $item;', $attribute->getCleanName());
235 102
        } else {
236
            $assignment = sprintf('$this->%s[] = $this->{\'%s\'}[] = $item;', $attribute->getCleanName(), addslashes($attribute->getName()));
237
        }
238 102
239
        $method
240 102
            ->addChild($assignment)
241 102
            ->addChild('')
242 102
            ->addChild('return $this;')
243 102
        ;
244 100
245
        return $this;
246
    }
247
248 102
    protected function addStructMethodSet(StructAttributeModel $attribute): self
249 102
    {
250
        $method = new PhpMethod($attribute->getSetterName(), [
251
            $this->getStructMethodParameter($attribute),
252
        ], self::TYPE_SELF);
253 102
        $this->addStructMethodSetBody($method, $attribute);
254
        $this->methods->add($method);
255 102
256
        return $this;
257 18
    }
258 18
259 18
    protected function addStructMethodSetBody(PhpMethod $method, StructAttributeModel $attribute): self
260 18
    {
261 18
        $parameters = $method->getParameters();
262
        $parameter = array_shift($parameters);
263
        $parameterName = is_string($parameter) ? $parameter : $parameter->getName();
264 98
265
        return $this
266
            ->applyRules($method, $attribute, $parameterName)
267 102
            ->addStructMethodSetBodyAssignment($method, $attribute, $parameterName)
268
            ->addStructMethodSetBodyReturn($method)
269
        ;
270 102
    }
271
272
    protected function addStructMethodSetBodyAssignment(PhpMethod $method, StructAttributeModel $attribute, string $parameterName): self
273 102
    {
274 102
        if ($attribute->getRemovableFromRequest() || $attribute->isAChoice()) {
275
            $method
276
                ->addChild(sprintf('if (is_null($%1$s) || (is_array($%1$s) && empty($%1$s))) {', $parameterName))
277 102
                ->addChild($method->getIndentedString(sprintf('unset($this->%1$s%2$s);', $attribute->getCleanName(), $attribute->nameIsClean() ? '' : sprintf(', $this->{\'%s\'}', addslashes($attribute->getName()))), 1))
278
                ->addChild('} else {')
279
                ->addChild($method->getIndentedString($this->getStructMethodSetBodyAssignment($attribute, $parameterName), 1))
280 102
                ->addChild('}')
281
            ;
282 102
        } else {
283 102
            $method->addChild($this->getStructMethodSetBodyAssignment($attribute, $parameterName));
284 6
        }
285 6
286 102
        return $this;
287 4
    }
288 4
289
    protected function addStructMethodSetBodyReturn(PhpMethod $method): self
290
    {
291 102
        $method
292 102
            ->addChild('')
293
            ->addChild('return $this;')
294 2
        ;
295
296
        return $this;
297 102
    }
298
299
    protected function getStructMethodSetBodyAssignment(StructAttributeModel $attribute, string $parameterName): string
300 102
    {
301
        $prefix = '$';
302 102
        if ($attribute->isList()) {
303
            $prefix = '';
304
            $parameterName = sprintf('is_array($%1$s) ? implode(\' \', $%1$s) : $%1$s', $parameterName);
305 102
        } elseif ($attribute->isXml()) {
306
            $prefix = '';
307 102
            $parameterName = sprintf('($%1$s instanceof \DOMDocument) ? $%1$s->saveXML($%1$s->hasChildNodes() ? $%1$s->childNodes->item(0) : null) : $%1$s', $parameterName);
308 102
        }
309
310 4
        if ($attribute->nameIsClean()) {
311 4
            $assignment = sprintf('$this->%s = %s%s;', $attribute->getName(), $prefix, $parameterName);
312 4
        } else {
313 4
            $assignment = sprintf('$this->%s = $this->{\'%s\'} = %s%s;', $attribute->getCleanName(), addslashes($attribute->getName()), $prefix, $parameterName);
314 4
        }
315
316 4
        return $assignment;
317
    }
318
319 4
    protected function addStructMethodGetBody(PhpMethod $method, StructAttributeModel $attribute, string $thisAccess): self
320
    {
321 102
        return $this->addStructMethodGetBodyReturn($method, $attribute, $thisAccess);
322 18
    }
323
324 102
    protected function addStructMethodGetBodyReturn(PhpMethod $method, StructAttributeModel $attribute, string $thisAccess): self
325
    {
326 102
        $return = sprintf('return $this->%s;', $thisAccess);
327
        if ($attribute->isXml()) {
328
            $method
329 102
                ->addChild('$domDocument = null;')
330
                ->addChild(sprintf('if (!empty($this->%1$s) && $asDomDocument) {', $thisAccess))
331
                ->addChild($method->getIndentedString('$domDocument = new \DOMDocument(\'1.0\', \'UTF-8\');', 1))
332
                ->addChild($method->getIndentedString(sprintf('$domDocument->loadXML($this->%s);', $thisAccess), 1))
333 102
                ->addChild('}')
334 4
            ;
335
            if ($attribute->getRemovableFromRequest() || $attribute->isAChoice()) {
336 4
                $return = sprintf('return $asDomDocument ? $domDocument : (isset($this->%1$s) ? $this->%1$s : null);', $thisAccess);
337
            } else {
338
                $return = sprintf('return $asDomDocument ? $domDocument : $this->%1$s;', $thisAccess);
339 102
            }
340
        } elseif ($attribute->getRemovableFromRequest() || $attribute->isAChoice()) {
341 102
            $return = sprintf('return $this->%s ?? null;', $thisAccess);
342
        }
343
        $method->addChild($return);
344 102
345 102
        return $this;
346 102
    }
347
348
    protected function addStructMethodGet(StructAttributeModel $attribute): self
349 102
    {
350 102
        switch (true) {
351
            // it can either be a string, a DOMDocument or null...
352 2
            case $attribute->isXml():
353
                $returnType = '';
354 102
355 102
                break;
356
357 102
            default:
358
                $returnType = (!$attribute->getRemovableFromRequest() && !$attribute->isAChoice() && $attribute->isRequired() ? '' : '?').$this->getStructAttributeTypeAsPhpType($attribute);
359
360 102
                break;
361
        }
362 102
363 102
        $method = new PhpMethod(
364 4
            $attribute->getGetterName(),
365
            $this->getStructMethodGetParameters($attribute),
366
            $returnType
367 102
        );
368
        if ($attribute->nameIsClean()) {
369
            $thisAccess = sprintf('%s', $attribute->getName());
370 102
        } else {
371
            $thisAccess = sprintf('{\'%s\'}', addslashes($attribute->getName()));
372 102
        }
373
        $this->addStructMethodGetBody($method, $attribute, $thisAccess);
374
        $this->methods->add($method);
375 102
376
        return $this;
377 102
    }
378
379 102
    protected function getStructMethodGetParameters(StructAttributeModel $attribute): array
380 102
    {
381 102
        $parameters = [];
382
        if ($attribute->isXml()) {
383 102
            $parameters[] = new PhpFunctionParameter('asDomDocument', false, self::TYPE_BOOL, $attribute);
384
        }
385 102
386 102
        return $parameters;
387 102
    }
388
389 102
    protected function getMethodAnnotationBlock(PhpMethod $method): ?PhpAnnotationBlock
390
    {
391 66
        return $this->getStructMethodAnnotationBlock($method);
392 36
    }
393
394 36
    protected function getStructMethodAnnotationBlock(PhpMethod $method): ?PhpAnnotationBlock
395
    {
396 66
        $annotationBlock = null;
397 4
        $matches = [];
398
399 4
        switch ($method->getName()) {
400
            case self::METHOD_CONSTRUCT:
401 62
                $annotationBlock = $this->getStructMethodConstructAnnotationBlock();
402 56
403
                break;
404 56
405
            case 0 === mb_strpos($method->getName(), 'get'):
406 12
            case 0 === mb_strpos($method->getName(), 'set'):
407 8
                $annotationBlock = $this->getStructMethodsSetAndGetAnnotationBlock($method);
408
409 8
                break;
410
411 4
            case 0 === mb_strpos($method->getName(), 'addTo'):
412 4
                $annotationBlock = $this->getStructMethodsAddToAnnotationBlock($method);
413
414 4
                break;
415
416 4
            case 1 === preg_match('/validate(.+)For(.+)ConstraintFrom(.+)/', $method->getName(), $matches):
417 4
                $annotationBlock = $this->getStructMethodsValidateMethodAnnotationBlock($matches[1], $matches[2], $matches[3]);
418
419 4
                break;
420
        }
421
422
        return $annotationBlock;
423
    }
424
425
    protected function getStructMethodConstructAnnotationBlock(): PhpAnnotationBlock
426
    {
427 102
        $annotationBlock = new PhpAnnotationBlock([
428
            sprintf('Constructor method for %s', $this->getModel()->getName()),
429
        ]);
430 102
        $this->addStructPropertiesToAnnotationBlock($annotationBlock);
431
432 102
        return $annotationBlock;
433 102
    }
434
435 102
    protected function getStructMethodsSetAndGetAnnotationBlock(PhpMethod $method): PhpAnnotationBlock
436
    {
437 102
        $parameters = $method->getParameters();
438
        $setOrGet = mb_strtolower(mb_substr($method->getName(), 0, 3));
439
        $parameter = array_shift($parameters);
440 102
        // Only set parameter must be based on a potential PhpFunctionParameter
441
        if ($parameter instanceof PhpFunctionParameter && 'set' === $setOrGet) {
442 102
            $parameterName = ucfirst($parameter->getName());
443 102
        } else {
444 102
            $parameterName = mb_substr($method->getName(), 3);
445
        }
446 102
447 102
        /**
448
         * Since properties can be duplicated with different case, we assume that _\d+ is replaceable by an empty string as methods are "duplicated" with this suffix.
449 102
         */
450
        $parameterName = preg_replace('/(_\d+)/', '', $parameterName);
451
        $attribute = $this->getModel()->getAttribute($parameterName);
452
        if (!$attribute instanceof StructAttributeModel) {
453
            $attribute = $this->getModel()->getAttributeByCleanName($parameterName);
454 102
        }
455 102
        if (!$attribute instanceof StructAttributeModel) {
456 102
            $parameterName = lcfirst($parameterName);
457 42
            $attribute = $this->getModel()->getAttribute($parameterName);
458
            if (!$attribute instanceof StructAttributeModel) {
459 102
                $attribute = $this->getModel()->getAttributeByCleanName($parameterName);
460 42
            }
461 42
        }
462 42
        $setValueAnnotation = '%s %s value';
463 4
        $annotationBlock = new PhpAnnotationBlock();
464
        if ($attribute instanceof StructAttributeModel) {
465
            $annotationBlock->addChild(sprintf($setValueAnnotation, ucfirst($setOrGet), $parameterName));
466 102
            $this->addStructMethodsSetAndGetAnnotationBlockFromStructAttribute($setOrGet, $annotationBlock, $attribute);
467 102
        } elseif (!$attribute) {
0 ignored issues
show
introduced by
$attribute is of type null, thus it always evaluated to false.
Loading history...
468 102
            $annotationBlock->addChild(sprintf($setValueAnnotation, ucfirst($setOrGet), lcfirst($parameterName)));
469 102
            $this->addStructMethodsSetAndGetAnnotationBlockFromScalar($setOrGet, $annotationBlock, $parameterName);
470 102
        }
471
472 2
        return $annotationBlock;
473 2
    }
474
475
    protected function addStructMethodsSetAndGetAnnotationBlockFromStructAttribute(string $setOrGet, PhpAnnotationBlock $annotationBlock, StructAttributeModel $attribute): self
476 102
    {
477
        switch ($setOrGet) {
478
            case 'set':
479 102
                if ($attribute->getRemovableFromRequest()) {
480
                    $annotationBlock->addChild('This property is removable from request (nillable=true+minOccurs=0), therefore if the value assigned to this property is null, it is removed from this object');
481 102
                }
482 102
                if ($attribute->isAChoice()) {
483 102
                    $annotationBlock->addChild('This property belongs to a choice that allows only one property to exist. It is therefore removable from the request, consequently if the value assigned to this property is null, the property is removed from this object');
484 10
                }
485
                if ($attribute->isXml()) {
486 102
                    $annotationBlock
487 8
                        ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMDocument::hasChildNodes()'))
488
                        ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMDocument::saveXML()'))
489 102
                        ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMNode::item()'))
490
                    ;
491 4
                }
492 4
                if ($this->getGenerator()->getOptionValidation()) {
493 4
                    if ($attribute->isAChoice()) {
494
                        $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, \InvalidArgumentException::class));
495
                    }
496 102
                    if (($model = $this->getRestrictionFromStructAttribute($attribute)) instanceof StructModel) {
497 100
                        $annotationBlock
498 8
                            ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_VALUE_IS_VALID)))
499
                            ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_GET_VALID_VALUES)))
500 100
                            ->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, \InvalidArgumentException::class))
501
                        ;
502 48
                    } elseif ($attribute->isArray()) {
503 48
                        $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, \InvalidArgumentException::class));
504 48
                    }
505
                }
506 98
                $this->addStructMethodsSetAnnotationBlock($annotationBlock, $this->getStructAttributeTypeSetAnnotation($attribute, false), lcfirst($attribute->getCleanName()));
507 50
508
                break;
509
510 102
            case 'get':
511
                if ($attribute->getRemovableFromRequest()) {
512 102
                    $annotationBlock->addChild('An additional test has been added (isset) before returning the property value as this property may have been unset before, due to the fact that this property is removable from the request (nillable=true+minOccurs=0)');
513
                }
514 102
                $this
515 102
                    ->addStructMethodsGetAnnotationBlockFromXmlAttribute($annotationBlock, $attribute)
516 10
                    ->addStructMethodsGetAnnotationBlock($annotationBlock, $this->getStructAttributeTypeGetAnnotation($attribute, true, $attribute->isAChoice()))
517
                ;
518
519 102
                break;
520 102
        }
521
522
        return $this;
523 102
    }
524
525
    protected function addStructMethodsSetAndGetAnnotationBlockFromScalar(string $setOrGet, PhpAnnotationBlock $annotationBlock, string $attributeName): self
526 102
    {
527
        switch ($setOrGet) {
528
            case 'set':
529 2
                $this->addStructMethodsSetAnnotationBlock($annotationBlock, lcfirst($attributeName), lcfirst($attributeName));
530
531 2
                break;
532 2
533 2
            case 'get':
534
                $this->addStructMethodsGetAnnotationBlock($annotationBlock, lcfirst($attributeName));
535 2
536
                break;
537 2
        }
538 2
539
        return $this;
540 2
    }
541
542
    protected function addStructMethodsSetAnnotationBlock(PhpAnnotationBlock $annotationBlock, string $type, string $name): self
543 2
    {
544
        $annotationBlock
545
            ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $%s', $type, $name)))
546 102
            ->addChild(new PhpAnnotation(self::ANNOTATION_RETURN, $this->getModel()->getPackagedName(true)))
547
        ;
548
549 102
        return $this;
550 102
    }
551
552
    protected function addStructMethodsGetAnnotationBlock(PhpAnnotationBlock $annotationBlock, string $attributeType): self
553 102
    {
554
        $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_RETURN, $attributeType));
555
556 102
        return $this;
557
    }
558 102
559
    protected function addStructMethodsGetAnnotationBlockFromXmlAttribute(PhpAnnotationBlock $annotationBlock, StructAttributeModel $attribute): self
560 102
    {
561
        if ($attribute->isXml()) {
562
            $annotationBlock
563 102
                ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMDocument::loadXML()'))
564
                ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, 'bool $asDomDocument true: returns \DOMDocument, false: returns XML string'))
565 102
            ;
566
        }
567 4
568 4
        return $this;
569
    }
570
571
    protected function addStructPropertiesToAnnotationBlock(PhpAnnotationBlock $annotationBlock): self
572 102
    {
573
        return $this
574
            ->addStructPropertiesToAnnotationBlockUses($annotationBlock)
575 102
            ->addStructPropertiesToAnnotationBlockParams($annotationBlock)
576
        ;
577
    }
578 102
579 102
    protected function addStructPropertiesToAnnotationBlockUses(PhpAnnotationBlock $annotationBlock): self
580
    {
581
        foreach ($this->getModelAttributes() as $attribute) {
582
            $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $this->getModel()->getPackagedName(), $attribute->getSetterName())));
583 102
        }
584
585 102
        return $this;
586 102
    }
587
588
    protected function addStructPropertiesToAnnotationBlockParams(PhpAnnotationBlock $annotationBlock): self
589 102
    {
590
        foreach ($this->getModelAttributes() as $attribute) {
591
            $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $%s', $this->getStructAttributeTypeSetAnnotation($attribute, false), lcfirst($attribute->getCleanName()))));
592 102
        }
593
594 102
        return $this;
595 102
    }
596
597
    protected function getStructMethodsAddToAnnotationBlock(PhpMethod $method): PhpAnnotationBlock
598 102
    {
599
        $methodParameters = $method->getParameters();
600
601 36
        /** @var PhpFunctionParameter $firstParameter */
602
        $firstParameter = array_shift($methodParameters);
603 36
        $attribute = $this->getModel()->getAttribute($firstParameter->getModel()->getName());
604 36
        $annotationBlock = new PhpAnnotationBlock();
605 36
        if ($attribute instanceof StructAttributeModel) {
606 36
            $model = $this->getRestrictionFromStructAttribute($attribute);
607 36
            $annotationBlock->addChild(sprintf('Add item to %s value', $attribute->getCleanName()));
608 36
            if ($model instanceof StructModel) {
609 36
                $annotationBlock
610 36
                    ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_VALUE_IS_VALID)))
611
                    ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_GET_VALID_VALUES)))
612 2
                ;
613 2
            }
614
            $annotationBlock
615
                ->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, \InvalidArgumentException::class))
616
                ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $item', $this->getStructAttributeTypeSetAnnotation($attribute, false, true))))
617 36
                ->addChild(new PhpAnnotation(self::ANNOTATION_RETURN, $this->getModel()->getPackagedName(true)))
618 36
            ;
619 36
        }
620
621
        return $annotationBlock;
622
    }
623 36
624
    protected function getStructMethodsValidateMethodAnnotationBlock(string $propertyName, string $constraintName, string $fromMethodName): PhpAnnotationBlock
625
    {
626 56
        $customConstraintMessage = '';
627
        $constraintArgName = 'array $values';
628 56
629
        switch (lcfirst($constraintName)) {
630 56
            case ArrayRule::NAME:
631 56
                $customConstraintMessage = 'This has to validate that each item contained by the array match the itemType constraint';
632 56
633 56
                break;
634 56
635
            case ChoiceRule::NAME:
636
                $customConstraintMessage = 'This has to validate that the property which is being set is the only one among the given choices';
637
                $constraintArgName = 'mixed $value';
638 4
639
                break;
640 4
641
            case LengthRule::NAME:
642 4
            case MaxLengthRule::NAME:
643 4
            case MinLengthRule::NAME:
644 4
                $customConstraintMessage = 'This has to validate that the items contained by the array match the length constraint';
645 4
646 4
                break;
647 4
648
            case PatternRule::NAME:
649
                $customConstraintMessage = 'This has to validate that the items contained by the array match the defined pattern';
650
651 8
                break;
652
653 8
            case UnionRule::NAME:
654
                $customConstraintMessage = sprintf('This is a set of validation rules based on the union types associated to the property %s', $propertyName);
655 8
                $constraintArgName = 'mixed $value';
656 8
657 8
                break;
658 8
        }
659 8
660 8
        return new PhpAnnotationBlock([
661
            new PhpAnnotation(
662
                PhpAnnotation::NO_NAME,
663
                sprintf(
664 4
                    'This method is responsible for validating the value(s) passed to the %s method',
665
                    lcfirst($fromMethodName)
666 4
                ),
667 4
                self::ANNOTATION_LONG_LENGTH
668
            ),
669 4
            new PhpAnnotation(
670 4
                PhpAnnotation::NO_NAME,
671 4
                sprintf(
672 4
                    'This method is willingly generated in order to preserve the one-line inline validation within the %s method',
673 4
                    lcfirst($fromMethodName)
674 4
                ),
675
                self::ANNOTATION_LONG_LENGTH
676
            ),
677
            new PhpAnnotation(
678 100
                PhpAnnotation::NO_NAME,
679
                $customConstraintMessage,
680 100
                self::ANNOTATION_LONG_LENGTH
681 100
            ),
682 100
            new PhpAnnotation(self::ANNOTATION_PARAM, $constraintArgName),
683
            new PhpAnnotation(
684 100
                self::ANNOTATION_RETURN,
685
                'string A non-empty message if the values does not match the validation rules'
686
            ),
687
        ]);
688
    }
689
690
    protected function applyRules(PhpMethod $method, StructAttributeModel $attribute, string $parameterName, bool $itemType = false): self
691
    {
692
        if ($this->getGenerator()->getOptionValidation()) {
693
            $rules = new Rules($this, $method, $attribute, $this->methods);
694
            $rules->applyRules($parameterName, $itemType);
695
        }
696
697
        return $this;
698
    }
699
}
700