Passed
Pull Request — develop (#279)
by Mikaël
09:14 queued 07:02
created

Struct::addStructMethodSetBodyAssignment()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

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