Passed
Push — develop ( 393819...8447c9 )
by Mikaël
74:52 queued 24:50
created

Struct::fillClassConstants()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 0
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 2
ccs 1
cts 1
cp 1
crap 1
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 312
    public function setModel(AbstractModel $model): self
25
    {
26 312
        if (!$model instanceof StructModel) {
27
            throw new InvalidArgumentException('Model must be an instance of a Struct', __LINE__);
28
        }
29
30
        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
    protected function defineUseStatements(): self
34
    {
35
        if ($this->getGenerator()->getOptionValidation()) {
36 360
            $this->getFile()->addUse(InvalidArgumentException::class, null, false);
37
        }
38 360
39
        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
    protected function fillClassConstants(ConstantContainer $constants): void
43 360
    {
44
    }
45 360
46 300
    protected function getConstantAnnotationBlock(PhpConstant $constant): ?PhpAnnotationBlock
47 180
    {
48 360
    }
49
50
    protected function getModelAttributes(): StructAttributeContainer
51
    {
52 300
        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

52
        return $this->getModel()->/** @scrutinizer ignore-call */ getProperAttributes(true);
Loading history...
53
    }
54 300
55 300
    protected function fillClassProperties(PropertyContainer $properties): void
56 300
    {
57 300
        /** @var StructAttributeModel $attribute */
58 6
        foreach ($this->getModelAttributes() as $attribute) {
59 3
            switch (true) {
60 300
                case $attribute->isXml():
61 300
                    $type = null;
62 300
63 150
                    break;
64 300
65
                default:
66 312
                    $type = ($attribute->isRequired() || $attribute->isArray() ? '' : '?').$this->getStructAttributeTypeAsPhpType($attribute);
67
68 156
                    break;
69 312
            }
70 312
71 312
            $properties->add(
72
                new PhpProperty(
73
                    $attribute->getCleanName(),
74
                    $attribute->isArray() ? [] : ($attribute->isRequired() ? PhpProperty::NO_VALUE : null),
75 312
                    $this->getGenerator()->getOptionValidation() ? PhpProperty::ACCESS_PROTECTED : PhpProperty::ACCESS_PUBLIC,
76
                    $type
77 312
                )
78 300
            );
79 300
        }
80 300
    }
81 150
82 312
    protected function getPropertyAnnotationBlock(PhpProperty $property): ?PhpAnnotationBlock
83
    {
84
        $annotationBlock = new PhpAnnotationBlock();
85
        $annotationBlock->addChild(sprintf('The %s', $property->getName()));
86
        $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

86
        $attribute = $this->getModel()->/** @scrutinizer ignore-call */ getAttribute($property->getName());
Loading history...
87
        if (!$attribute instanceof StructAttributeModel) {
88 300
            $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

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