Passed
Push — develop ( 0a8b55...80d7ca )
by Mikaël
06:50
created

AbstractModelFile::defineMethods()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 8
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 13
ccs 9
cts 9
cp 1
crap 3
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 178
    public function getFileDestination(bool $withSrc = true): string
48
    {
49 178
        return sprintf(
50 178
            '%s%s%s',
51 178
            $this->getDestinationFolder($withSrc),
52 178
            $this->getModel()->getSubDirectory(),
53 178
            !empty($this->getModel()->getSubDirectory()) ? '/' : ''
54
        );
55
    }
56
57 178
    public function getDestinationFolder(bool $withSrc = true): string
58
    {
59 178
        $src = rtrim($this->generator->getOptionSrcDirname(), DIRECTORY_SEPARATOR);
60
61 178
        return sprintf(
62 178
            '%s%s%s%s',
63 178
            $this->getGenerator()->getOptionDestination(),
64 178
            (bool) $withSrc && !empty($src) ? $src.DIRECTORY_SEPARATOR : '',
65 178
            str_replace('\\', DIRECTORY_SEPARATOR, $this->getGenerator()->getOptionNamespacePrefix()),
66 178
            $this->getGenerator()->getOptionNamespacePrefix() ? DIRECTORY_SEPARATOR : ''
67
        );
68
    }
69
70 168
    public function writeFile(bool $withSrc = true): void
71
    {
72 168
        if (!$this->getModel()) {
73 2
            throw new InvalidArgumentException('You MUST define the model before being able to generate the file', __LINE__);
74
        }
75
76 166
        GeneratorUtils::createDirectory($this->getFileDestination($withSrc));
77
78
        $this
79 166
            ->addDeclareDirective()
80 166
            ->defineNamespace()
81 166
            ->defineUseStatements()
82 166
            ->addAnnotationBlock()
83 166
            ->addClassElement()
84
        ;
85
86 166
        parent::writeFile();
87 166
    }
88
89 192
    public function setModel(AbstractModel $model): self
90
    {
91 192
        $this->model = $model;
92
93
        $this
94 192
            ->getFile()
95 192
            ->getMainElement()
96 192
            ->setName($model->getPackagedName())
97
        ;
98
99 192
        return $this;
100
    }
101
102 194
    public function getModel(): ?AbstractModel
103
    {
104 194
        return $this->model;
105
    }
106
107 114
    public function getModelFromStructAttribute(StructAttributeModel $attribute = null): ?StructModel
108
    {
109 114
        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 114
    public function getStructAttributeType(StructAttributeModel $attribute = null, bool $namespaced = false, bool $returnArrayType = true): string
133
    {
134 114
        $attribute = $this->getStructAttribute($attribute);
135
136 114
        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 114
        if ($returnArrayType && ($attribute->isArray())) {
141 52
            return self::TYPE_ARRAY;
142
        }
143
144 114
        $inheritance = $attribute->getInheritance();
145 114
        $type = empty($inheritance) ? $attribute->getType() : $inheritance;
146
147 114
        if (!empty($type) && ($struct = $this->getGenerator()->getStructByName($type))) {
148 98
            $inheritance = $struct->getTopInheritance();
149 98
            if (!empty($inheritance)) {
150 70
                $type = str_replace('[]', '', $inheritance);
151
            } else {
152 74
                $type = $struct->getPackagedName($namespaced);
153
            }
154
        }
155
156 114
        $model = $this->getModelFromStructAttribute($attribute);
157 114
        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 104
            if ($model->isRestriction() || $model->isUnion()) {
161 52
                $type = self::TYPE_STRING;
162 100
            } elseif ($model->isStruct()) {
163 86
                $type = $model->getPackagedName($namespaced);
164 52
            } elseif ($model->isArray() && ($inheritanceStruct = $model->getInheritanceStruct()) instanceof StructModel) {
165 8
                $type = $inheritanceStruct->getPackagedName($namespaced);
166
            }
167
        }
168
169 114
        return $type;
170
    }
171
172 114
    public function getStructAttributeTypeAsPhpType(StructAttributeModel $fromAttribute = null, bool $returnArrayType = true): string
173
    {
174 114
        $attribute = $this->getStructAttribute($fromAttribute);
175
176 114
        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 114
        $attributeType = $this->getStructAttributeType($attribute, true, $returnArrayType);
181 114
        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 92
            $attributeType = self::getPhpType($attributeType, $this->getGenerator()->getOptionXsdTypesPath());
183
        }
184
185 114
        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 102
    public static function getPhpType($type, $xsdTypesPath = null, $fallback = self::TYPE_STRING)
214
    {
215 102
        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 166
    protected function addAnnotationBlock(): AbstractModelFile
219
    {
220 166
        $this->getFile()->addAnnotationBlockElement($this->getClassAnnotationBlock());
221
222 166
        return $this;
223
    }
224
225 18
    protected function getModelByName(string $name): ?StructModel
226
    {
227 18
        return $this->getGenerator()->getStructByName($name);
228
    }
229
230 156
    protected function definePackageAnnotations(PhpAnnotationBlock $block): self
231
    {
232 156
        $packageName = $this->getPackageName();
233 156
        if (!empty($packageName)) {
234 138
            $block->addChild(new PhpAnnotation(self::ANNOTATION_PACKAGE, $packageName));
235
        }
236 156
        if (count($this->getModel()->getDocSubPackages()) > 0) {
237 156
            $block->addChild(new PhpAnnotation(self::ANNOTATION_SUB_PACKAGE, implode(',', $this->getModel()->getDocSubPackages())));
238
        }
239
240 156
        return $this;
241
    }
242
243 156
    protected function getPackageName(): string
244
    {
245 156
        $packageName = '';
246 156
        if (!empty($this->getGenerator()->getOptionPrefix())) {
247 134
            $packageName = $this->getGenerator()->getOptionPrefix();
248 22
        } elseif (!empty($this->getGenerator()->getOptionSuffix())) {
249 4
            $packageName = $this->getGenerator()->getOptionSuffix();
250
        }
251
252 156
        return $packageName;
253
    }
254
255 156
    protected function defineGeneralAnnotations(PhpAnnotationBlock $block): self
256
    {
257 156
        foreach ($this->getGenerator()->getOptionAddComments() as $tagName => $tagValue) {
258 144
            $block->addChild(new PhpAnnotation($tagName, $tagValue));
259
        }
260
261 156
        return $this;
262
    }
263
264 156
    protected function getClassAnnotationBlock(): PhpAnnotationBlock
265
    {
266 156
        $block = new PhpAnnotationBlock();
267 156
        $block->addChild($this->getClassDeclarationLine());
268 156
        $this->defineModelAnnotationsFromWsdl($block)->definePackageAnnotations($block)->defineGeneralAnnotations($block);
269
270 156
        return $block;
271
    }
272
273 156
    protected function getClassDeclarationLine(): string
274
    {
275 156
        return sprintf($this->getClassDeclarationLineText(), $this->getModel()->getName(), $this->getModel()->getContextualPart());
276
    }
277
278 148
    protected function getClassDeclarationLineText(): string
279
    {
280 148
        return 'This class stands for %s %s';
281
    }
282
283 156
    protected function defineModelAnnotationsFromWsdl(PhpAnnotationBlock $block, AbstractModel $model = null): self
284
    {
285 156
        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 156
        return $this;
288
    }
289
290 166
    protected function addClassElement(): AbstractModelFile
291
    {
292 166
        $class = new PhpClass($this->getModel()->getPackagedName(), $this->getModel()->isAbstract(), '' === $this->getModel()->getExtendsClassName() ? null : $this->getModel()->getExtendsClassName());
293
        $this
294 166
            ->defineConstants($class)
295 166
            ->defineProperties($class)
296 166
            ->defineMethods($class)
297 166
            ->getFile()
298 166
            ->addClassComponent($class)
299
        ;
300
301 166
        return $this;
302
    }
303
304 166
    protected function addDeclareDirective(): self
305
    {
306 166
        $this->getFile()->setDeclare(PhpDeclare::DIRECTIVE_STRICT_TYPES, 1);
307
308 166
        return $this;
309
    }
310
311 166
    protected function defineNamespace(): self
312
    {
313 166
        if (!empty($this->getModel()->getNamespace())) {
314 156
            $this->getFile()->setNamespace($this->getModel()->getNamespace());
315
        }
316
317 166
        return $this;
318
    }
319
320 166
    protected function defineUseStatements(): self
321
    {
322 166
        if (!empty($this->getModel()->getExtends())) {
323 156
            $this->getFile()->addUse($this->getModel()->getExtends(), null, true);
324
        }
325
326 166
        return $this;
327
    }
328
329 166
    protected function defineConstants(PhpClass $class): self
330
    {
331 166
        $constants = new Constant($this->getGenerator());
332 166
        $this->fillClassConstants($constants);
333 166
        foreach ($constants as $constant) {
334 42
            $annotationBlock = $this->getConstantAnnotationBlock($constant);
335 42
            if (!empty($annotationBlock)) {
336 42
                $class->addAnnotationBlockElement($annotationBlock);
337
            }
338 42
            $class->addConstantElement($constant);
339
        }
340
341 166
        return $this;
342
    }
343
344 166
    protected function defineProperties(PhpClass $class): self
345
    {
346 166
        $properties = new Property($this->getGenerator());
347 166
        $this->fillClassProperties($properties);
348 166
        foreach ($properties as $property) {
349 102
            $annotationBlock = $this->getPropertyAnnotationBlock($property);
350 102
            if (!empty($annotationBlock)) {
351 102
                $class->addAnnotationBlockElement($annotationBlock);
352
            }
353 102
            $class->addPropertyElement($property);
354
        }
355
356 166
        return $this;
357
    }
358
359 166
    protected function defineMethods(PhpClass $class): self
360
    {
361 166
        $this->methods = new Method($this->getGenerator());
362 166
        $this->fillClassMethods();
363 166
        foreach ($this->methods as $method) {
364 162
            $annotationBlock = $this->getMethodAnnotationBlock($method);
365 162
            if (!empty($annotationBlock)) {
366 162
                $class->addAnnotationBlockElement($annotationBlock);
367
            }
368 162
            $class->addMethodElement($method);
369
        }
370
371 166
        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 114
    protected function getStructAttribute(StructAttributeModel $attribute = null): ?StructAttributeModel
387
    {
388 114
        $struct = $this->getModel();
389 114
        if (empty($attribute) && $struct instanceof StructModel && 1 === $struct->getAttributes()->count()) {
390 16
            $attribute = $struct->getAttributes()->offsetGet(0);
391
        }
392
393 114
        return $attribute;
394
    }
395
396 102
    protected function getStructAttributeTypeGetAnnotation(StructAttributeModel $attribute = null, bool $returnArrayType = true, bool $nullableItemType = false): string
397
    {
398 102
        $attribute = $this->getStructAttribute($attribute);
399
400 102
        if ($attribute->isXml()) {
401 4
            return '\\DOMDocument|string|null';
402
        }
403
404 102
        return sprintf('%s%s%s', $this->getStructAttributeTypeAsPhpType($attribute, false), $this->useBrackets($attribute, $returnArrayType) ? '[]' : '', !$nullableItemType && ($attribute->isRequired() || $attribute->isArray() || $attribute->isList()) ? '' : '|null');
405
    }
406
407 102
    protected function getStructAttributeTypeSetAnnotation(StructAttributeModel $attribute, bool $returnArrayType = true, bool $itemType = false): string
408
    {
409 102
        if ($attribute->isXml()) {
410 4
            return '\\DOMDocument|string|null';
411
        }
412
413 102
        if ($attribute->isList()) {
414 6
            return 'array|string';
415
        }
416
417 102
        return sprintf('%s%s', $this->getStructAttributeTypeAsPhpType($attribute, $returnArrayType), $this->useBrackets($attribute, !$itemType) ? '[]' : '');
418
    }
419
420 102
    protected function useBrackets(StructAttributeModel $attribute, bool $returnArrayType = true): bool
421
    {
422 102
        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