Passed
Push — feature/issue-124 ( f8e59e...f27344 )
by Mikaël
08:40
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 131
    public function setModel(AbstractModel $model): self
25
    {
26 131
        if (!$model instanceof StructModel) {
27 2
            throw new InvalidArgumentException('Model must be an instance of a Struct', __LINE__);
28
        }
29
30 129
        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
    /**
34
     * @todo: only add `use InvalidArgumentException` if it's used!
35
     *
36
     * @return $this
37
     */
38 121
    protected function defineUseStatements(): self
39
    {
40 121
        if ($this->getGenerator()->getOptionValidation()) {
41 118
            $this->getFile()->addUse(InvalidArgumentException::class, null, false);
42
        }
43
44 121
        return parent::defineUseStatements();
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::defineUseStatements() returns the type WsdlToPhp\PackageGenerator\File\AbstractModelFile which includes types incompatible with the type-hinted return WsdlToPhp\PackageGenerator\File\Struct.
Loading history...
45
    }
46
47 105
    protected function fillClassConstants(ConstantContainer $constants): void
48
    {
49 105
    }
50
51
    protected function getConstantAnnotationBlock(PhpConstant $constant): ?PhpAnnotationBlock
52
    {
53
    }
54
55 121
    protected function getModelAttributes(): StructAttributeContainer
56
    {
57 121
        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

57
        return $this->getModel()->/** @scrutinizer ignore-call */ getProperAttributes(true);
Loading history...
58
    }
59
60 121
    protected function fillClassProperties(PropertyContainer $properties): void
61
    {
62 121
        foreach ($this->getModelAttributes() as $attribute) {
63 101
            $properties->add(
64 101
                new PhpProperty(
65 101
                    $attribute->getCleanName(),
66 101
                    $attribute->isArray() ? [] : ($attribute->isRequired() ? PhpProperty::NO_VALUE : null),
67 101
                    $this->getGenerator()->getOptionValidation() ? PhpProperty::ACCESS_PROTECTED : PhpProperty::ACCESS_PUBLIC,
68 101
                    $attribute->isArray() ? self::TYPE_ARRAY : ($attribute->isRequired() ? '' : '?').$this->getStructAttributeTypeAsPhpType($attribute)
69
                )
70
            );
71
        }
72 121
    }
73
74 101
    protected function getPropertyAnnotationBlock(PhpProperty $property): ?PhpAnnotationBlock
75
    {
76 101
        $annotationBlock = new PhpAnnotationBlock();
77 101
        $annotationBlock->addChild(sprintf('The %s', $property->getName()));
78 101
        $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

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

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