Passed
Push — develop ( 333d24...82ab60 )
by Mikaël
09:54
created

Struct::getModel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
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
            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
         * 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.
459
         */
460 102
        $parameterName = preg_replace('/(_\d+)/', '', $parameterName);
461 102
        $attribute = $this->getModel()->getAttribute($parameterName);
462 102
        if (!$attribute instanceof StructAttributeModel) {
463 42
            $attribute = $this->getModel()->getAttributeByCleanName($parameterName);
464
        }
465 102
        if (!$attribute instanceof StructAttributeModel) {
466 42
            $parameterName = lcfirst($parameterName);
467 42
            $attribute = $this->getModel()->getAttribute($parameterName);
468 42
            if (!$attribute instanceof StructAttributeModel) {
469 4
                $attribute = $this->getModel()->getAttributeByCleanName($parameterName);
470
            }
471
        }
472 102
        $setValueAnnotation = '%s %s value';
473 102
        $annotationBlock = new PhpAnnotationBlock();
474 102
        if ($attribute instanceof StructAttributeModel) {
475 102
            $annotationBlock->addChild(sprintf($setValueAnnotation, ucfirst($setOrGet), $parameterName));
476 102
            $this->addStructMethodsSetAndGetAnnotationBlockFromStructAttribute($setOrGet, $annotationBlock, $attribute);
477 2
        } elseif (!$attribute) {
0 ignored issues
show
introduced by
$attribute is of type null, thus it always evaluated to false.
Loading history...
478 2
            $annotationBlock->addChild(sprintf($setValueAnnotation, ucfirst($setOrGet), lcfirst($parameterName)));
479 2
            $this->addStructMethodsSetAndGetAnnotationBlockFromScalar($setOrGet, $annotationBlock, $parameterName);
480
        }
481
482 102
        return $annotationBlock;
483
    }
484
485 102
    protected function addStructMethodsSetAndGetAnnotationBlockFromStructAttribute(string $setOrGet, PhpAnnotationBlock $annotationBlock, StructAttributeModel $attribute): self
486
    {
487 102
        switch ($setOrGet) {
488 102
            case 'set':
489 102
                if ($attribute->getRemovableFromRequest()) {
490 10
                    $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');
491
                }
492 102
                if ($attribute->isAChoice()) {
493 8
                    $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');
494
                }
495 102
                if ($attribute->isXml()) {
496
                    $annotationBlock
497 4
                        ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMDocument::hasChildNodes()'))
498 4
                        ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMDocument::saveXML()'))
499 4
                        ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMNode::item()'))
500
                    ;
501
                }
502 102
                if ($this->getGenerator()->getOptionValidation()) {
503 100
                    if ($attribute->isAChoice()) {
504 8
                        $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, InvalidArgumentException::class));
505
                    }
506 100
                    if (($model = $this->getRestrictionFromStructAttribute($attribute)) instanceof StructModel) {
507
                        $annotationBlock
508 48
                            ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_VALUE_IS_VALID)))
509 48
                            ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_GET_VALID_VALUES)))
510 48
                            ->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, InvalidArgumentException::class))
511
                        ;
512 98
                    } elseif ($attribute->isArray()) {
513 52
                        $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, InvalidArgumentException::class));
514
                    }
515
                }
516 102
                $this->addStructMethodsSetAnnotationBlock($annotationBlock, $this->getStructAttributeTypeSetAnnotation($attribute, false), lcfirst($attribute->getCleanName()));
517
518 102
                break;
519
520 102
            case 'get':
521 102
                if ($attribute->getRemovableFromRequest()) {
522 10
                    $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)');
523
                }
524
                $this
525 102
                    ->addStructMethodsGetAnnotationBlockFromXmlAttribute($annotationBlock, $attribute)
526 102
                    ->addStructMethodsGetAnnotationBlock($annotationBlock, $this->getStructAttributeTypeGetAnnotation($attribute))
527
                ;
528
529 102
                break;
530
        }
531
532 102
        return $this;
533
    }
534
535 2
    protected function addStructMethodsSetAndGetAnnotationBlockFromScalar(string $setOrGet, PhpAnnotationBlock $annotationBlock, string $attributeName): self
536
    {
537 2
        switch ($setOrGet) {
538 2
            case 'set':
539 2
                $this->addStructMethodsSetAnnotationBlock($annotationBlock, lcfirst($attributeName), lcfirst($attributeName));
540
541 2
                break;
542
543 2
            case 'get':
544 2
                $this->addStructMethodsGetAnnotationBlock($annotationBlock, lcfirst($attributeName));
545
546 2
                break;
547
        }
548
549 2
        return $this;
550
    }
551
552 102
    protected function addStructMethodsSetAnnotationBlock(PhpAnnotationBlock $annotationBlock, string $type, string $name): self
553
    {
554
        $annotationBlock
555 102
            ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $%s', $type, $name)))
556 102
            ->addChild(new PhpAnnotation(self::ANNOTATION_RETURN, $this->getModel()->getPackagedName(true)))
557
        ;
558
559 102
        return $this;
560
    }
561
562 102
    protected function addStructMethodsGetAnnotationBlock(PhpAnnotationBlock $annotationBlock, string $attributeType): self
563
    {
564 102
        $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_RETURN, $attributeType));
565
566 102
        return $this;
567
    }
568
569 102
    protected function addStructMethodsGetAnnotationBlockFromXmlAttribute(PhpAnnotationBlock $annotationBlock, StructAttributeModel $attribute): self
570
    {
571 102
        if ($attribute->isXml()) {
572
            $annotationBlock
573 4
                ->addChild(new PhpAnnotation(self::ANNOTATION_USES, '\DOMDocument::loadXML()'))
574 4
                ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, 'bool $asDomDocument true: returns \DOMDocument, false: returns XML string'))
575
            ;
576
        }
577
578 102
        return $this;
579
    }
580
581 102
    protected function addStructPropertiesToAnnotationBlock(PhpAnnotationBlock $annotationBlock): self
582
    {
583
        return $this
584 102
            ->addStructPropertiesToAnnotationBlockUses($annotationBlock)
585 102
            ->addStructPropertiesToAnnotationBlockParams($annotationBlock)
586
        ;
587
    }
588
589 102
    protected function addStructPropertiesToAnnotationBlockUses(PhpAnnotationBlock $annotationBlock): self
590
    {
591 102
        foreach ($this->getModelAttributes() as $attribute) {
592 102
            $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $this->getModel()->getPackagedName(), $attribute->getSetterName())));
593
        }
594
595 102
        return $this;
596
    }
597
598 102
    protected function addStructPropertiesToAnnotationBlockParams(PhpAnnotationBlock $annotationBlock): self
599
    {
600 102
        foreach ($this->getModelAttributes() as $attribute) {
601 102
            $annotationBlock->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $%s', $this->getStructAttributeTypeSetAnnotation($attribute, false), lcfirst($attribute->getCleanName()))));
602
        }
603
604 102
        return $this;
605
    }
606
607 38
    protected function getStructMethodsAddToAnnotationBlock(PhpMethod $method): PhpAnnotationBlock
608
    {
609 38
        $methodParameters = $method->getParameters();
610
        /** @var PhpFunctionParameter $firstParameter */
611 38
        $firstParameter = array_shift($methodParameters);
612 38
        $attribute = $this->getModel()->getAttribute($firstParameter->getModel()->getName());
613 38
        $annotationBlock = new PhpAnnotationBlock();
614 38
        if ($attribute instanceof StructAttributeModel) {
615 38
            $model = $this->getRestrictionFromStructAttribute($attribute);
616 38
            $annotationBlock->addChild(sprintf('Add item to %s value', $attribute->getCleanName()));
617 38
            if ($model instanceof StructModel) {
618
                $annotationBlock
619 2
                    ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_VALUE_IS_VALID)))
620 2
                    ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $model->getPackagedName(true), StructEnum::METHOD_GET_VALID_VALUES)))
621
                ;
622
            }
623
            $annotationBlock
624 38
                ->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, InvalidArgumentException::class))
625 38
                ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $item', $this->getStructAttributeTypeSetAnnotation($attribute, false, true))))
626 38
                ->addChild(new PhpAnnotation(self::ANNOTATION_RETURN, $this->getModel()->getPackagedName(true)))
627
            ;
628
        }
629
630 38
        return $annotationBlock;
631
    }
632
633 58
    protected function getStructMethodsValidateArrayAnnotationBlock(PhpMethod $method): PhpAnnotationBlock
634
    {
635 58
        $methodName = lcfirst(mb_substr($method->getName(), mb_strpos($method->getName(), 'ForArrayConstraintsFrom') + mb_strlen('ForArrayConstraintsFrom')));
636
637 58
        return new PhpAnnotationBlock([
638 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),
639 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),
640 58
            new PhpAnnotation(self::ANNOTATION_PARAM, 'array $values'),
641 58
            new PhpAnnotation(self::ANNOTATION_RETURN, 'string A non-empty message if the values does not match the validation rules'),
642
        ]);
643
    }
644
645 4
    protected function getStructMethodsValidateUnionAnnotationBlock(PhpMethod $method): PhpAnnotationBlock
646
    {
647 4
        $methodName = lcfirst(mb_substr($method->getName(), mb_strpos($method->getName(), 'ForUnionConstraintsFrom') + mb_strlen('ForUnionConstraintsFrom')));
648
649 4
        return new PhpAnnotationBlock([
650 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),
651 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),
652 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),
653 4
            new PhpAnnotation(self::ANNOTATION_PARAM, 'mixed $value'),
654 4
            new PhpAnnotation(self::ANNOTATION_RETURN, 'string A non-empty message if the values does not match the validation rules'),
655
        ]);
656
    }
657
658 8
    protected function getStructMethodsValidateChoiceAnnotationBlock(PhpMethod $method): PhpAnnotationBlock
659
    {
660 8
        $methodName = lcfirst(mb_substr($method->getName(), mb_strpos($method->getName(), 'ForChoiceConstraintsFrom') + mb_strlen('ForChoiceConstraintsFrom')));
661
662 8
        return new PhpAnnotationBlock([
663 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),
664 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),
665 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),
666 8
            new PhpAnnotation(self::ANNOTATION_PARAM, 'mixed $value'),
667 8
            new PhpAnnotation(self::ANNOTATION_RETURN, 'string A non-empty message if the values does not match the validation rules'),
668
        ]);
669
    }
670
671 4
    protected function getStructMethodsValidateLengthAnnotationBlock(PhpMethod $method, string $type = ''): PhpAnnotationBlock
672
    {
673 4
        $replace = sprintf('%sLengthConstraintFrom', ucfirst($type));
674 4
        $methodName = lcfirst(mb_substr($method->getName(), mb_strpos($method->getName(), $replace) + mb_strlen($replace)));
675
676 4
        return new PhpAnnotationBlock([
677 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),
678 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),
679 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),
680 4
            new PhpAnnotation(self::ANNOTATION_PARAM, 'mixed $values'),
681 4
            new PhpAnnotation(self::ANNOTATION_RETURN, 'string A non-empty message if the values does not match the validation rules'),
682
        ]);
683
    }
684
685 100
    protected function applyRules(PhpMethod $method, StructAttributeModel $attribute, string $parameterName, bool $itemType = false): void
686
    {
687 100
        if ($this->getGenerator()->getOptionValidation()) {
688 100
            $rules = new Rules($this, $method, $attribute, $this->methods);
689 100
            $rules->applyRules($parameterName, $itemType);
690
        }
691
    }
692
}
693