Passed
Push — feature/issue-165 ( 2b7e50...101bca )
by Mikaël
14:10
created

AbstractModelFile   F

Complexity

Total Complexity 84

Size/Duplication

Total Lines 504
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 154
dl 0
loc 504
ccs 182
cts 182
cp 1
rs 2
c 0
b 0
f 0
wmc 84

35 Methods

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