Struct::getMethodAnnotationBlock()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

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