Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.18% |
162 / 165 |
|
90.32% |
28 / 31 |
CRAP | |
0.00% |
0 / 1 |
AbstractModelFile | |
98.18% |
162 / 165 |
|
90.32% |
28 / 31 |
90 | |
0.00% |
0 / 1 |
getFileDestination | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
getDestinationFolder | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
6 | |||
writeFile | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
2 | |||
setModel | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
getModel | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getModelFromStructAttribute | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRestrictionFromStructAttribute | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
7 | |||
getStructAttributeType | |
95.24% |
20 / 21 |
|
0.00% |
0 / 1 |
14 | |||
getStructAttributeTypeAsPhpType | |
85.71% |
6 / 7 |
|
0.00% |
0 / 1 |
3.03 | |||
getValidType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
getPhpType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
addAnnotationBlock | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getModelByName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
definePackageAnnotations | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
getPackageName | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
defineGeneralAnnotations | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getClassAnnotationBlock | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getClassDeclarationLine | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getClassDeclarationLineText | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
defineModelAnnotationsFromWsdl | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
addClassElement | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
2 | |||
addDeclareDirective | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
defineNamespace | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
defineUseStatements | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
defineConstants | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
defineProperties | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
defineMethods | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
fillClassConstants | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getConstantAnnotationBlock | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
fillClassProperties | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getPropertyAnnotationBlock | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
fillClassMethods | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getMethodAnnotationBlock | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getStructAttribute | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
4 | |||
getStructAttributeTypeGetAnnotation | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
8 | |||
getStructAttributeTypeSetAnnotation | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
4 | |||
useBrackets | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace WsdlToPhp\PackageGenerator\File; |
6 | |
7 | use WsdlToPhp\PackageGenerator\ConfigurationReader\XsdTypes; |
8 | use WsdlToPhp\PackageGenerator\Container\PhpElement\Constant; |
9 | use WsdlToPhp\PackageGenerator\Container\PhpElement\Method; |
10 | use WsdlToPhp\PackageGenerator\Container\PhpElement\Property; |
11 | use WsdlToPhp\PackageGenerator\File\Utils as FileUtils; |
12 | use WsdlToPhp\PackageGenerator\Generator\Utils as GeneratorUtils; |
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\Component\PhpClass; |
17 | use WsdlToPhp\PhpGenerator\Element\PhpAnnotation; |
18 | use WsdlToPhp\PhpGenerator\Element\PhpAnnotationBlock; |
19 | use WsdlToPhp\PhpGenerator\Element\PhpConstant; |
20 | use WsdlToPhp\PhpGenerator\Element\PhpDeclare; |
21 | use WsdlToPhp\PhpGenerator\Element\PhpMethod; |
22 | use WsdlToPhp\PhpGenerator\Element\PhpProperty; |
23 | |
24 | abstract class AbstractModelFile extends AbstractFile |
25 | { |
26 | public const ANNOTATION_META_LENGTH = 250; |
27 | public const ANNOTATION_LONG_LENGTH = 1000; |
28 | public const ANNOTATION_PACKAGE = 'package'; |
29 | public const ANNOTATION_SUB_PACKAGE = 'subpackage'; |
30 | public const ANNOTATION_RETURN = 'return'; |
31 | public const ANNOTATION_USES = 'uses'; |
32 | public const ANNOTATION_PARAM = 'param'; |
33 | public const ANNOTATION_VAR = 'var'; |
34 | public const ANNOTATION_SEE = 'see'; |
35 | public const ANNOTATION_THROWS = 'throws'; |
36 | public const METHOD_CONSTRUCT = '__construct'; |
37 | public const TYPE_ARRAY = 'array'; |
38 | public const TYPE_BOOL = 'bool'; |
39 | public const TYPE_STRING = 'string'; |
40 | public const TYPE_SELF = 'self'; |
41 | |
42 | protected Method $methods; |
43 | |
44 | private ?AbstractModel $model = null; |
45 | |
46 | public function getFileDestination(bool $withSrc = true): string |
47 | { |
48 | return sprintf( |
49 | '%s%s%s', |
50 | $this->getDestinationFolder($withSrc), |
51 | $this->getModel()->getSubDirectory(), |
52 | !empty($this->getModel()->getSubDirectory()) ? '/' : '' |
53 | ); |
54 | } |
55 | |
56 | public function getDestinationFolder(bool $withSrc = true): string |
57 | { |
58 | $src = rtrim($this->generator->getOptionSrcDirname(), DIRECTORY_SEPARATOR); |
59 | |
60 | return sprintf( |
61 | '%s%s%s%s', |
62 | $this->getGenerator()->getOptionDestination(), |
63 | $withSrc && !empty($src) ? $src.DIRECTORY_SEPARATOR : '', |
64 | $this->getGenerator()->getOptionNamespaceDictatesDirectories() ? str_replace('\\', DIRECTORY_SEPARATOR, $this->getGenerator()->getOptionNamespacePrefix()) : '', |
65 | $this->getGenerator()->getOptionNamespacePrefix() && $this->getGenerator()->getOptionNamespaceDictatesDirectories() ? DIRECTORY_SEPARATOR : '' |
66 | ); |
67 | } |
68 | |
69 | public function writeFile(bool $withSrc = true): void |
70 | { |
71 | if (!$this->getModel()) { |
72 | throw new \InvalidArgumentException('You MUST define the model before being able to generate the file', __LINE__); |
73 | } |
74 | |
75 | GeneratorUtils::createDirectory($this->getFileDestination($withSrc)); |
76 | |
77 | $this |
78 | ->addDeclareDirective() |
79 | ->defineNamespace() |
80 | ->defineUseStatements() |
81 | ->addAnnotationBlock() |
82 | ->addClassElement() |
83 | ; |
84 | |
85 | parent::writeFile(); |
86 | } |
87 | |
88 | public function setModel(AbstractModel $model): self |
89 | { |
90 | $this->model = $model; |
91 | |
92 | $this |
93 | ->getFile() |
94 | ->getMainElement() |
95 | ->setName($model->getPackagedName()) |
96 | ; |
97 | |
98 | return $this; |
99 | } |
100 | |
101 | public function getModel(): ?AbstractModel |
102 | { |
103 | return $this->model; |
104 | } |
105 | |
106 | public function getModelFromStructAttribute(StructAttributeModel $attribute = null): ?StructModel |
107 | { |
108 | return $this->getStructAttribute($attribute)->getTypeStruct(); |
109 | } |
110 | |
111 | public function getRestrictionFromStructAttribute(StructAttributeModel $attribute = null): ?StructModel |
112 | { |
113 | $model = $this->getModelFromStructAttribute($attribute); |
114 | if ($model instanceof StructModel) { |
115 | // list are mainly scalar values of basic types (string, int, etc.) or of Restriction values |
116 | if ($model->isList()) { |
117 | $subModel = $this->getModelByName($model->getList()); |
118 | if ($subModel && $subModel->isRestriction()) { |
119 | $model = $subModel; |
120 | } elseif (!$model->isRestriction()) { |
121 | $model = null; |
122 | } |
123 | } elseif (!$model->isRestriction()) { |
124 | $model = null; |
125 | } |
126 | } |
127 | |
128 | return $model; |
129 | } |
130 | |
131 | public function getStructAttributeType(StructAttributeModel $attribute = null, bool $namespaced = false, bool $returnArrayType = true): string |
132 | { |
133 | $attribute = $this->getStructAttribute($attribute); |
134 | |
135 | if (!$attribute instanceof StructAttributeModel) { |
136 | throw new \InvalidArgumentException('Could not find any valid StructAttribute'); |
137 | } |
138 | |
139 | if ($returnArrayType && $attribute->isArray()) { |
140 | return self::TYPE_ARRAY; |
141 | } |
142 | |
143 | $inheritance = $attribute->getInheritance(); |
144 | $type = empty($inheritance) ? $attribute->getType() : $inheritance; |
145 | |
146 | if (!empty($type) && ($struct = $this->getGenerator()->getStructByName($type))) { |
147 | $inheritance = $struct->getTopInheritance(); |
148 | if (!empty($inheritance)) { |
149 | $type = str_replace('[]', '', $inheritance); |
150 | } else { |
151 | $type = $struct->getPackagedName($namespaced); |
152 | } |
153 | } |
154 | |
155 | $model = $this->getModelFromStructAttribute($attribute); |
156 | if ($model instanceof StructModel) { |
157 | // issue #84: union is considered as string as it would be difficult to have a method that accepts multiple object types. |
158 | // If the property has to be an object of multiple types => new issue... |
159 | if ($model->isRestriction() || $model->isUnion()) { |
160 | $type = self::TYPE_STRING; |
161 | } elseif ($model->isStruct()) { |
162 | $type = $model->getPackagedName($namespaced); |
163 | } elseif ($model->isArray() && ($inheritanceStruct = $model->getInheritanceStruct()) instanceof StructModel) { |
164 | $type = $inheritanceStruct->getPackagedName($namespaced); |
165 | } |
166 | } |
167 | |
168 | return $type; |
169 | } |
170 | |
171 | public function getStructAttributeTypeAsPhpType(StructAttributeModel $fromAttribute = null, bool $returnArrayType = true): string |
172 | { |
173 | $attribute = $this->getStructAttribute($fromAttribute); |
174 | |
175 | if (!$attribute instanceof StructAttributeModel) { |
176 | throw new \InvalidArgumentException('Could not find any valid StructAttribute'); |
177 | } |
178 | |
179 | $attributeType = $this->getStructAttributeType($attribute, true, $returnArrayType); |
180 | if (XsdTypes::instance($this->getGenerator()->getOptionXsdTypesPath())->isXsd($attributeType)) { |
181 | $attributeType = self::getPhpType($attributeType, $this->getGenerator()->getOptionXsdTypesPath()); |
182 | } |
183 | |
184 | return $attributeType; |
185 | } |
186 | |
187 | /** |
188 | * See http://php.net/manual/fr/language.oop5.typehinting.php for these cases |
189 | * Also see http://www.w3schools.com/schema/schema_dtypes_numeric.asp. |
190 | * |
191 | * @param mixed $type |
192 | * @param null $xsdTypesPath |
193 | * @param mixed $fallback |
194 | * |
195 | * @return mixed |
196 | */ |
197 | public static function getValidType($type, $xsdTypesPath = null, $fallback = null) |
198 | { |
199 | return XsdTypes::instance($xsdTypesPath)->isXsd(str_replace('[]', '', $type)) ? $fallback : $type; |
200 | } |
201 | |
202 | /** |
203 | * See http://php.net/manual/fr/language.oop5.typehinting.php for these cases |
204 | * Also see http://www.w3schools.com/schema/schema_dtypes_numeric.asp. |
205 | * |
206 | * @param mixed $type |
207 | * @param null $xsdTypesPath |
208 | * @param mixed $fallback |
209 | * |
210 | * @return mixed |
211 | */ |
212 | public static function getPhpType($type, $xsdTypesPath = null, $fallback = self::TYPE_STRING) |
213 | { |
214 | return XsdTypes::instance($xsdTypesPath)->isXsd(str_replace('[]', '', $type)) ? XsdTypes::instance($xsdTypesPath)->phpType($type) : $fallback; |
215 | } |
216 | |
217 | protected function addAnnotationBlock(): AbstractModelFile |
218 | { |
219 | $this->getFile()->addAnnotationBlockElement($this->getClassAnnotationBlock()); |
220 | |
221 | return $this; |
222 | } |
223 | |
224 | protected function getModelByName(string $name): ?StructModel |
225 | { |
226 | return $this->getGenerator()->getStructByName($name); |
227 | } |
228 | |
229 | protected function definePackageAnnotations(PhpAnnotationBlock $block): self |
230 | { |
231 | $packageName = $this->getPackageName(); |
232 | if (!empty($packageName)) { |
233 | $block->addChild(new PhpAnnotation(self::ANNOTATION_PACKAGE, $packageName)); |
234 | } |
235 | if (count($this->getModel()->getDocSubPackages()) > 0) { |
236 | $block->addChild(new PhpAnnotation(self::ANNOTATION_SUB_PACKAGE, implode(',', $this->getModel()->getDocSubPackages()))); |
237 | } |
238 | |
239 | return $this; |
240 | } |
241 | |
242 | protected function getPackageName(): string |
243 | { |
244 | $packageName = ''; |
245 | if (!empty($this->getGenerator()->getOptionPrefix())) { |
246 | $packageName = $this->getGenerator()->getOptionPrefix(); |
247 | } elseif (!empty($this->getGenerator()->getOptionSuffix())) { |
248 | $packageName = $this->getGenerator()->getOptionSuffix(); |
249 | } |
250 | |
251 | return $packageName; |
252 | } |
253 | |
254 | protected function defineGeneralAnnotations(PhpAnnotationBlock $block): self |
255 | { |
256 | foreach ($this->getGenerator()->getOptionAddComments() as $tagName => $tagValue) { |
257 | $block->addChild(new PhpAnnotation($tagName, $tagValue)); |
258 | } |
259 | |
260 | return $this; |
261 | } |
262 | |
263 | protected function getClassAnnotationBlock(): PhpAnnotationBlock |
264 | { |
265 | $block = new PhpAnnotationBlock(); |
266 | $block->addChild($this->getClassDeclarationLine()); |
267 | $this->defineModelAnnotationsFromWsdl($block)->definePackageAnnotations($block)->defineGeneralAnnotations($block); |
268 | |
269 | return $block; |
270 | } |
271 | |
272 | protected function getClassDeclarationLine(): string |
273 | { |
274 | return sprintf($this->getClassDeclarationLineText(), $this->getModel()->getName(), $this->getModel()->getContextualPart()); |
275 | } |
276 | |
277 | protected function getClassDeclarationLineText(): string |
278 | { |
279 | return 'This class stands for %s %s'; |
280 | } |
281 | |
282 | protected function defineModelAnnotationsFromWsdl(PhpAnnotationBlock $block, AbstractModel $model = null): self |
283 | { |
284 | FileUtils::defineModelAnnotationsFromWsdl($block, $model instanceof AbstractModel ? $model : $this->getModel()); |
285 | |
286 | return $this; |
287 | } |
288 | |
289 | protected function addClassElement(): AbstractModelFile |
290 | { |
291 | $class = new PhpClass($this->getModel()->getPackagedName(), $this->getModel()->isAbstract(), '' === $this->getModel()->getExtendsClassName() ? null : $this->getModel()->getExtendsClassName()); |
292 | $this |
293 | ->defineConstants($class) |
294 | ->defineProperties($class) |
295 | ->defineMethods($class) |
296 | ->getFile() |
297 | ->addClassComponent($class) |
298 | ; |
299 | |
300 | return $this; |
301 | } |
302 | |
303 | protected function addDeclareDirective(): self |
304 | { |
305 | $this->getFile()->setDeclare(PhpDeclare::DIRECTIVE_STRICT_TYPES, 1); |
306 | |
307 | return $this; |
308 | } |
309 | |
310 | protected function defineNamespace(): self |
311 | { |
312 | if (!empty($this->getModel()->getNamespace())) { |
313 | $this->getFile()->setNamespace($this->getModel()->getNamespace()); |
314 | } |
315 | |
316 | return $this; |
317 | } |
318 | |
319 | protected function defineUseStatements(): self |
320 | { |
321 | if (!empty($this->getModel()->getExtends())) { |
322 | $this->getFile()->addUse($this->getModel()->getExtends(), null, true); |
323 | } |
324 | |
325 | return $this; |
326 | } |
327 | |
328 | protected function defineConstants(PhpClass $class): self |
329 | { |
330 | $constants = new Constant($this->getGenerator()); |
331 | $this->fillClassConstants($constants); |
332 | foreach ($constants as $constant) { |
333 | $annotationBlock = $this->getConstantAnnotationBlock($constant); |
334 | if (!empty($annotationBlock)) { |
335 | $class->addAnnotationBlockElement($annotationBlock); |
336 | } |
337 | $class->addConstantElement($constant); |
338 | } |
339 | |
340 | return $this; |
341 | } |
342 | |
343 | protected function defineProperties(PhpClass $class): self |
344 | { |
345 | $properties = new Property($this->getGenerator()); |
346 | $this->fillClassProperties($properties); |
347 | foreach ($properties as $property) { |
348 | $annotationBlock = $this->getPropertyAnnotationBlock($property); |
349 | if (!empty($annotationBlock)) { |
350 | $class->addAnnotationBlockElement($annotationBlock); |
351 | } |
352 | $class->addPropertyElement($property); |
353 | } |
354 | |
355 | return $this; |
356 | } |
357 | |
358 | protected function defineMethods(PhpClass $class): self |
359 | { |
360 | $this->methods = new Method($this->getGenerator()); |
361 | $this->fillClassMethods(); |
362 | foreach ($this->methods as $method) { |
363 | $annotationBlock = $this->getMethodAnnotationBlock($method); |
364 | if (!empty($annotationBlock)) { |
365 | $class->addAnnotationBlockElement($annotationBlock); |
366 | } |
367 | $class->addMethodElement($method); |
368 | } |
369 | |
370 | return $this; |
371 | } |
372 | |
373 | abstract protected function fillClassConstants(Constant $constants): void; |
374 | |
375 | abstract protected function getConstantAnnotationBlock(PhpConstant $constant): ?PhpAnnotationBlock; |
376 | |
377 | abstract protected function fillClassProperties(Property $properties): void; |
378 | |
379 | abstract protected function getPropertyAnnotationBlock(PhpProperty $property): ?PhpAnnotationBlock; |
380 | |
381 | abstract protected function fillClassMethods(): void; |
382 | |
383 | abstract protected function getMethodAnnotationBlock(PhpMethod $method): ?PhpAnnotationBlock; |
384 | |
385 | protected function getStructAttribute(StructAttributeModel $attribute = null): ?StructAttributeModel |
386 | { |
387 | $struct = $this->getModel(); |
388 | if (empty($attribute) && $struct instanceof StructModel && 1 === $struct->getAttributes()->count()) { |
389 | $attribute = $struct->getAttributes()->offsetGet(0); |
390 | } |
391 | |
392 | return $attribute; |
393 | } |
394 | |
395 | protected function getStructAttributeTypeGetAnnotation(StructAttributeModel $attribute = null, bool $returnArrayType = true, bool $nullableItemType = false): string |
396 | { |
397 | $attribute = $this->getStructAttribute($attribute); |
398 | |
399 | if ($attribute->isXml()) { |
400 | return '\\DOMDocument|string|null'; |
401 | } |
402 | |
403 | return sprintf( |
404 | '%s%s%s', |
405 | $this->getStructAttributeTypeAsPhpType($attribute, false), |
406 | $this->useBrackets($attribute, $returnArrayType) ? '[]' : '', |
407 | !$nullableItemType && !$attribute->isNullable() && ($attribute->isRequired() || $attribute->isArray() || $attribute->isList()) ? '' : '|null' |
408 | ); |
409 | } |
410 | |
411 | protected function getStructAttributeTypeSetAnnotation(StructAttributeModel $attribute, bool $returnArrayType = true, bool $itemType = false): string |
412 | { |
413 | if ($attribute->isXml()) { |
414 | return '\\DOMDocument|string|null'; |
415 | } |
416 | |
417 | if ($attribute->isList()) { |
418 | return 'array|string'; |
419 | } |
420 | |
421 | return sprintf( |
422 | '%s%s', |
423 | $this->getStructAttributeTypeAsPhpType($attribute, $returnArrayType), |
424 | $this->useBrackets($attribute, !$itemType) ? '[]' : '' |
425 | ); |
426 | } |
427 | |
428 | protected function useBrackets(StructAttributeModel $attribute, bool $returnArrayType = true): bool |
429 | { |
430 | return $returnArrayType && $attribute->isArray(); |
431 | } |
432 | } |