Completed
Push — master ( ab2db9...9b8f8b )
by Mikaël
23:04
created

getRestrictionFromStructAttribute()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 7.0145

Importance

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