Passed
Pull Request — develop (#273)
by Mikaël
03:19 queued 41s
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 InvalidArgumentException;
8
use WsdlToPhp\PackageGenerator\Container\Model\StructAttribute as StructAttributeContainer;
9
use WsdlToPhp\PackageGenerator\Container\PhpElement\Constant as ConstantContainer;
10
use WsdlToPhp\PackageGenerator\Container\PhpElement\Property as PropertyContainer;
11
use WsdlToPhp\PackageGenerator\File\Element\PhpFunctionParameter;
12
use WsdlToPhp\PackageGenerator\File\Validation\Rules;
13
use WsdlToPhp\PackageGenerator\Model\AbstractModel;
14
use WsdlToPhp\PackageGenerator\Model\Struct as StructModel;
15
use WsdlToPhp\PackageGenerator\Model\StructAttribute as StructAttributeModel;
16
use WsdlToPhp\PhpGenerator\Element\PhpAnnotation;
17
use WsdlToPhp\PhpGenerator\Element\PhpAnnotationBlock;
18
use WsdlToPhp\PhpGenerator\Element\PhpConstant;
19
use WsdlToPhp\PhpGenerator\Element\PhpMethod;
20
use WsdlToPhp\PhpGenerator\Element\PhpProperty;
21
22
class Struct extends AbstractModelFile
23
{
24 136
    public function setModel(AbstractModel $model): self
25
    {
26 136
        if (!$model instanceof StructModel) {
27 2
            throw new InvalidArgumentException('Model must be an instance of a Struct', __LINE__);
28
        }
29
30 134
        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...
31
    }
32
33 132
    public function getModel(): ?StructModel
34
    {
35 132
        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...
36
    }
37
38 106
    protected function defineUseStatements(): self
39
    {
40 106
        if ($this->getGenerator()->getOptionValidation()) {
41 104
            $this->getFile()->addUse(InvalidArgumentException::class, null, false);
42
        }
43
44 106
        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...
45
    }
46
47
    protected function fillClassConstants(ConstantContainer $constants): void
48
    {
49
    }
50
51
    protected function getConstantAnnotationBlock(PhpConstant $constant): ?PhpAnnotationBlock
52
    {
53
        return null;
54
    }
55
56 122
    protected function getModelAttributes(): StructAttributeContainer
57
    {
58 122
        return $this->getModel()->getProperAttributes(true);
59
    }
60
61 122
    protected function fillClassProperties(PropertyContainer $properties): void
62
    {
63
        /** @var StructAttributeModel $attribute */
64 122
        foreach ($this->getModelAttributes() as $attribute) {
65
            switch (true) {
66 102
                case $attribute->isXml():
67 4
                    $type = null;
68
69 4
                    break;
70
71
                default:
72 102
                    $type = (($attribute->isRequired() && !$attribute->isNullable()) ? '' : '?').$this->getStructAttributeTypeAsPhpType($attribute);
73
74 102
                    break;
75
            }
76
77 102
            $properties->add(
78 102
                new PhpProperty(
79 102
                    $attribute->getCleanName(),
80 102
                    $attribute->isRequired() ? PhpProperty::NO_VALUE : null,
81 102
                    $this->getGenerator()->getOptionValidation() ? PhpProperty::ACCESS_PROTECTED : PhpProperty::ACCESS_PUBLIC,
82
                    $type
83
                )
84
            );
85
        }
86
    }
87
88 102
    protected function getPropertyAnnotationBlock(PhpProperty $property): ?PhpAnnotationBlock
89
    {
90 102
        $annotationBlock = new PhpAnnotationBlock();
91 102
        $annotationBlock->addChild(sprintf('The %s', $property->getName()));
92 102
        $attribute = $this->getModel()->getAttribute($property->getName());
93 102
        if (!$attribute instanceof StructAttributeModel) {
94 2
            $attribute = $this->getModel()->getAttributeByCleanName($property->getName());
95
        }
96 102
        if ($attribute instanceof StructAttributeModel) {
97 102
            $this->defineModelAnnotationsFromWsdl($annotationBlock, $attribute);
98 102
            $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_VAR, $this->getStructAttributeTypeGetAnnotation($attribute)));
99
        }
100
101 102
        return $annotationBlock;
102
    }
103
104 106
    protected function fillClassMethods(): void
105
    {
106
        $this
107 106
            ->addStructMethodConstruct()
108 106
            ->addStructMethodsSetAndGet()
109
        ;
110
    }
111
112 106
    protected function addStructMethodConstruct(): self
113
    {
114 106
        if (0 < count($parameters = $this->getStructMethodParametersValues())) {
115 102
            $method = new PhpMethod(self::METHOD_CONSTRUCT, $parameters);
116 102
            $this->addStructMethodConstructBody($method);
117 102
            $this->methods->add($method);
118
        }
119
120 106
        return $this;
121
    }
122
123 102
    protected function addStructMethodConstructBody(PhpMethod $method): self
124
    {
125 102
        $count = $this->getModelAttributes()->count();
126 102
        foreach ($this->getModelAttributes() as $index => $attribute) {
127 102
            if (0 === $index) {
128 102
                $method->addChild('$this');
129
            }
130 102
            $this->addStructMethodConstructBodyForAttribute($method, $attribute, $count - 1 === $index);
131
        }
132
133 102
        return $this;
134
    }
135
136 102
    protected function addStructMethodConstructBodyForAttribute(PhpMethod $method, StructAttributeModel $attribute, bool $isLast): self
137
    {
138 102
        $uniqueString = $attribute->getUniqueString($attribute->getCleanName(), 'method');
139 102
        $method->addChild($method->getIndentedString(sprintf('->%s($%s)%s', $attribute->getSetterName(), lcfirst($uniqueString), $isLast ? ';' : ''), 1));
140
141 102
        return $this;
142
    }
143
144 106
    protected function getStructMethodParametersValues(): array
145
    {
146 106
        $parametersValues = [];
147 106
        foreach ($this->getModelAttributes() as $attribute) {
148 102
            $parametersValues[] = $this->getStructMethodParameter($attribute);
149
        }
150
151 106
        return $parametersValues;
152
    }
153
154 102
    protected function getStructMethodParameter(StructAttributeModel $attribute): PhpFunctionParameter
155
    {
156
        switch (true) {
157 102
            case $attribute->isXml():
158 102
            case $attribute->isList():
159 10
                $type = null;
160
161 10
                break;
162
163
            default:
164 102
                $type = (($attribute->isRequired() && !$attribute->isNullable()) ? '' : '?').$this->getStructAttributeTypeAsPhpType($attribute);
165
166 102
                break;
167
        }
168
169
        try {
170 102
            $defaultValue = $attribute->getDefaultValue();
171 102
172 102
            return new PhpFunctionParameter(
173
                lcfirst($attribute->getUniqueString($attribute->getCleanName(), 'method')),
174
                $attribute->isRequired() && !$attribute->isAChoice() ? PhpFunctionParameter::NO_VALUE : (str_contains($type, '?') ? $defaultValue ?? null : $defaultValue),
0 ignored issues
show
Bug introduced by
It seems like $type can also be of type null; however, parameter $haystack of str_contains() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

174
                $attribute->isRequired() && !$attribute->isAChoice() ? PhpFunctionParameter::NO_VALUE : (str_contains(/** @scrutinizer ignore-type */ $type, '?') ? $defaultValue ?? null : $defaultValue),
Loading history...
175
                $type,
176
                $attribute
177
            );
178
        } catch (InvalidArgumentException $exception) {
179
            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);
180
        }
181 106
    }
182
183 106
    protected function addStructMethodsSetAndGet(): self
184
    {
185 102
        foreach ($this->getModelAttributes() as $attribute) {
186 102
            $this
187 102
                ->addStructMethodGet($attribute)
188
                ->addStructMethodSet($attribute)
189
                ->addStructMethodAddTo($attribute)
190
            ;
191 106
        }
192
193
        return $this;
194 92
    }
195
196 92
    protected function addStructMethodAddTo(StructAttributeModel $attribute): self
197 38
    {
198 38
        if ($attribute->isArray()) {
199
            $method = new PhpMethod(sprintf('addTo%s', ucfirst($attribute->getCleanName())), [
200
                new PhpFunctionParameter(
201 38
                    'item',
202
                    PhpFunctionParameter::NO_VALUE,
203
                    $this->getStructAttributeTypeAsPhpType($attribute, false),
204
                    $attribute
205 38
                ),
206 38
            ], self::TYPE_SELF);
207
            $this->addStructMethodAddToBody($method, $attribute);
208
            $this->methods->add($method);
209 92
        }
210
211
        return $this;
212 38
    }
213
214 38
    protected function addStructMethodAddToBody(PhpMethod $method, StructAttributeModel $attribute): self
215 38
    {
216
        if ($this->getGenerator()->getOptionValidation()) {
217
            $this->applyRules($method, $attribute, 'item', true);
218 38
        }
219 38
220
        if ($attribute->nameIsClean()) {
221
            $assignment = sprintf('$this->%s[] = $item;', $attribute->getCleanName());
222
        } else {
223
            $assignment = sprintf('$this->%s[] = $this->{\'%s\'}[] = $item;', $attribute->getCleanName(), addslashes($attribute->getName()));
224
        }
225 38
226 38
        $method
227 38
            ->addChild($assignment)
228
            ->addChild('')
229
            ->addChild('return $this;')
230 38
        ;
231
232
        return $this;
233 102
    }
234
235 102
    protected function addStructMethodSet(StructAttributeModel $attribute): self
236 102
    {
237
        $method = new PhpMethod($attribute->getSetterName(), [
238 102
            $this->getStructMethodParameter($attribute),
239 102
        ], self::TYPE_SELF);
240
        $this->addStructMethodSetBody($method, $attribute);
241 102
        $this->methods->add($method);
242
243
        return $this;
244 102
    }
245
246 102
    protected function addStructMethodSetBody(PhpMethod $method, StructAttributeModel $attribute): self
247 102
    {
248 102
        $parameters = $method->getParameters();
249 102
        $parameter = array_shift($parameters);
250 100
        $parameterName = is_string($parameter) ? $parameter : $parameter->getName();
251
        if ($this->getGenerator()->getOptionValidation()) {
252
            $this->applyRules($method, $attribute, $parameterName);
253
        }
254 102
255 102
        return $this
256
            ->addStructMethodSetBodyAssignment($method, $attribute, $parameterName)
257
            ->addStructMethodSetBodyReturn($method)
258
        ;
259 102
    }
260
261 102
    protected function addStructMethodSetBodyAssignment(PhpMethod $method, StructAttributeModel $attribute, string $parameterName): self
262
    {
263 18
        if ($attribute->getRemovableFromRequest() || $attribute->isAChoice()) {
264 18
            $method
265 18
                ->addChild(sprintf('if (is_null($%1$s) || (is_array($%1$s) && empty($%1$s))) {', $parameterName))
266 18
                ->addChild($method->getIndentedString(sprintf('unset($this->%1$s%2$s);', $attribute->getCleanName(), $attribute->nameIsClean() ? '' : sprintf(', $this->{\'%s\'}', addslashes($attribute->getName()))), 1))
267 18
                ->addChild('} else {')
268
                ->addChild($method->getIndentedString($this->getStructMethodSetBodyAssignment($attribute, $parameterName), 1))
269
                ->addChild('}')
270 98
            ;
271
        } else {
272
            $method->addChild($this->getStructMethodSetBodyAssignment($attribute, $parameterName));
273 102
        }
274
275
        return $this;
276 102
    }
277
278
    protected function addStructMethodSetBodyReturn(PhpMethod $method): self
279 102
    {
280 102
        $method
281
            ->addChild('')
282
            ->addChild('return $this;')
283 102
        ;
284
285
        return $this;
286 102
    }
287
288 102
    protected function getStructMethodSetBodyAssignment(StructAttributeModel $attribute, string $parameterName): string
289 102
    {
290 6
        $prefix = '$';
291 6
        if ($attribute->isList()) {
292 102
            $prefix = '';
293 4
            $parameterName = sprintf('is_array($%1$s) ? implode(\' \', $%1$s) : $%1$s', $parameterName);
294 4
        } elseif ($attribute->isXml()) {
295
            $prefix = '';
296
            $parameterName = sprintf('($%1$s instanceof \DOMDocument) ? $%1$s->saveXML($%1$s->hasChildNodes() ? $%1$s->childNodes->item(0) : null) : $%1$s', $parameterName);
297 102
        }
298 102
299
        if ($attribute->nameIsClean()) {
300 2
            $assignment = sprintf('$this->%s = %s%s;', $attribute->getName(), $prefix, $parameterName);
301
        } else {
302
            $assignment = sprintf('$this->%s = $this->{\'%s\'} = %s%s;', $attribute->getCleanName(), addslashes($attribute->getName()), $prefix, $parameterName);
303 102
        }
304
305
        return $assignment;
306 102
    }
307
308 102
    protected function addStructMethodGetBody(PhpMethod $method, StructAttributeModel $attribute, string $thisAccess): self
309
    {
310
        return $this->addStructMethodGetBodyReturn($method, $attribute, $thisAccess);
311 102
    }
312
313 102
    protected function addStructMethodGetBodyReturn(PhpMethod $method, StructAttributeModel $attribute, string $thisAccess): self
314 102
    {
315
        $return = sprintf('return $this->%s;', $thisAccess);
316 4
        if ($attribute->isXml()) {
317 4
            $method
318 4
                ->addChild('$domDocument = null;')
319 4
                ->addChild(sprintf('if (!empty($this->%1$s) && $asDomDocument) {', $thisAccess))
320 4
                ->addChild($method->getIndentedString('$domDocument = new \DOMDocument(\'1.0\', \'UTF-8\');', 1))
321
                ->addChild($method->getIndentedString(sprintf('$domDocument->loadXML($this->%s);', $thisAccess), 1))
322 4
                ->addChild('}')
323
            ;
324
            if ($attribute->getRemovableFromRequest() || $attribute->isAChoice()) {
325 4
                $return = sprintf('return $asDomDocument ? $domDocument : (isset($this->%1$s) ? $this->%1$s : null);', $thisAccess);
326
            } else {
327 102
                $return = sprintf('return $asDomDocument ? $domDocument : $this->%1$s;', $thisAccess);
328 18
            }
329
        } elseif ($attribute->getRemovableFromRequest() || $attribute->isAChoice()) {
330 102
            $return = sprintf('return $this->%s ?? null;', $thisAccess);
331
        }
332 102
        $method->addChild($return);
333
334
        return $this;
335 102
    }
336
337
    protected function addStructMethodGet(StructAttributeModel $attribute): self
338
    {
339 102
        switch (true) {
340 4
            // it can either be a string, a DOMDocument or null...
341
            case $attribute->isXml():
342 4
                $returnType = '';
343
344
                break;
345 102
346
            default:
347 102
                $returnType = (!$attribute->getRemovableFromRequest() && !$attribute->isAChoice() && $attribute->isRequired() ? '' : '?').$this->getStructAttributeTypeAsPhpType($attribute);
348
349
                break;
350 102
        }
351 102
352 102
        $method = new PhpMethod(
353
            $attribute->getGetterName(),
354
            $this->getStructMethodGetParameters($attribute),
355 102
            $returnType
356 102
        );
357
        if ($attribute->nameIsClean()) {
358 2
            $thisAccess = sprintf('%s', $attribute->getName());
359
        } else {
360 102
            $thisAccess = sprintf('{\'%s\'}', addslashes($attribute->getName()));
361 102
        }
362
        $this->addStructMethodGetBody($method, $attribute, $thisAccess);
363 102
        $this->methods->add($method);
364
365
        return $this;
366 102
    }
367
368 102
    protected function getStructMethodGetParameters(StructAttributeModel $attribute): array
369 102
    {
370 4
        $parameters = [];
371
        if ($attribute->isXml()) {
372
            $parameters[] = new PhpFunctionParameter('asDomDocument', false, self::TYPE_BOOL, $attribute);
373 102
        }
374
375
        return $parameters;
376 102
    }
377
378 102
    protected function getMethodAnnotationBlock(PhpMethod $method): ?PhpAnnotationBlock
379
    {
380
        return $this->getStructMethodAnnotationBlock($method);
381 102
    }
382
383 102
    protected function getStructMethodAnnotationBlock(PhpMethod $method): ?PhpAnnotationBlock
384
    {
385 102
        $annotationBlock = null;
386 102
387 102
        switch ($method->getName()) {
388
            case self::METHOD_CONSTRUCT:
389 102
                $annotationBlock = $this->getStructMethodConstructAnnotationBlock();
390
391 102
                break;
392 102
393 102
            case 0 === mb_strpos($method->getName(), 'get'):
394
            case 0 === mb_strpos($method->getName(), 'set'):
395 102
                $annotationBlock = $this->getStructMethodsSetAndGetAnnotationBlock($method);
396
397 66
                break;
398 38
399
            case 0 === mb_strpos($method->getName(), 'addTo'):
400 38
                $annotationBlock = $this->getStructMethodsAddToAnnotationBlock($method);
401
402 66
                break;
403 4
404
            case false !== mb_strpos($method->getName(), 'ForUnionConstraintsFrom'):
405 4
                $annotationBlock = $this->getStructMethodsValidateUnionAnnotationBlock($method);
406
407 62
                break;
408 58
409
            case false !== mb_strpos($method->getName(), 'ForArrayConstraintsFrom'):
410 58
                $annotationBlock = $this->getStructMethodsValidateArrayAnnotationBlock($method);
411
412 12
                break;
413 8
414
            case false !== mb_strpos($method->getName(), 'ForChoiceConstraintsFrom'):
415 8
                $annotationBlock = $this->getStructMethodsValidateChoiceAnnotationBlock($method);
416
417 4
                break;
418 4
419
            case false !== mb_strpos($method->getName(), 'MaxLengthConstraintFrom'):
420 4
                $annotationBlock = $this->getStructMethodsValidateLengthAnnotationBlock($method, 'max');
421
422 4
                break;
423 4
424
            case false !== mb_strpos($method->getName(), 'MinLengthConstraintFrom'):
425 4
                $annotationBlock = $this->getStructMethodsValidateLengthAnnotationBlock($method, 'min');
426
427
                break;
428
429
            case false !== mb_strpos($method->getName(), 'LengthConstraintFrom'):
430
                $annotationBlock = $this->getStructMethodsValidateLengthAnnotationBlock($method);
431
432
                break;
433 102
        }
434
435
        return $annotationBlock;
436 102
    }
437
438 102
    protected function getStructMethodConstructAnnotationBlock(): PhpAnnotationBlock
439 102
    {
440
        $annotationBlock = new PhpAnnotationBlock([
441 102
            sprintf('Constructor method for %s', $this->getModel()->getName()),
442
        ]);
443 102
        $this->addStructPropertiesToAnnotationBlock($annotationBlock);
444
445
        return $annotationBlock;
446 102
    }
447
448 102
    protected function getStructMethodsSetAndGetAnnotationBlock(PhpMethod $method): PhpAnnotationBlock
449 102
    {
450 102
        $parameters = $method->getParameters();
451
        $setOrGet = mb_strtolower(mb_substr($method->getName(), 0, 3));
452 102
        $parameter = array_shift($parameters);
453 102
        // Only set parameter must be based on a potential PhpFunctionParameter
454
        if ($parameter instanceof PhpFunctionParameter && 'set' === $setOrGet) {
455 102
            $parameterName = ucfirst($parameter->getName());
456
        } else {
457
            $parameterName = mb_substr($method->getName(), 3);
458
        }
459
        /**
460 102
         * 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.
461 102
         */
462 102
        $parameterName = preg_replace('/(_\d+)/', '', $parameterName);
463 42
        $attribute = $this->getModel()->getAttribute($parameterName);
464
        if (!$attribute instanceof StructAttributeModel) {
465 102
            $attribute = $this->getModel()->getAttributeByCleanName($parameterName);
466 42
        }
467 42
        if (!$attribute instanceof StructAttributeModel) {
468 42
            $parameterName = lcfirst($parameterName);
469 4
            $attribute = $this->getModel()->getAttribute($parameterName);
470
            if (!$attribute instanceof StructAttributeModel) {
471
                $attribute = $this->getModel()->getAttributeByCleanName($parameterName);
472 102
            }
473 102
        }
474 102
        $setValueAnnotation = '%s %s value';
475 102
        $annotationBlock = new PhpAnnotationBlock();
476 102
        if ($attribute instanceof StructAttributeModel) {
477 2
            $annotationBlock->addChild(sprintf($setValueAnnotation, ucfirst($setOrGet), $parameterName));
478 2
            $this->addStructMethodsSetAndGetAnnotationBlockFromStructAttribute($setOrGet, $annotationBlock, $attribute);
479 2
        } elseif (!$attribute) {
0 ignored issues
show
introduced by
$attribute is of type null, thus it always evaluated to false.
Loading history...
480
            $annotationBlock->addChild(sprintf($setValueAnnotation, ucfirst($setOrGet), lcfirst($parameterName)));
481
            $this->addStructMethodsSetAndGetAnnotationBlockFromScalar($setOrGet, $annotationBlock, $parameterName);
482 102
        }
483
484
        return $annotationBlock;
485 102
    }
486
487 102
    protected function addStructMethodsSetAndGetAnnotationBlockFromStructAttribute(string $setOrGet, PhpAnnotationBlock $annotationBlock, StructAttributeModel $attribute): self
488 102
    {
489 102
        switch ($setOrGet) {
490 10
            case 'set':
491
                if ($attribute->getRemovableFromRequest()) {
492 102
                    $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');
493 8
                }
494
                if ($attribute->isAChoice()) {
495 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');
496
                }
497 4
                if ($attribute->isXml()) {
498 4
                    $annotationBlock
499 4
                        ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMDocument::hasChildNodes()'))
500
                        ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMDocument::saveXML()'))
501
                        ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMNode::item()'))
502 102
                    ;
503 100
                }
504 8
                if ($this->getGenerator()->getOptionValidation()) {
505
                    if ($attribute->isAChoice()) {
506 100
                        $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, InvalidArgumentException::class));
507
                    }
508 48
                    if (($model = $this->getRestrictionFromStructAttribute($attribute)) instanceof StructModel) {
509 48
                        $annotationBlock
510 48
                            ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_VALUE_IS_VALID)))
511
                            ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_GET_VALID_VALUES)))
512 98
                            ->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, InvalidArgumentException::class))
513 52
                        ;
514
                    } elseif ($attribute->isArray()) {
515
                        $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, InvalidArgumentException::class));
516 102
                    }
517
                }
518 102
                $this->addStructMethodsSetAnnotationBlock($annotationBlock, $this->getStructAttributeTypeSetAnnotation($attribute, false), lcfirst($attribute->getCleanName()));
519
520 102
                break;
521 102
522 10
            case 'get':
523
                if ($attribute->getRemovableFromRequest()) {
524
                    $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)');
525 102
                }
526 102
                $this
527
                    ->addStructMethodsGetAnnotationBlockFromXmlAttribute($annotationBlock, $attribute)
528
                    ->addStructMethodsGetAnnotationBlock($annotationBlock, $this->getStructAttributeTypeGetAnnotation($attribute, true, $attribute->isAChoice()))
529 102
                ;
530
531
                break;
532 102
        }
533
534
        return $this;
535 2
    }
536
537 2
    protected function addStructMethodsSetAndGetAnnotationBlockFromScalar(string $setOrGet, PhpAnnotationBlock $annotationBlock, string $attributeName): self
538 2
    {
539 2
        switch ($setOrGet) {
540
            case 'set':
541 2
                $this->addStructMethodsSetAnnotationBlock($annotationBlock, lcfirst($attributeName), lcfirst($attributeName));
542
543 2
                break;
544 2
545
            case 'get':
546 2
                $this->addStructMethodsGetAnnotationBlock($annotationBlock, lcfirst($attributeName));
547
548
                break;
549 2
        }
550
551
        return $this;
552 102
    }
553
554
    protected function addStructMethodsSetAnnotationBlock(PhpAnnotationBlock $annotationBlock, string $type, string $name): self
555 102
    {
556 102
        $annotationBlock
557
            ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $%s', $type, $name)))
558
            ->addChild(new PhpAnnotation(self::ANNOTATION_RETURN, $this->getModel()->getPackagedName(true)))
559 102
        ;
560
561
        return $this;
562 102
    }
563
564 102
    protected function addStructMethodsGetAnnotationBlock(PhpAnnotationBlock $annotationBlock, string $attributeType): self
565
    {
566 102
        $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_RETURN, $attributeType));
567
568
        return $this;
569 102
    }
570
571 102
    protected function addStructMethodsGetAnnotationBlockFromXmlAttribute(PhpAnnotationBlock $annotationBlock, StructAttributeModel $attribute): self
572
    {
573 4
        if ($attribute->isXml()) {
574 4
            $annotationBlock
575
                ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMDocument::loadXML()'))
576
                ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, 'bool $asDomDocument true: returns \DOMDocument, false: returns XML string'))
577
            ;
578 102
        }
579
580
        return $this;
581 102
    }
582
583
    protected function addStructPropertiesToAnnotationBlock(PhpAnnotationBlock $annotationBlock): self
584 102
    {
585 102
        return $this
586
            ->addStructPropertiesToAnnotationBlockUses($annotationBlock)
587
            ->addStructPropertiesToAnnotationBlockParams($annotationBlock)
588
        ;
589 102
    }
590
591 102
    protected function addStructPropertiesToAnnotationBlockUses(PhpAnnotationBlock $annotationBlock): self
592 102
    {
593
        foreach ($this->getModelAttributes() as $attribute) {
594
            $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $this->getModel()->getPackagedName(), $attribute->getSetterName())));
595 102
        }
596
597
        return $this;
598 102
    }
599
600 102
    protected function addStructPropertiesToAnnotationBlockParams(PhpAnnotationBlock $annotationBlock): self
601 102
    {
602
        foreach ($this->getModelAttributes() as $attribute) {
603
            $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $%s', $this->getStructAttributeTypeSetAnnotation($attribute, false), lcfirst($attribute->getCleanName()))));
604 102
        }
605
606
        return $this;
607 38
    }
608
609 38
    protected function getStructMethodsAddToAnnotationBlock(PhpMethod $method): PhpAnnotationBlock
610
    {
611 38
        $methodParameters = $method->getParameters();
612 38
        /** @var PhpFunctionParameter $firstParameter */
613 38
        $firstParameter = array_shift($methodParameters);
614 38
        $attribute = $this->getModel()->getAttribute($firstParameter->getModel()->getName());
615 38
        $annotationBlock = new PhpAnnotationBlock();
616 38
        if ($attribute instanceof StructAttributeModel) {
617 38
            $model = $this->getRestrictionFromStructAttribute($attribute);
618
            $annotationBlock->addChild(sprintf('Add item to %s value', $attribute->getCleanName()));
619 2
            if ($model instanceof StructModel) {
620 2
                $annotationBlock
621
                    ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_VALUE_IS_VALID)))
622
                    ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_GET_VALID_VALUES)))
623
                ;
624 38
            }
625 38
            $annotationBlock
626 38
                ->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, InvalidArgumentException::class))
627
                ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $item', $this->getStructAttributeTypeSetAnnotation($attribute, false, true))))
628
                ->addChild(new PhpAnnotation(self::ANNOTATION_RETURN, $this->getModel()->getPackagedName(true)))
629
            ;
630 38
        }
631
632
        return $annotationBlock;
633 58
    }
634
635 58
    protected function getStructMethodsValidateArrayAnnotationBlock(PhpMethod $method): PhpAnnotationBlock
636
    {
637 58
        $methodName = lcfirst(mb_substr($method->getName(), mb_strpos($method->getName(), 'ForArrayConstraintsFrom') + mb_strlen('ForArrayConstraintsFrom')));
638 58
639 58
        return new PhpAnnotationBlock([
640 58
            new PhpAnnotation(PhpAnnotation::NO_NAME, sprintf('This method is responsible for validating the values passed to the %s method', $methodName), self::ANNOTATION_LONG_LENGTH),
641 58
            new PhpAnnotation(PhpAnnotation::NO_NAME, sprintf('This method is willingly generated in order to preserve the one-line inline validation within the %s method', $methodName), self::ANNOTATION_LONG_LENGTH),
642
            new PhpAnnotation(self::ANNOTATION_PARAM, 'array $values'),
643
            new PhpAnnotation(self::ANNOTATION_RETURN, 'string A non-empty message if the values does not match the validation rules'),
644
        ]);
645 4
    }
646
647 4
    protected function getStructMethodsValidateUnionAnnotationBlock(PhpMethod $method): PhpAnnotationBlock
648
    {
649 4
        $methodName = lcfirst(mb_substr($method->getName(), mb_strpos($method->getName(), 'ForUnionConstraintsFrom') + mb_strlen('ForUnionConstraintsFrom')));
650 4
651 4
        return new PhpAnnotationBlock([
652 4
            new PhpAnnotation(PhpAnnotation::NO_NAME, sprintf('This method is responsible for validating the value passed to the %s method', $methodName), self::ANNOTATION_LONG_LENGTH),
653 4
            new PhpAnnotation(PhpAnnotation::NO_NAME, sprintf('This method is willingly generated in order to preserve the one-line inline validation within the %s method', $methodName), self::ANNOTATION_LONG_LENGTH),
654 4
            new PhpAnnotation(PhpAnnotation::NO_NAME, sprintf('This is a set of validation rules based on the union types associated to the property being set by the %s method', $methodName), self::ANNOTATION_LONG_LENGTH),
655
            new PhpAnnotation(self::ANNOTATION_PARAM, 'mixed $value'),
656
            new PhpAnnotation(self::ANNOTATION_RETURN, 'string A non-empty message if the values does not match the validation rules'),
657
        ]);
658 8
    }
659
660 8
    protected function getStructMethodsValidateChoiceAnnotationBlock(PhpMethod $method): PhpAnnotationBlock
661
    {
662 8
        $methodName = lcfirst(mb_substr($method->getName(), mb_strpos($method->getName(), 'ForChoiceConstraintsFrom') + mb_strlen('ForChoiceConstraintsFrom')));
663 8
664 8
        return new PhpAnnotationBlock([
665 8
            new PhpAnnotation(PhpAnnotation::NO_NAME, sprintf('This method is responsible for validating the value passed to the %s method', $methodName), self::ANNOTATION_LONG_LENGTH),
666 8
            new PhpAnnotation(PhpAnnotation::NO_NAME, sprintf('This method is willingly generated in order to preserve the one-line inline validation within the %s method', $methodName), self::ANNOTATION_LONG_LENGTH),
667 8
            new PhpAnnotation(PhpAnnotation::NO_NAME, 'This has to validate that the property which is being set is the only one among the given choices', self::ANNOTATION_LONG_LENGTH),
668
            new PhpAnnotation(self::ANNOTATION_PARAM, 'mixed $value'),
669
            new PhpAnnotation(self::ANNOTATION_RETURN, 'string A non-empty message if the values does not match the validation rules'),
670
        ]);
671 4
    }
672
673 4
    protected function getStructMethodsValidateLengthAnnotationBlock(PhpMethod $method, string $type = ''): PhpAnnotationBlock
674 4
    {
675
        $replace = sprintf('%sLengthConstraintFrom', ucfirst($type));
676 4
        $methodName = lcfirst(mb_substr($method->getName(), mb_strpos($method->getName(), $replace) + mb_strlen($replace)));
677 4
678 4
        return new PhpAnnotationBlock([
679 4
            new PhpAnnotation(PhpAnnotation::NO_NAME, sprintf('This method is responsible for validating the value passed to the %s method', $methodName), self::ANNOTATION_LONG_LENGTH),
680 4
            new PhpAnnotation(PhpAnnotation::NO_NAME, sprintf('This method is willingly generated in order to preserve the one-line inline validation within the %s method', $methodName), self::ANNOTATION_LONG_LENGTH),
681 4
            new PhpAnnotation(PhpAnnotation::NO_NAME, 'This has to validate that the items contained by the array match the length constraint', self::ANNOTATION_LONG_LENGTH),
682
            new PhpAnnotation(self::ANNOTATION_PARAM, 'mixed $values'),
683
            new PhpAnnotation(self::ANNOTATION_RETURN, 'string A non-empty message if the values does not match the validation rules'),
684
        ]);
685 100
    }
686
687 100
    protected function applyRules(PhpMethod $method, StructAttributeModel $attribute, string $parameterName, bool $itemType = false): void
688 100
    {
689 100
        if ($this->getGenerator()->getOptionValidation()) {
690
            $rules = new Rules($this, $method, $attribute, $this->methods);
691
            $rules->applyRules($parameterName, $itemType);
692
        }
693
    }
694
}
695