Passed
Push — feature/issue-124 ( a8c837...f8e59e )
by Mikaël
09:39
created

AbstractModelFile::getModelFromStructAttribute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace WsdlToPhp\PackageGenerator\File;
6
7
use InvalidArgumentException;
8
use WsdlToPhp\PackageGenerator\ConfigurationReader\XsdTypes;
9
use WsdlToPhp\PackageGenerator\Container\PhpElement\Constant;
10
use WsdlToPhp\PackageGenerator\Container\PhpElement\Method;
11
use WsdlToPhp\PackageGenerator\Container\PhpElement\Property;
12
use WsdlToPhp\PackageGenerator\File\Utils as FileUtils;
13
use WsdlToPhp\PackageGenerator\Generator\Utils as GeneratorUtils;
14
use WsdlToPhp\PackageGenerator\Model\AbstractModel;
15
use WsdlToPhp\PackageGenerator\Model\Struct as StructModel;
16
use WsdlToPhp\PackageGenerator\Model\StructAttribute as StructAttributeModel;
17
use WsdlToPhp\PhpGenerator\Component\PhpClass;
18
use WsdlToPhp\PhpGenerator\Element\PhpAnnotation;
19
use WsdlToPhp\PhpGenerator\Element\PhpAnnotationBlock;
20
use WsdlToPhp\PhpGenerator\Element\PhpConstant;
21
use WsdlToPhp\PhpGenerator\Element\PhpDeclare;
22
use WsdlToPhp\PhpGenerator\Element\PhpMethod;
23
use WsdlToPhp\PhpGenerator\Element\PhpProperty;
24
25
abstract class AbstractModelFile extends AbstractFile
26
{
27
    public const ANNOTATION_META_LENGTH = 250;
28
    public const ANNOTATION_LONG_LENGTH = 1000;
29
    public const ANNOTATION_PACKAGE = 'package';
30
    public const ANNOTATION_SUB_PACKAGE = 'subpackage';
31
    public const ANNOTATION_RETURN = 'return';
32
    public const ANNOTATION_USES = 'uses';
33
    public const ANNOTATION_PARAM = 'param';
34
    public const ANNOTATION_VAR = 'var';
35
    public const ANNOTATION_SEE = 'see';
36
    public const ANNOTATION_THROWS = 'throws';
37
    public const METHOD_CONSTRUCT = '__construct';
38
    public const TYPE_ARRAY = 'array';
39
    public const TYPE_BOOL = 'bool';
40
    public const TYPE_STRING = 'string';
41
    public const TYPE_SELF = 'self';
42
43
    protected Method $methods;
44
45
    private ?AbstractModel $model = null;
46
47 177
    public function getFileDestination(bool $withSrc = true): string
48
    {
49 177
        return sprintf('%s%s%s', $this->getDestinationFolder($withSrc), $this->getModel()->getSubDirectory(), !empty($this->getModel()->getSubDirectory()) ? '/' : '');
50
    }
51
52 177
    public function getDestinationFolder(bool $withSrc = true): string
53
    {
54 177
        $src = rtrim($this->generator->getOptionSrcDirname(), DIRECTORY_SEPARATOR);
55
56 177
        return sprintf('%s%s', $this->getGenerator()->getOptionDestination(), (bool) $withSrc && !empty($src) ? $src.DIRECTORY_SEPARATOR : '');
57
    }
58
59 167
    public function writeFile(bool $withSrc = true): void
60
    {
61 167
        if (!$this->getModel()) {
62 2
            throw new InvalidArgumentException('You MUST define the model before being able to generate the file', __LINE__);
63
        }
64 165
        GeneratorUtils::createDirectory($this->getFileDestination($withSrc));
65
        $this
66 165
            ->addDeclareDirective()
67 165
            ->defineNamespace()
68 165
            ->defineUseStatements()
69 165
            ->addAnnotationBlock()
70 165
            ->addClassElement()
71
        ;
72 165
        parent::writeFile();
73 165
    }
74
75 191
    public function setModel(AbstractModel $model): self
76
    {
77 191
        $this->model = $model;
78
79
        $this
80 191
            ->getFile()
81 191
            ->getMainElement()
82 191
            ->setName($model->getPackagedName())
83
        ;
84
85 191
        return $this;
86
    }
87
88 193
    public function getModel(): ?AbstractModel
89
    {
90 193
        return $this->model;
91
    }
92
93 113
    public function getModelFromStructAttribute(StructAttributeModel $attribute = null): ?StructModel
94
    {
95 113
        return $this->getStructAttribute($attribute)->getTypeStruct();
96
    }
97
98 110
    public function getRestrictionFromStructAttribute(StructAttributeModel $attribute = null): ?StructModel
99
    {
100 110
        $model = $this->getModelFromStructAttribute($attribute);
101 110
        if ($model instanceof StructModel) {
102
            // list are mainly scalar values of basic types (string, int, etc.) or of Restriction values
103 100
            if ($model->isList()) {
104 6
                $subModel = $this->getModelByName($model->getList());
105 6
                if ($subModel && $subModel->isRestriction()) {
106 4
                    $model = $subModel;
107 2
                } elseif (!$model->isRestriction()) {
108 6
                    $model = null;
109
                }
110 96
            } elseif (!$model->isRestriction()) {
111 92
                $model = null;
112
            }
113
        }
114
115 110
        return $model;
116
    }
117
118 101
    public function isAttributeAList(StructAttributeModel $attribute = null): bool
119
    {
120 101
        return $this->getStructAttribute($attribute)->isList();
121
    }
122
123 113
    public function getStructAttributeType(StructAttributeModel $attribute = null, bool $namespaced = false): string
124
    {
125 113
        $attribute = $this->getStructAttribute($attribute);
126 113
        $inheritance = $attribute->getInheritance();
127 113
        $type = empty($inheritance) ? $attribute->getType() : $inheritance;
128
129 113
        if (!empty($type) && ($struct = $this->getGenerator()->getStructByName($type))) {
130 97
            $inheritance = $struct->getTopInheritance();
131 97
            if (!empty($inheritance)) {
132 69
                $type = str_replace('[]', '', $inheritance);
133
            } else {
134 75
                $type = $struct->getPackagedName($namespaced);
135
            }
136
        }
137
138 113
        $model = $this->getModelFromStructAttribute($attribute);
139 113
        if ($model instanceof StructModel) {
140
            // issue #84: union is considered as string as it would be difficult to have a method that accepts multiple object types.
141
            // If the property has to be an object of multiple types => new issue...
142 103
            if ($model->isRestriction() || $model->isUnion()) {
143 51
                $type = self::TYPE_STRING;
144 99
            } elseif ($model->isStruct()) {
145 87
                $type = $model->getPackagedName($namespaced);
146 50
            } elseif ($model->isArray() && ($inheritanceStruct = $model->getInheritanceStruct()) instanceof StructModel) {
147 8
                $type = $inheritanceStruct->getPackagedName($namespaced);
148
            }
149
        }
150
151 113
        return $type;
152
    }
153
154 113
    public function getStructAttributeTypeAsPhpType(StructAttributeModel $attribute = null): string
155
    {
156 113
        $attribute = $this->getStructAttribute($attribute);
157 113
        $attributeType = $this->getStructAttributeType($attribute, true);
158 113
        if (XsdTypes::instance($this->getGenerator()->getOptionXsdTypesPath())->isXsd($attributeType)) {
0 ignored issues
show
Bug introduced by
The method isXsd() does not exist on WsdlToPhp\PackageGenerat...ader\AbstractYamlReader. It seems like you code against a sub-type of WsdlToPhp\PackageGenerat...ader\AbstractYamlReader such as WsdlToPhp\PackageGenerat...gurationReader\XsdTypes. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

158
        if (XsdTypes::instance($this->getGenerator()->getOptionXsdTypesPath())->/** @scrutinizer ignore-call */ isXsd($attributeType)) {
Loading history...
159 91
            $attributeType = self::getPhpType($attributeType, $this->getGenerator()->getOptionXsdTypesPath());
160
        }
161
162 113
        return $attributeType;
163
    }
164
165
    /**
166
     * See http://php.net/manual/fr/language.oop5.typehinting.php for these cases
167
     * Also see http://www.w3schools.com/schema/schema_dtypes_numeric.asp.
168
     *
169
     * @param mixed $type
170
     * @param null  $xsdTypesPath
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $xsdTypesPath is correct as it would always require null to be passed?
Loading history...
171
     * @param mixed $fallback
172
     *
173
     * @return mixed
174
     */
175
    public static function getValidType($type, $xsdTypesPath = null, $fallback = null)
176
    {
177
        return XsdTypes::instance($xsdTypesPath)->isXsd(str_replace('[]', '', $type)) ? $fallback : $type;
178
    }
179
180
    /**
181
     * See http://php.net/manual/fr/language.oop5.typehinting.php for these cases
182
     * Also see http://www.w3schools.com/schema/schema_dtypes_numeric.asp.
183
     *
184
     * @param mixed $type
185
     * @param null  $xsdTypesPath
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $xsdTypesPath is correct as it would always require null to be passed?
Loading history...
186
     * @param mixed $fallback
187
     *
188
     * @return mixed
189
     */
190 113
    public static function getPhpType($type, $xsdTypesPath = null, $fallback = self::TYPE_STRING)
191
    {
192 113
        return XsdTypes::instance($xsdTypesPath)->isXsd(str_replace('[]', '', $type)) ? XsdTypes::instance($xsdTypesPath)->phpType($type) : $fallback;
0 ignored issues
show
Bug introduced by
The method phpType() does not exist on WsdlToPhp\PackageGenerat...ader\AbstractYamlReader. It seems like you code against a sub-type of WsdlToPhp\PackageGenerat...ader\AbstractYamlReader such as WsdlToPhp\PackageGenerat...gurationReader\XsdTypes. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

192
        return XsdTypes::instance($xsdTypesPath)->isXsd(str_replace('[]', '', $type)) ? XsdTypes::instance($xsdTypesPath)->/** @scrutinizer ignore-call */ phpType($type) : $fallback;
Loading history...
193
    }
194
195 165
    protected function addAnnotationBlock(): AbstractModelFile
196
    {
197 165
        $this->getFile()->addAnnotationBlockElement($this->getClassAnnotationBlock());
198
199 165
        return $this;
200
    }
201
202 18
    protected function getModelByName(string $name): ?StructModel
203
    {
204 18
        return $this->getGenerator()->getStructByName($name);
205
    }
206
207 155
    protected function definePackageAnnotations(PhpAnnotationBlock $block): self
208
    {
209 155
        $packageName = $this->getPackageName();
210 155
        if (!empty($packageName)) {
211 137
            $block->addChild(new PhpAnnotation(self::ANNOTATION_PACKAGE, $packageName));
212
        }
213 155
        if (count($this->getModel()->getDocSubPackages()) > 0) {
214 155
            $block->addChild(new PhpAnnotation(self::ANNOTATION_SUB_PACKAGE, implode(',', $this->getModel()->getDocSubPackages())));
215
        }
216
217 155
        return $this;
218
    }
219
220 155
    protected function getPackageName(): string
221
    {
222 155
        $packageName = '';
223 155
        if (!empty($this->getGenerator()->getOptionPrefix())) {
224 133
            $packageName = $this->getGenerator()->getOptionPrefix();
225 22
        } elseif (!empty($this->getGenerator()->getOptionSuffix())) {
226 4
            $packageName = $this->getGenerator()->getOptionSuffix();
227
        }
228
229 155
        return $packageName;
230
    }
231
232 155
    protected function defineGeneralAnnotations(PhpAnnotationBlock $block): self
233
    {
234 155
        foreach ($this->getGenerator()->getOptionAddComments() as $tagName => $tagValue) {
235 143
            $block->addChild(new PhpAnnotation($tagName, $tagValue));
236
        }
237
238 155
        return $this;
239
    }
240
241 155
    protected function getClassAnnotationBlock(): PhpAnnotationBlock
242
    {
243 155
        $block = new PhpAnnotationBlock();
244 155
        $block->addChild($this->getClassDeclarationLine());
245 155
        $this->defineModelAnnotationsFromWsdl($block)->definePackageAnnotations($block)->defineGeneralAnnotations($block);
246
247 155
        return $block;
248
    }
249
250 155
    protected function getClassDeclarationLine(): string
251
    {
252 155
        return sprintf($this->getClassDeclarationLineText(), $this->getModel()->getName(), $this->getModel()->getContextualPart());
253
    }
254
255 147
    protected function getClassDeclarationLineText(): string
256
    {
257 147
        return 'This class stands for %s %s';
258
    }
259
260 155
    protected function defineModelAnnotationsFromWsdl(PhpAnnotationBlock $block, AbstractModel $model = null): self
261
    {
262 155
        FileUtils::defineModelAnnotationsFromWsdl($block, $model instanceof AbstractModel ? $model : $this->getModel());
0 ignored issues
show
Bug introduced by
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 ignore-type  annotation

262
        FileUtils::defineModelAnnotationsFromWsdl($block, /** @scrutinizer ignore-type */ $model instanceof AbstractModel ? $model : $this->getModel());
Loading history...
263
264 155
        return $this;
265
    }
266
267 165
    protected function addClassElement(): AbstractModelFile
268
    {
269 165
        $class = new PhpClass($this->getModel()->getPackagedName(), $this->getModel()->isAbstract(), '' === $this->getModel()->getExtendsClassName() ? null : $this->getModel()->getExtendsClassName());
270
        $this
271 165
            ->defineConstants($class)
272 165
            ->defineProperties($class)
273 165
            ->defineMethods($class)
274 165
            ->getFile()
275 165
            ->addClassComponent($class)
276
        ;
277
278 165
        return $this;
279
    }
280
281 165
    protected function addDeclareDirective(): self
282
    {
283 165
        $this->getFile()->setDeclare(PhpDeclare::DIRECTIVE_STRICT_TYPES, 1);
284
285 165
        return $this;
286
    }
287
288 165
    protected function defineNamespace(): self
289
    {
290 165
        if (!empty($this->getModel()->getNamespace())) {
291 161
            $this->getFile()->setNamespace($this->getModel()->getNamespace());
292
        }
293
294 165
        return $this;
295
    }
296
297 165
    protected function defineUseStatements(): self
298
    {
299 165
        if (!empty($this->getModel()->getExtends())) {
300 155
            $this->getFile()->addUse($this->getModel()->getExtends(), null, true);
301
        }
302
303 165
        return $this;
304
    }
305
306 165
    protected function defineConstants(PhpClass $class): self
307
    {
308 165
        $constants = new Constant($this->getGenerator());
309 165
        $this->fillClassConstants($constants);
310 165
        foreach ($constants as $constant) {
311 43
            $annotationBlock = $this->getConstantAnnotationBlock($constant);
312 43
            if (!empty($annotationBlock)) {
313 43
                $class->addAnnotationBlockElement($annotationBlock);
314
            }
315 43
            $class->addConstantElement($constant);
316
        }
317
318 165
        return $this;
319
    }
320
321 165
    protected function defineProperties(PhpClass $class): self
322
    {
323 165
        $properties = new Property($this->getGenerator());
324 165
        $this->fillClassProperties($properties);
325 165
        foreach ($properties as $property) {
326 101
            $annotationBlock = $this->getPropertyAnnotationBlock($property);
327 101
            if (!empty($annotationBlock)) {
328 101
                $class->addAnnotationBlockElement($annotationBlock);
329
            }
330 101
            $class->addPropertyElement($property);
331
        }
332
333 165
        return $this;
334
    }
335
336 165
    protected function defineMethods(PhpClass $class): self
337
    {
338 165
        $this->methods = new Method($this->getGenerator());
339 165
        $this->fillClassMethods();
340 165
        foreach ($this->methods as $method) {
341 161
            $annotationBlock = $this->getMethodAnnotationBlock($method);
342 161
            if (!empty($annotationBlock)) {
343 161
                $class->addAnnotationBlockElement($annotationBlock);
344
            }
345 161
            $class->addMethodElement($method);
346
        }
347
348 165
        return $this;
349
    }
350
351
    abstract protected function fillClassConstants(Constant $constants): void;
352
353
    abstract protected function getConstantAnnotationBlock(PhpConstant $constant): ?PhpAnnotationBlock;
354
355
    abstract protected function fillClassProperties(Property $properties): void;
356
357
    abstract protected function getPropertyAnnotationBlock(PhpProperty $property): ?PhpAnnotationBlock;
358
359
    abstract protected function fillClassMethods(): void;
360
361
    abstract protected function getMethodAnnotationBlock(PhpMethod $method): ?PhpAnnotationBlock;
362
363 113
    protected function getStructAttribute(StructAttributeModel $attribute = null): ?StructAttributeModel
364
    {
365 113
        $struct = $this->getModel();
366 113
        if (empty($attribute) && $struct instanceof StructModel && 1 === $struct->getAttributes()->count()) {
367 17
            $attribute = $struct->getAttributes()->offsetGet(0);
368
        }
369
370 113
        return $attribute;
371
    }
372
373 101
    protected function getStructAttributeTypeGetAnnotation(StructAttributeModel $attribute = null, bool $returnArrayType = true): string
374
    {
375 101
        $attribute = $this->getStructAttribute($attribute);
376
377 101
        if ($attribute->isXml()) {
378 2
            return '\\DOMDocument|string|null';
379
        }
380
381 101
        return sprintf('%s%s%s', $this->getStructAttributeTypeAsPhpType($attribute), $this->useBrackets($attribute, $returnArrayType) ? '[]' : '', $attribute->isRequired() ? '' : '|null');
382
    }
383
384 101
    protected function getStructAttributeTypeSetAnnotation(StructAttributeModel $attribute = null, bool $returnArrayType = true): string
385
    {
386 101
        $attribute = $this->getStructAttribute($attribute);
387
388 101
        return sprintf('%s%s', $this->getStructAttributeTypeAsPhpType($attribute), $this->useBrackets($attribute, $returnArrayType) ? '[]' : '');
389
    }
390
391 101
    protected function useBrackets(StructAttributeModel $attribute, bool $returnArrayType = true): bool
392
    {
393 101
        return $returnArrayType && $attribute->isArray();
394
    }
395
396 101
    protected function getStructAttributeTypeHint(StructAttributeModel $attribute = null, bool $returnArrayType = true): string
397
    {
398 101
        $attribute = $this->getStructAttribute($attribute);
399
400 101
        return ($returnArrayType && ($attribute->isArray() || $this->isAttributeAList($attribute))) ? self::TYPE_ARRAY : $this->getStructAttributeType($attribute, true);
401
    }
402
}
403