Passed
Push — feature/issue-264 ( 16c0d2 )
by Mikaël
31:40 queued 28:41
created

Struct::addStructMethodConstructBodyForAttribute()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
c 0
b 0
f 0
nc 1
nop 3
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace WsdlToPhp\PackageGenerator\File;
6
7
use InvalidArgumentException;
8
use WsdlToPhp\PackageGenerator\Container\Model\StructAttribute as StructAttributeContainer;
9
use WsdlToPhp\PackageGenerator\Container\PhpElement\Constant as ConstantContainer;
10
use WsdlToPhp\PackageGenerator\Container\PhpElement\Property as PropertyContainer;
11
use WsdlToPhp\PackageGenerator\File\Element\PhpFunctionParameter;
12
use WsdlToPhp\PackageGenerator\File\Validation\Rules;
13
use WsdlToPhp\PackageGenerator\Model\AbstractModel;
14
use WsdlToPhp\PackageGenerator\Model\Struct as StructModel;
15
use WsdlToPhp\PackageGenerator\Model\StructAttribute as StructAttributeModel;
16
use WsdlToPhp\PhpGenerator\Element\PhpAnnotation;
17
use WsdlToPhp\PhpGenerator\Element\PhpAnnotationBlock;
18
use WsdlToPhp\PhpGenerator\Element\PhpConstant;
19
use WsdlToPhp\PhpGenerator\Element\PhpMethod;
20
use WsdlToPhp\PhpGenerator\Element\PhpProperty;
21
22
class Struct extends AbstractModelFile
23
{
24 132
    public function setModel(AbstractModel $model): self
25
    {
26 132
        if (!$model instanceof StructModel) {
27 2
            throw new InvalidArgumentException('Model must be an instance of a Struct', __LINE__);
28
        }
29
30 130
        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 106
    protected function defineUseStatements(): self
34
    {
35 106
        if ($this->getGenerator()->getOptionValidation()) {
36 104
            $this->getFile()->addUse(InvalidArgumentException::class, null, false);
37
        }
38
39 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...
40
    }
41
42 106
    protected function fillClassConstants(ConstantContainer $constants): void
43
    {
44 106
    }
45
46
    protected function getConstantAnnotationBlock(PhpConstant $constant): ?PhpAnnotationBlock
47
    {
48
        return null;
49
    }
50
51 122
    protected function getModelAttributes(): StructAttributeContainer
52
    {
53 122
        return $this->getModel()->getProperAttributes(true);
0 ignored issues
show
Bug introduced by
The method getProperAttributes() does not exist on WsdlToPhp\PackageGenerator\Model\AbstractModel. It seems like you code against a sub-type of WsdlToPhp\PackageGenerator\Model\AbstractModel such as WsdlToPhp\PackageGenerator\Model\Struct. ( Ignorable by Annotation )

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

53
        return $this->getModel()->/** @scrutinizer ignore-call */ getProperAttributes(true);
Loading history...
54
    }
55
56 122
    protected function fillClassProperties(PropertyContainer $properties): void
57
    {
58
        /** @var StructAttributeModel $attribute */
59 122
        foreach ($this->getModelAttributes() as $attribute) {
60
            switch (true) {
61 102
                case $attribute->isXml():
62 4
                    $type = null;
63
64 4
                    break;
65
66
                default:
67 102
                    $type = (($attribute->isRequired() && !$attribute->isNullable()) ? '' : '?').$this->getStructAttributeTypeAsPhpType($attribute);
68
69 102
                    break;
70
            }
71
72 102
            $properties->add(
73 102
                new PhpProperty(
74 102
                    $attribute->getCleanName(),
75 102
                    $attribute->isRequired() ? PhpProperty::NO_VALUE : null,
76 102
                    $this->getGenerator()->getOptionValidation() ? PhpProperty::ACCESS_PROTECTED : PhpProperty::ACCESS_PUBLIC,
77
                    $type
78
                )
79
            );
80
        }
81 122
    }
82
83 102
    protected function getPropertyAnnotationBlock(PhpProperty $property): ?PhpAnnotationBlock
84
    {
85 102
        $annotationBlock = new PhpAnnotationBlock();
86 102
        $annotationBlock->addChild(sprintf('The %s', $property->getName()));
87 102
        $attribute = $this->getModel()->getAttribute($property->getName());
0 ignored issues
show
Bug introduced by
The method getAttribute() does not exist on WsdlToPhp\PackageGenerator\Model\AbstractModel. It seems like you code against a sub-type of WsdlToPhp\PackageGenerator\Model\AbstractModel such as WsdlToPhp\PackageGenerator\Model\Struct. ( Ignorable by Annotation )

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

87
        $attribute = $this->getModel()->/** @scrutinizer ignore-call */ getAttribute($property->getName());
Loading history...
88 102
        if (!$attribute instanceof StructAttributeModel) {
89 2
            $attribute = $this->getModel()->getAttributeByCleanName($property->getName());
0 ignored issues
show
Bug introduced by
The method getAttributeByCleanName() does not exist on WsdlToPhp\PackageGenerator\Model\AbstractModel. It seems like you code against a sub-type of WsdlToPhp\PackageGenerator\Model\AbstractModel such as WsdlToPhp\PackageGenerator\Model\Struct. ( Ignorable by Annotation )

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

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