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