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 | 188 | public function getFileDestination(bool $withSrc = true): string |
|||
47 | { |
||||
48 | 188 | return sprintf( |
|||
49 | 188 | '%s%s%s', |
|||
50 | 188 | $this->getDestinationFolder($withSrc), |
|||
51 | 188 | $this->getModel()->getSubDirectory(), |
|||
52 | 188 | !empty($this->getModel()->getSubDirectory()) ? '/' : '' |
|||
53 | 188 | ); |
|||
54 | } |
||||
55 | |||||
56 | 192 | public function getDestinationFolder(bool $withSrc = true): string |
|||
57 | { |
||||
58 | 192 | $src = rtrim($this->generator->getOptionSrcDirname(), DIRECTORY_SEPARATOR); |
|||
59 | |||||
60 | 192 | return sprintf( |
|||
61 | 192 | '%s%s%s%s', |
|||
62 | 192 | $this->getGenerator()->getOptionDestination(), |
|||
63 | 192 | $withSrc && !empty($src) ? $src.DIRECTORY_SEPARATOR : '', |
|||
64 | 192 | $this->getGenerator()->getOptionNamespaceDictatesDirectories() ? str_replace('\\', DIRECTORY_SEPARATOR, $this->getGenerator()->getOptionNamespacePrefix()) : '', |
|||
65 | 192 | $this->getGenerator()->getOptionNamespacePrefix() && $this->getGenerator()->getOptionNamespaceDictatesDirectories() ? DIRECTORY_SEPARATOR : '' |
|||
66 | 192 | ); |
|||
67 | } |
||||
68 | |||||
69 | 178 | public function writeFile(bool $withSrc = true): void |
|||
70 | { |
||||
71 | 178 | if (!$this->getModel()) { |
|||
72 | 2 | throw new \InvalidArgumentException('You MUST define the model before being able to generate the file', __LINE__); |
|||
73 | } |
||||
74 | |||||
75 | 176 | GeneratorUtils::createDirectory($this->getFileDestination($withSrc)); |
|||
76 | |||||
77 | 176 | $this |
|||
78 | 176 | ->addDeclareDirective() |
|||
79 | 176 | ->defineNamespace() |
|||
80 | 176 | ->defineUseStatements() |
|||
81 | 176 | ->addAnnotationBlock() |
|||
82 | 176 | ->addClassElement() |
|||
83 | 176 | ; |
|||
84 | |||||
85 | 176 | parent::writeFile(); |
|||
86 | } |
||||
87 | |||||
88 | 206 | public function setModel(AbstractModel $model): self |
|||
89 | { |
||||
90 | 206 | $this->model = $model; |
|||
91 | |||||
92 | 206 | $this |
|||
93 | 206 | ->getFile() |
|||
94 | 206 | ->getMainElement() |
|||
95 | 206 | ->setName($model->getPackagedName()) |
|||
96 | 206 | ; |
|||
97 | |||||
98 | 206 | return $this; |
|||
99 | } |
||||
100 | |||||
101 | 204 | public function getModel(): ?AbstractModel |
|||
102 | { |
||||
103 | 204 | return $this->model; |
|||
104 | } |
||||
105 | |||||
106 | 122 | public function getModelFromStructAttribute(StructAttributeModel $attribute = null): ?StructModel |
|||
107 | { |
||||
108 | 122 | return $this->getStructAttribute($attribute)->getTypeStruct(); |
|||
109 | } |
||||
110 | |||||
111 | 120 | public function getRestrictionFromStructAttribute(StructAttributeModel $attribute = null): ?StructModel |
|||
112 | { |
||||
113 | 120 | $model = $this->getModelFromStructAttribute($attribute); |
|||
114 | 120 | if ($model instanceof StructModel) { |
|||
115 | // list are mainly scalar values of basic types (string, int, etc.) or of Restriction values |
||||
116 | 110 | if ($model->isList()) { |
|||
117 | 6 | $subModel = $this->getModelByName($model->getList()); |
|||
118 | 6 | if ($subModel && $subModel->isRestriction()) { |
|||
119 | 4 | $model = $subModel; |
|||
120 | 2 | } elseif (!$model->isRestriction()) { |
|||
121 | $model = null; |
||||
122 | } |
||||
123 | 106 | } elseif (!$model->isRestriction()) { |
|||
124 | 102 | $model = null; |
|||
125 | } |
||||
126 | } |
||||
127 | |||||
128 | 120 | return $model; |
|||
129 | } |
||||
130 | |||||
131 | 122 | public function getStructAttributeType(StructAttributeModel $attribute = null, bool $namespaced = false, bool $returnArrayType = true): string |
|||
132 | { |
||||
133 | 122 | $attribute = $this->getStructAttribute($attribute); |
|||
134 | |||||
135 | 122 | if (!$attribute instanceof StructAttributeModel) { |
|||
0 ignored issues
–
show
introduced
by
![]() |
|||||
136 | throw new \InvalidArgumentException('Could not find any valid StructAttribute'); |
||||
137 | } |
||||
138 | |||||
139 | 122 | if ($returnArrayType && $attribute->isArray()) { |
|||
140 | 60 | return self::TYPE_ARRAY; |
|||
141 | } |
||||
142 | |||||
143 | 122 | $inheritance = $attribute->getInheritance(); |
|||
144 | 122 | $type = empty($inheritance) ? $attribute->getType() : $inheritance; |
|||
145 | |||||
146 | 122 | if (!empty($type) && ($struct = $this->getGenerator()->getStructByName($type))) { |
|||
147 | 106 | $inheritance = $struct->getTopInheritance(); |
|||
148 | 106 | if (!empty($inheritance)) { |
|||
149 | 78 | $type = str_replace('[]', '', $inheritance); |
|||
150 | } else { |
||||
151 | 74 | $type = $struct->getPackagedName($namespaced); |
|||
152 | } |
||||
153 | } |
||||
154 | |||||
155 | 122 | $model = $this->getModelFromStructAttribute($attribute); |
|||
156 | 122 | 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 | 112 | if ($model->isRestriction() || $model->isUnion()) { |
|||
160 | 52 | $type = self::TYPE_STRING; |
|||
161 | 108 | } elseif ($model->isStruct()) { |
|||
162 | 86 | $type = $model->getPackagedName($namespaced); |
|||
163 | 64 | } elseif ($model->isArray() && ($inheritanceStruct = $model->getInheritanceStruct()) instanceof StructModel) { |
|||
164 | 8 | $type = $inheritanceStruct->getPackagedName($namespaced); |
|||
165 | } |
||||
166 | } |
||||
167 | |||||
168 | 122 | return $type; |
|||
169 | } |
||||
170 | |||||
171 | 122 | public function getStructAttributeTypeAsPhpType(StructAttributeModel $fromAttribute = null, bool $returnArrayType = true): string |
|||
172 | { |
||||
173 | 122 | $attribute = $this->getStructAttribute($fromAttribute); |
|||
174 | |||||
175 | 122 | if (!$attribute instanceof StructAttributeModel) { |
|||
0 ignored issues
–
show
|
|||||
176 | throw new \InvalidArgumentException('Could not find any valid StructAttribute'); |
||||
177 | } |
||||
178 | |||||
179 | 122 | $attributeType = $this->getStructAttributeType($attribute, true, $returnArrayType); |
|||
180 | 122 | if (XsdTypes::instance($this->getGenerator()->getOptionXsdTypesPath())->isXsd($attributeType)) { |
|||
181 | 100 | $attributeType = self::getPhpType($attributeType, $this->getGenerator()->getOptionXsdTypesPath()); |
|||
182 | } |
||||
183 | |||||
184 | 122 | 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 |
||||
0 ignored issues
–
show
|
|||||
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 |
||||
0 ignored issues
–
show
|
|||||
208 | * @param mixed $fallback |
||||
209 | * |
||||
210 | * @return mixed |
||||
211 | */ |
||||
212 | 110 | public static function getPhpType($type, $xsdTypesPath = null, $fallback = self::TYPE_STRING) |
|||
213 | { |
||||
214 | 110 | return XsdTypes::instance($xsdTypesPath)->isXsd(str_replace('[]', '', $type)) ? XsdTypes::instance($xsdTypesPath)->phpType($type) : $fallback; |
|||
215 | } |
||||
216 | |||||
217 | 176 | protected function addAnnotationBlock(): AbstractModelFile |
|||
218 | { |
||||
219 | 176 | $this->getFile()->addAnnotationBlockElement($this->getClassAnnotationBlock()); |
|||
220 | |||||
221 | 176 | return $this; |
|||
222 | } |
||||
223 | |||||
224 | 18 | protected function getModelByName(string $name): ?StructModel |
|||
225 | { |
||||
226 | 18 | return $this->getGenerator()->getStructByName($name); |
|||
227 | } |
||||
228 | |||||
229 | 166 | protected function definePackageAnnotations(PhpAnnotationBlock $block): self |
|||
230 | { |
||||
231 | 166 | $packageName = $this->getPackageName(); |
|||
232 | 166 | if (!empty($packageName)) { |
|||
233 | 148 | $block->addChild(new PhpAnnotation(self::ANNOTATION_PACKAGE, $packageName)); |
|||
234 | } |
||||
235 | 166 | if (count($this->getModel()->getDocSubPackages()) > 0) { |
|||
236 | 166 | $block->addChild(new PhpAnnotation(self::ANNOTATION_SUB_PACKAGE, implode(',', $this->getModel()->getDocSubPackages()))); |
|||
237 | } |
||||
238 | |||||
239 | 166 | return $this; |
|||
240 | } |
||||
241 | |||||
242 | 166 | protected function getPackageName(): string |
|||
243 | { |
||||
244 | 166 | $packageName = ''; |
|||
245 | 166 | if (!empty($this->getGenerator()->getOptionPrefix())) { |
|||
246 | 144 | $packageName = $this->getGenerator()->getOptionPrefix(); |
|||
247 | 22 | } elseif (!empty($this->getGenerator()->getOptionSuffix())) { |
|||
248 | 4 | $packageName = $this->getGenerator()->getOptionSuffix(); |
|||
249 | } |
||||
250 | |||||
251 | 166 | return $packageName; |
|||
252 | } |
||||
253 | |||||
254 | 166 | protected function defineGeneralAnnotations(PhpAnnotationBlock $block): self |
|||
255 | { |
||||
256 | 166 | foreach ($this->getGenerator()->getOptionAddComments() as $tagName => $tagValue) { |
|||
257 | 154 | $block->addChild(new PhpAnnotation($tagName, $tagValue)); |
|||
258 | } |
||||
259 | |||||
260 | 166 | return $this; |
|||
261 | } |
||||
262 | |||||
263 | 166 | protected function getClassAnnotationBlock(): PhpAnnotationBlock |
|||
264 | { |
||||
265 | 166 | $block = new PhpAnnotationBlock(); |
|||
266 | 166 | $block->addChild($this->getClassDeclarationLine()); |
|||
267 | 166 | $this->defineModelAnnotationsFromWsdl($block)->definePackageAnnotations($block)->defineGeneralAnnotations($block); |
|||
268 | |||||
269 | 166 | return $block; |
|||
270 | } |
||||
271 | |||||
272 | 166 | protected function getClassDeclarationLine(): string |
|||
273 | { |
||||
274 | 166 | return sprintf($this->getClassDeclarationLineText(), $this->getModel()->getName(), $this->getModel()->getContextualPart()); |
|||
275 | } |
||||
276 | |||||
277 | 158 | protected function getClassDeclarationLineText(): string |
|||
278 | { |
||||
279 | 158 | return 'This class stands for %s %s'; |
|||
280 | } |
||||
281 | |||||
282 | 166 | protected function defineModelAnnotationsFromWsdl(PhpAnnotationBlock $block, AbstractModel $model = null): self |
|||
283 | { |
||||
284 | 166 | FileUtils::defineModelAnnotationsFromWsdl($block, $model instanceof AbstractModel ? $model : $this->getModel()); |
|||
0 ignored issues
–
show
It seems like
$model instanceof WsdlTo...del : $this->getModel() can also be of type null ; however, parameter $model of WsdlToPhp\PackageGenerat...elAnnotationsFromWsdl() does only seem to accept WsdlToPhp\PackageGenerator\Model\AbstractModel , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
285 | |||||
286 | 166 | return $this; |
|||
287 | } |
||||
288 | |||||
289 | 176 | protected function addClassElement(): AbstractModelFile |
|||
290 | { |
||||
291 | 176 | $class = new PhpClass($this->getModel()->getPackagedName(), $this->getModel()->isAbstract(), '' === $this->getModel()->getExtendsClassName() ? null : $this->getModel()->getExtendsClassName()); |
|||
292 | 176 | $this |
|||
293 | 176 | ->defineConstants($class) |
|||
294 | 176 | ->defineProperties($class) |
|||
295 | 176 | ->defineMethods($class) |
|||
296 | 176 | ->getFile() |
|||
297 | 176 | ->addClassComponent($class) |
|||
298 | 176 | ; |
|||
299 | |||||
300 | 176 | return $this; |
|||
301 | } |
||||
302 | |||||
303 | 176 | protected function addDeclareDirective(): self |
|||
304 | { |
||||
305 | 176 | $this->getFile()->setDeclare(PhpDeclare::DIRECTIVE_STRICT_TYPES, 1); |
|||
306 | |||||
307 | 176 | return $this; |
|||
308 | } |
||||
309 | |||||
310 | 176 | protected function defineNamespace(): self |
|||
311 | { |
||||
312 | 176 | if (!empty($this->getModel()->getNamespace())) { |
|||
313 | 166 | $this->getFile()->setNamespace($this->getModel()->getNamespace()); |
|||
314 | } |
||||
315 | |||||
316 | 176 | return $this; |
|||
317 | } |
||||
318 | |||||
319 | 176 | protected function defineUseStatements(): self |
|||
320 | { |
||||
321 | 176 | if (!empty($this->getModel()->getExtends())) { |
|||
322 | 166 | $this->getFile()->addUse($this->getModel()->getExtends(), null, true); |
|||
323 | } |
||||
324 | |||||
325 | 176 | return $this; |
|||
326 | } |
||||
327 | |||||
328 | 176 | protected function defineConstants(PhpClass $class): self |
|||
329 | { |
||||
330 | 176 | $constants = new Constant($this->getGenerator()); |
|||
331 | 176 | $this->fillClassConstants($constants); |
|||
332 | 176 | foreach ($constants as $constant) { |
|||
333 | 44 | $annotationBlock = $this->getConstantAnnotationBlock($constant); |
|||
334 | 44 | if (!empty($annotationBlock)) { |
|||
335 | 44 | $class->addAnnotationBlockElement($annotationBlock); |
|||
336 | } |
||||
337 | 44 | $class->addConstantElement($constant); |
|||
338 | } |
||||
339 | |||||
340 | 176 | return $this; |
|||
341 | } |
||||
342 | |||||
343 | 176 | protected function defineProperties(PhpClass $class): self |
|||
344 | { |
||||
345 | 176 | $properties = new Property($this->getGenerator()); |
|||
346 | 176 | $this->fillClassProperties($properties); |
|||
347 | 176 | foreach ($properties as $property) { |
|||
348 | 110 | $annotationBlock = $this->getPropertyAnnotationBlock($property); |
|||
349 | 110 | if (!empty($annotationBlock)) { |
|||
350 | 110 | $class->addAnnotationBlockElement($annotationBlock); |
|||
351 | } |
||||
352 | 110 | $class->addPropertyElement($property); |
|||
353 | } |
||||
354 | |||||
355 | 176 | return $this; |
|||
356 | } |
||||
357 | |||||
358 | 176 | protected function defineMethods(PhpClass $class): self |
|||
359 | { |
||||
360 | 176 | $this->methods = new Method($this->getGenerator()); |
|||
361 | 176 | $this->fillClassMethods(); |
|||
362 | 176 | foreach ($this->methods as $method) { |
|||
363 | 172 | $annotationBlock = $this->getMethodAnnotationBlock($method); |
|||
364 | 172 | if (!empty($annotationBlock)) { |
|||
365 | 172 | $class->addAnnotationBlockElement($annotationBlock); |
|||
366 | } |
||||
367 | 172 | $class->addMethodElement($method); |
|||
368 | } |
||||
369 | |||||
370 | 176 | 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 | 122 | protected function getStructAttribute(StructAttributeModel $attribute = null): ?StructAttributeModel |
|||
386 | { |
||||
387 | 122 | $struct = $this->getModel(); |
|||
388 | 122 | if (empty($attribute) && $struct instanceof StructModel && 1 === $struct->getAttributes()->count()) { |
|||
389 | 20 | $attribute = $struct->getAttributes()->offsetGet(0); |
|||
390 | } |
||||
391 | |||||
392 | 122 | return $attribute; |
|||
393 | } |
||||
394 | |||||
395 | 110 | protected function getStructAttributeTypeGetAnnotation(StructAttributeModel $attribute = null, bool $returnArrayType = true, bool $nullableItemType = false): string |
|||
396 | { |
||||
397 | 110 | $attribute = $this->getStructAttribute($attribute); |
|||
398 | |||||
399 | 110 | if ($attribute->isXml()) { |
|||
400 | 4 | return '\\DOMDocument|string|null'; |
|||
401 | } |
||||
402 | |||||
403 | 110 | return sprintf( |
|||
404 | '%s%s%s', |
||||
405 | $this->getStructAttributeTypeAsPhpType($attribute, false), |
||||
406 | 110 | $this->useBrackets($attribute, $returnArrayType) ? '[]' : '', |
|||
407 | !$nullableItemType && !$attribute->isNullable() && ($attribute->isRequired() || $attribute->isArray() || $attribute->isList()) ? '' : '|null' |
||||
408 | 110 | ); |
|||
409 | 4 | } |
|||
410 | |||||
411 | protected function getStructAttributeTypeSetAnnotation(StructAttributeModel $attribute, bool $returnArrayType = true, bool $itemType = false): string |
||||
412 | 110 | { |
|||
413 | 6 | if ($attribute->isXml()) { |
|||
414 | return '\\DOMDocument|string|null'; |
||||
415 | } |
||||
416 | 110 | ||||
417 | if ($attribute->isList()) { |
||||
418 | return 'array|string'; |
||||
419 | 110 | } |
|||
420 | |||||
421 | 110 | 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 | } |
||||
433 |