AbstractModelFile   F
last analyzed

Complexity

Total Complexity 89

Size/Duplication

Total Lines 398
Duplicated Lines 0 %

Test Coverage

Coverage 97.33%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 170
c 3
b 0
f 0
dl 0
loc 398
ccs 182
cts 187
cp 0.9733
rs 2
wmc 89

31 Methods

Rating   Name   Duplication   Size   Complexity  
B getRestrictionFromStructAttribute() 0 18 7
A addAnnotationBlock() 0 5 1
A defineMethods() 0 13 3
A getModel() 0 3 1
C getStructAttributeType() 0 38 14
A getStructAttributeTypeAsPhpType() 0 14 3
A addDeclareDirective() 0 5 1
A definePackageAnnotations() 0 11 3
A getFileDestination() 0 7 2
A defineProperties() 0 13 3
A defineModelAnnotationsFromWsdl() 0 5 2
B getStructAttributeTypeGetAnnotation() 0 9 7
A getModelFromStructAttribute() 0 3 1
A getClassDeclarationLineText() 0 3 1
A getValidType() 0 3 2
A useBrackets() 0 3 2
A defineGeneralAnnotations() 0 7 2
A addClassElement() 0 12 2
A defineNamespace() 0 7 2
A getStructAttributeTypeSetAnnotation() 0 11 4
A setModel() 0 11 1
A getPackageName() 0 10 3
A defineConstants() 0 13 3
A getStructAttribute() 0 8 4
A getClassAnnotationBlock() 0 7 1
A getDestinationFolder() 0 10 6
A getClassDeclarationLine() 0 3 1
A getPhpType() 0 3 2
A writeFile() 0 17 2
A defineUseStatements() 0 7 2
A getModelByName() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like AbstractModelFile often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractModelFile, and based on these observations, apply Extract Interface, too.

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
$attribute is always a sub-type of WsdlToPhp\PackageGenerator\Model\StructAttribute.
Loading history...
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
introduced by
$attribute is always a sub-type of WsdlToPhp\PackageGenerator\Model\StructAttribute.
Loading history...
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
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...
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
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...
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
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

284
        FileUtils::defineModelAnnotationsFromWsdl($block, /** @scrutinizer ignore-type */ $model instanceof AbstractModel ? $model : $this->getModel());
Loading history...
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('%s%s%s', $this->getStructAttributeTypeAsPhpType($attribute, false), $this->useBrackets($attribute, $returnArrayType) ? '[]' : '', !$nullableItemType && ($attribute->isRequired() || $attribute->isArray() || $attribute->isList()) ? '' : '|null');
404
    }
405
406 110
    protected function getStructAttributeTypeSetAnnotation(StructAttributeModel $attribute, bool $returnArrayType = true, bool $itemType = false): string
407
    {
408 110
        if ($attribute->isXml()) {
409 4
            return '\\DOMDocument|string|null';
410
        }
411
412 110
        if ($attribute->isList()) {
413 6
            return 'array|string';
414
        }
415
416 110
        return sprintf('%s%s', $this->getStructAttributeTypeAsPhpType($attribute, $returnArrayType), $this->useBrackets($attribute, !$itemType) ? '[]' : '');
417
    }
418
419 110
    protected function useBrackets(StructAttributeModel $attribute, bool $returnArrayType = true): bool
420
    {
421 110
        return $returnArrayType && $attribute->isArray();
422
    }
423
}
424