Passed
Pull Request — develop (#271)
by Mikaël
05:18 queued 03:02
created

Struct::addStructMethodConstruct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

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