Passed
Push — feature/issue-246 ( 79981d )
by Mikaël
14:50
created

AbstractModelFile::writeFile()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

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