Passed
Push — master ( f0393b...d8ac0a )
by Mikaël
12:30 queued 13s
created

Struct::addStructPropertiesToAnnotationBlockUses()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

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