Passed
Push — feature/issue-124 ( 91e4aa...dde646 )
by Mikaël
09:46
created

AbstractModelFile::defineNamespace()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
rs 10
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 179
    public function getFileDestination(bool $withSrc = true): string
48
    {
49 179
        return sprintf(
50 179
            '%s%s%s',
51 179
            $this->getDestinationFolder($withSrc),
52 179
            $this->getModel()->getSubDirectory(),
53 179
            !empty($this->getModel()->getSubDirectory()) ? '/' : ''
54
        );
55
    }
56
57 179
    public function getDestinationFolder(bool $withSrc = true): string
58
    {
59 179
        $src = rtrim($this->generator->getOptionSrcDirname(), DIRECTORY_SEPARATOR);
60
61 179
        return sprintf(
62 179
            '%s%s%s%s',
63 179
            $this->getGenerator()->getOptionDestination(),
64 179
            (bool) $withSrc && !empty($src) ? $src.DIRECTORY_SEPARATOR : '',
65 179
            str_replace('\\', DIRECTORY_SEPARATOR, $this->getGenerator()->getOptionNamespacePrefix()),
66 179
            $this->getGenerator()->getOptionNamespacePrefix() ? DIRECTORY_SEPARATOR : ''
67
        );
68
    }
69
70 169
    public function writeFile(bool $withSrc = true): void
71
    {
72 169
        if (!$this->getModel()) {
73 2
            throw new InvalidArgumentException('You MUST define the model before being able to generate the file', __LINE__);
74
        }
75
76 167
        GeneratorUtils::createDirectory($this->getFileDestination($withSrc));
77
78
        $this
79 167
            ->addDeclareDirective()
80 167
            ->defineNamespace()
81 167
            ->defineUseStatements()
82 167
            ->addAnnotationBlock()
83 167
            ->addClassElement()
84
        ;
85
86 167
        parent::writeFile();
87 167
    }
88
89 193
    public function setModel(AbstractModel $model): self
90
    {
91 193
        $this->model = $model;
92
93
        $this
94 193
            ->getFile()
95 193
            ->getMainElement()
96 193
            ->setName($model->getPackagedName())
97
        ;
98
99 193
        return $this;
100
    }
101
102 195
    public function getModel(): ?AbstractModel
103
    {
104 195
        return $this->model;
105
    }
106
107 115
    public function getModelFromStructAttribute(StructAttributeModel $attribute = null): ?StructModel
108
    {
109 115
        return $this->getStructAttribute($attribute)->getTypeStruct();
110
    }
111
112 112
    public function getRestrictionFromStructAttribute(StructAttributeModel $attribute = null): ?StructModel
113
    {
114 112
        $model = $this->getModelFromStructAttribute($attribute);
115 112
        if ($model instanceof StructModel) {
116
            // list are mainly scalar values of basic types (string, int, etc.) or of Restriction values
117 102
            if ($model->isList()) {
118 6
                $subModel = $this->getModelByName($model->getList());
119 6
                if ($subModel && $subModel->isRestriction()) {
120 4
                    $model = $subModel;
121 2
                } elseif (!$model->isRestriction()) {
122 6
                    $model = null;
123
                }
124 98
            } elseif (!$model->isRestriction()) {
125 94
                $model = null;
126
            }
127
        }
128
129 112
        return $model;
130
    }
131
132 115
    public function getStructAttributeType(StructAttributeModel $attribute = null, bool $namespaced = false, bool $returnArrayType = true): string
133
    {
134 115
        $attribute = $this->getStructAttribute($attribute);
135
136 115
        if (!$attribute instanceof StructAttributeModel) {
0 ignored issues
show
introduced by
$attribute is always a sub-type of WsdlToPhp\PackageGenerator\Model\StructAttribute.
Loading history...
137
            throw new InvalidArgumentException(sprintf('Couldn\'t not find any valid StructAttribute'));
138
        }
139
140 115
        if ($returnArrayType && ($attribute->isArray())) {
141 53
            return self::TYPE_ARRAY;
142
        }
143
144 115
        $inheritance = $attribute->getInheritance();
145 115
        $type = empty($inheritance) ? $attribute->getType() : $inheritance;
146
147 115
        if (!empty($type) && ($struct = $this->getGenerator()->getStructByName($type))) {
148 99
            $inheritance = $struct->getTopInheritance();
149 99
            if (!empty($inheritance)) {
150 71
                $type = str_replace('[]', '', $inheritance);
151
            } else {
152 75
                $type = $struct->getPackagedName($namespaced);
153
            }
154
        }
155
156 115
        $model = $this->getModelFromStructAttribute($attribute);
157 115
        if ($model instanceof StructModel) {
158
            // issue #84: union is considered as string as it would be difficult to have a method that accepts multiple object types.
159
            // If the property has to be an object of multiple types => new issue...
160 105
            if ($model->isRestriction() || $model->isUnion()) {
161 53
                $type = self::TYPE_STRING;
162 101
            } elseif ($model->isStruct()) {
163 87
                $type = $model->getPackagedName($namespaced);
164 52
            } elseif ($model->isArray() && ($inheritanceStruct = $model->getInheritanceStruct()) instanceof StructModel) {
165 8
                $type = $inheritanceStruct->getPackagedName($namespaced);
166
            }
167
        }
168
169 115
        return $type;
170
    }
171
172 115
    public function getStructAttributeTypeAsPhpType(StructAttributeModel $fromAttribute = null, bool $returnArrayType = true): string
173
    {
174 115
        $attribute = $this->getStructAttribute($fromAttribute);
175
176 115
        if (!$attribute instanceof StructAttributeModel) {
0 ignored issues
show
introduced by
$attribute is always a sub-type of WsdlToPhp\PackageGenerator\Model\StructAttribute.
Loading history...
177
            throw new InvalidArgumentException(sprintf('Couldn\'t not find any valid StructAttribute'));
178
        }
179
180 115
        $attributeType = $this->getStructAttributeType($attribute, true, $returnArrayType);
181 115
        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

181
        if (XsdTypes::instance($this->getGenerator()->getOptionXsdTypesPath())->/** @scrutinizer ignore-call */ isXsd($attributeType)) {
Loading history...
182 93
            $attributeType = self::getPhpType($attributeType, $this->getGenerator()->getOptionXsdTypesPath());
183
        }
184
185 115
        return $attributeType;
186
    }
187
188
    /**
189
     * See http://php.net/manual/fr/language.oop5.typehinting.php for these cases
190
     * Also see http://www.w3schools.com/schema/schema_dtypes_numeric.asp.
191
     *
192
     * @param mixed $type
193
     * @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...
194
     * @param mixed $fallback
195
     *
196
     * @return mixed
197
     */
198
    public static function getValidType($type, $xsdTypesPath = null, $fallback = null)
199
    {
200
        return XsdTypes::instance($xsdTypesPath)->isXsd(str_replace('[]', '', $type)) ? $fallback : $type;
201
    }
202
203
    /**
204
     * See http://php.net/manual/fr/language.oop5.typehinting.php for these cases
205
     * Also see http://www.w3schools.com/schema/schema_dtypes_numeric.asp.
206
     *
207
     * @param mixed $type
208
     * @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...
209
     * @param mixed $fallback
210
     *
211
     * @return mixed
212
     */
213 103
    public static function getPhpType($type, $xsdTypesPath = null, $fallback = self::TYPE_STRING)
214
    {
215 103
        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

215
        return XsdTypes::instance($xsdTypesPath)->isXsd(str_replace('[]', '', $type)) ? XsdTypes::instance($xsdTypesPath)->/** @scrutinizer ignore-call */ phpType($type) : $fallback;
Loading history...
216
    }
217
218 167
    protected function addAnnotationBlock(): AbstractModelFile
219
    {
220 167
        $this->getFile()->addAnnotationBlockElement($this->getClassAnnotationBlock());
221
222 167
        return $this;
223
    }
224
225 18
    protected function getModelByName(string $name): ?StructModel
226
    {
227 18
        return $this->getGenerator()->getStructByName($name);
228
    }
229
230 157
    protected function definePackageAnnotations(PhpAnnotationBlock $block): self
231
    {
232 157
        $packageName = $this->getPackageName();
233 157
        if (!empty($packageName)) {
234 139
            $block->addChild(new PhpAnnotation(self::ANNOTATION_PACKAGE, $packageName));
235
        }
236 157
        if (count($this->getModel()->getDocSubPackages()) > 0) {
237 157
            $block->addChild(new PhpAnnotation(self::ANNOTATION_SUB_PACKAGE, implode(',', $this->getModel()->getDocSubPackages())));
238
        }
239
240 157
        return $this;
241
    }
242
243 157
    protected function getPackageName(): string
244
    {
245 157
        $packageName = '';
246 157
        if (!empty($this->getGenerator()->getOptionPrefix())) {
247 135
            $packageName = $this->getGenerator()->getOptionPrefix();
248 22
        } elseif (!empty($this->getGenerator()->getOptionSuffix())) {
249 4
            $packageName = $this->getGenerator()->getOptionSuffix();
250
        }
251
252 157
        return $packageName;
253
    }
254
255 157
    protected function defineGeneralAnnotations(PhpAnnotationBlock $block): self
256
    {
257 157
        foreach ($this->getGenerator()->getOptionAddComments() as $tagName => $tagValue) {
258 145
            $block->addChild(new PhpAnnotation($tagName, $tagValue));
259
        }
260
261 157
        return $this;
262
    }
263
264 157
    protected function getClassAnnotationBlock(): PhpAnnotationBlock
265
    {
266 157
        $block = new PhpAnnotationBlock();
267 157
        $block->addChild($this->getClassDeclarationLine());
268 157
        $this->defineModelAnnotationsFromWsdl($block)->definePackageAnnotations($block)->defineGeneralAnnotations($block);
269
270 157
        return $block;
271
    }
272
273 157
    protected function getClassDeclarationLine(): string
274
    {
275 157
        return sprintf($this->getClassDeclarationLineText(), $this->getModel()->getName(), $this->getModel()->getContextualPart());
276
    }
277
278 149
    protected function getClassDeclarationLineText(): string
279
    {
280 149
        return 'This class stands for %s %s';
281
    }
282
283 157
    protected function defineModelAnnotationsFromWsdl(PhpAnnotationBlock $block, AbstractModel $model = null): self
284
    {
285 157
        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

285
        FileUtils::defineModelAnnotationsFromWsdl($block, /** @scrutinizer ignore-type */ $model instanceof AbstractModel ? $model : $this->getModel());
Loading history...
286
287 157
        return $this;
288
    }
289
290 167
    protected function addClassElement(): AbstractModelFile
291
    {
292 167
        $class = new PhpClass($this->getModel()->getPackagedName(), $this->getModel()->isAbstract(), '' === $this->getModel()->getExtendsClassName() ? null : $this->getModel()->getExtendsClassName());
293
        $this
294 167
            ->defineConstants($class)
295 167
            ->defineProperties($class)
296 167
            ->defineMethods($class)
297 167
            ->getFile()
298 167
            ->addClassComponent($class)
299
        ;
300
301 167
        return $this;
302
    }
303
304 167
    protected function addDeclareDirective(): self
305
    {
306 167
        $this->getFile()->setDeclare(PhpDeclare::DIRECTIVE_STRICT_TYPES, 1);
307
308 167
        return $this;
309
    }
310
311 167
    protected function defineNamespace(): self
312
    {
313 167
        if (!empty($this->getModel()->getNamespace())) {
314 163
            $this->getFile()->setNamespace($this->getModel()->getNamespace());
315
        }
316
317 167
        return $this;
318
    }
319
320 167
    protected function defineUseStatements(): self
321
    {
322 167
        if (!empty($this->getModel()->getExtends())) {
323 157
            $this->getFile()->addUse($this->getModel()->getExtends(), null, true);
324
        }
325
326 167
        return $this;
327
    }
328
329 167
    protected function defineConstants(PhpClass $class): self
330
    {
331 167
        $constants = new Constant($this->getGenerator());
332 167
        $this->fillClassConstants($constants);
333 167
        foreach ($constants as $constant) {
334 43
            $annotationBlock = $this->getConstantAnnotationBlock($constant);
335 43
            if (!empty($annotationBlock)) {
336 43
                $class->addAnnotationBlockElement($annotationBlock);
337
            }
338 43
            $class->addConstantElement($constant);
339
        }
340
341 167
        return $this;
342
    }
343
344 167
    protected function defineProperties(PhpClass $class): self
345
    {
346 167
        $properties = new Property($this->getGenerator());
347 167
        $this->fillClassProperties($properties);
348 167
        foreach ($properties as $property) {
349 103
            $annotationBlock = $this->getPropertyAnnotationBlock($property);
350 103
            if (!empty($annotationBlock)) {
351 103
                $class->addAnnotationBlockElement($annotationBlock);
352
            }
353 103
            $class->addPropertyElement($property);
354
        }
355
356 167
        return $this;
357
    }
358
359 167
    protected function defineMethods(PhpClass $class): self
360
    {
361 167
        $this->methods = new Method($this->getGenerator());
362 167
        $this->fillClassMethods();
363 167
        foreach ($this->methods as $method) {
364 163
            $annotationBlock = $this->getMethodAnnotationBlock($method);
365 163
            if (!empty($annotationBlock)) {
366 163
                $class->addAnnotationBlockElement($annotationBlock);
367
            }
368 163
            $class->addMethodElement($method);
369
        }
370
371 167
        return $this;
372
    }
373
374
    abstract protected function fillClassConstants(Constant $constants): void;
375
376
    abstract protected function getConstantAnnotationBlock(PhpConstant $constant): ?PhpAnnotationBlock;
377
378
    abstract protected function fillClassProperties(Property $properties): void;
379
380
    abstract protected function getPropertyAnnotationBlock(PhpProperty $property): ?PhpAnnotationBlock;
381
382
    abstract protected function fillClassMethods(): void;
383
384
    abstract protected function getMethodAnnotationBlock(PhpMethod $method): ?PhpAnnotationBlock;
385
386 115
    protected function getStructAttribute(StructAttributeModel $attribute = null): ?StructAttributeModel
387
    {
388 115
        $struct = $this->getModel();
389 115
        if (empty($attribute) && $struct instanceof StructModel && 1 === $struct->getAttributes()->count()) {
390 17
            $attribute = $struct->getAttributes()->offsetGet(0);
391
        }
392
393 115
        return $attribute;
394
    }
395
396 103
    protected function getStructAttributeTypeGetAnnotation(StructAttributeModel $attribute = null, bool $returnArrayType = true, bool $nullableItemType = false): string
397
    {
398 103
        $attribute = $this->getStructAttribute($attribute);
399
400 103
        if ($attribute->isXml()) {
401 4
            return '\\DOMDocument|string|null';
402
        }
403
404 103
        return sprintf('%s%s%s', $this->getStructAttributeTypeAsPhpType($attribute, false), $this->useBrackets($attribute, $returnArrayType) ? '[]' : '', !$nullableItemType && ($attribute->isRequired() || $attribute->isArray() || $attribute->isList()) ? '' : '|null');
405
    }
406
407 103
    protected function getStructAttributeTypeSetAnnotation(StructAttributeModel $attribute, bool $returnArrayType = true, bool $itemType = false): string
408
    {
409 103
        if ($attribute->isXml()) {
410 4
            return '\\DOMDocument|string|null';
411
        }
412
413 103
        if ($attribute->isList()) {
414 6
            return 'array|string';
415
        }
416
417 103
        return sprintf('%s%s', $this->getStructAttributeTypeAsPhpType($attribute, $returnArrayType), $this->useBrackets($attribute, !$itemType) ? '[]' : '');
418
    }
419
420 103
    protected function useBrackets(StructAttributeModel $attribute, bool $returnArrayType = true): bool
421
    {
422 103
        return $returnArrayType && $attribute->isArray();
423
    }
424
425
    protected function getStructAttributeTypeHint(StructAttributeModel $attribute = null, bool $returnArrayType = true): string
426
    {
427
        $attribute = $this->getStructAttribute($attribute);
428
429
        return ($returnArrayType && ($attribute->isArray() || $attribute->isList())) ? self::TYPE_ARRAY : $this->getStructAttributeType($attribute, true);
430
    }
431
}
432