Passed
Pull Request — develop (#282)
by Mikaël
27:56 queued 25:33
created

Struct::getTopInheritance()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 9
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 15
ccs 10
cts 10
cp 1
crap 4
rs 9.9666
1
<?php
2
3
declare(strict_types=1);
4
5
namespace WsdlToPhp\PackageGenerator\Model;
6
7
use InvalidArgumentException;
8
use WsdlToPhp\PackageGenerator\ConfigurationReader\AbstractReservedWord;
9
use WsdlToPhp\PackageGenerator\ConfigurationReader\StructArrayReservedMethod;
10
use WsdlToPhp\PackageGenerator\ConfigurationReader\StructReservedMethod;
11
use WsdlToPhp\PackageGenerator\Container\Model\StructAttribute as StructAttributeContainer;
12
use WsdlToPhp\PackageGenerator\Container\Model\StructValue as StructValueContainer;
13
use WsdlToPhp\PackageGenerator\Generator\Generator;
14
use WsdlToPhp\PackageGenerator\Generator\Utils;
15
16
/**
17
 * Class Struct stands for an available struct described in the WSDL.
18
 */
19
final class Struct extends AbstractModel
20
{
21
    public const DOC_SUB_PACKAGE_STRUCTS = 'Structs';
22
    public const DOC_SUB_PACKAGE_ENUMERATIONS = 'Enumerations';
23
    public const DOC_SUB_PACKAGE_ARRAYS = 'Arrays';
24
    public const DEFAULT_ENUM_TYPE = 'string';
25
26
    /**
27
     * Attributes of the struct.
28
     */
29
    protected StructAttributeContainer $attributes;
30
31
    /**
32
     * Is the struct a restriction with defined values  ?
33
     */
34
    protected bool $isRestriction = false;
35
36
    /**
37
     * If the struct is a restriction with values, then store values.
38
     */
39
    protected StructValueContainer $values;
40
41
    /**
42
     * If the struct is a union with types, then store types.
43
     *
44
     * @var string[]
45
     */
46
    protected array $types = [];
47
48
    /**
49
     * Defines if the current struct is a concrete struct or just a virtual struct to store meta information.
50
     */
51
    protected bool $isStruct = false;
52
53
    /**
54
     * Defines if the current struct is a list of a type or not.
55
     * If it is a list of a type, then the list property value is the type.
56
     */
57
    protected string $list = '';
58
59 430
    public function __construct(Generator $generator, $name, $isStruct = true, $isRestriction = false)
60
    {
61 430
        parent::__construct($generator, $name);
62 430
        $this
63 430
            ->setStruct($isStruct)
64 430
            ->setRestriction($isRestriction)
65 430
            ->setAttributes(new StructAttributeContainer($generator))
66 430
            ->setValues(new StructValueContainer($generator))
67 430
        ;
68
    }
69
70 226
    public function getContextualPart(): string
71
    {
72 226
        $part = $this->getGenerator()->getOptionStructsFolder();
73 226
        if ($this->isRestriction()) {
74 76
            $part = $this->getGenerator()->getOptionEnumsFolder();
75 204
        } elseif ($this->isArray()) {
76 30
            $part = $this->getGenerator()->getOptionArraysFolder();
77
        }
78
79 226
        return $part;
80
    }
81
82 130
    public function getDocSubPackages(): array
83
    {
84 130
        $package = self::DOC_SUB_PACKAGE_STRUCTS;
85 130
        if ($this->isRestriction()) {
86 46
            $package = self::DOC_SUB_PACKAGE_ENUMERATIONS;
87 110
        } elseif ($this->isArray()) {
88 16
            $package = self::DOC_SUB_PACKAGE_ARRAYS;
89
        }
90
91 130
        return [
92 130
            $package,
93 130
        ];
94
    }
95
96 242
    public function isArray(): bool
97
    {
98 242
        return
99 242
            (
100 242
                (
101 242
                    ($this->isStruct() && 1 === $this->countAllAttributes())
102 242
                    || (!$this->isStruct() && 1 >= $this->countOwnAttributes())
103 242
                )
104 242
                && false !== mb_stripos($this->getName(), 'array')
105 242
            )
106 242
            || (!$this->isStruct() && false !== $this->getMetaValueFirstSet(['arraytype', 'arrayType'], false))
107 242
        ;
108
    }
109
110 276
    public function getAttributes(bool $includeInheritanceAttributes = false, bool $requiredFirst = false): StructAttributeContainer
111
    {
112 276
        if (!$includeInheritanceAttributes && !$requiredFirst) {
113 186
            $attributes = $this->attributes;
114
        } else {
115 236
            $attributes = $this->getAllAttributes($includeInheritanceAttributes, $requiredFirst);
116
        }
117
118 276
        return $attributes;
119
    }
120
121
    /**
122
     * Returns the attributes of the struct and not the ones that are declared by the parent struct if this struct inherits from a Struct
123
     * This means it removes from the attributes this Struct has the attributes declared by its parent class(es).
124
     *
125
     * @param bool $requiredFirst places the required attributes first, then the not required in order to have the _construct method with the required attribute at first
126
     */
127 132
    public function getProperAttributes(bool $requiredFirst = false): StructAttributeContainer
128
    {
129 132
        $properAttributes = new StructAttributeContainer($this->getGenerator());
130 132
        $parentAttributes = new StructAttributeContainer($this->getGenerator());
131
132 132
        if (!empty($this->getInheritance()) && ($model = $this->getInheritanceStruct()) instanceof Struct) {
133 20
            while ($model instanceof Struct && $model->isStruct()) {
134 18
                foreach ($model->getAttributes() as $attribute) {
135 16
                    $parentAttributes->add($attribute);
136
                }
137 18
                $model = $model->getInheritanceStruct();
138
            }
139
        }
140
141
        /** @var StructAttribute $attribute */
142 132
        foreach ($this->getAttributes() as $attribute) {
143 112
            if ($parentAttributes->getStructAttributeByName($attribute->getName())) {
144 4
                continue;
145
            }
146 108
            $properAttributes->add($attribute);
147
        }
148
149 132
        return $requiredFirst ? $this->putRequiredAttributesFirst($properAttributes) : $properAttributes;
150
    }
151
152 102
    public function countOwnAttributes(): int
153
    {
154 102
        return $this->getAttributes()->count();
155
    }
156
157 232
    public function countAllAttributes(): int
158
    {
159 232
        return $this->getAttributes(true)->count();
160
    }
161
162 430
    public function setAttributes(StructAttributeContainer $structAttributeContainer): self
163
    {
164 430
        $this->attributes = $structAttributeContainer;
165
166 430
        return $this;
167
    }
168
169 146
    public function addAttribute(string $attributeName, string $attributeType): self
170
    {
171 146
        if (empty($attributeName) || empty($attributeType)) {
172 4
            throw new InvalidArgumentException(sprintf('Attribute name "%s" and/or attribute type "%s" is invalid for Struct "%s"', $attributeName, $attributeType, $this->getName()), __LINE__);
173
        }
174 142
        if (is_null($this->attributes->getStructAttributeByName($attributeName))) {
175 142
            $structAttribute = new StructAttribute($this->getGenerator(), $attributeName, $attributeType, $this);
176 142
            $this->attributes->add($structAttribute);
177
        }
178
179 142
        return $this;
180
    }
181
182 158
    public function getAttribute(string $attributeName): ?StructAttribute
183
    {
184 158
        return $this->attributes->getStructAttributeByName($attributeName);
185
    }
186
187 42
    public function getAttributeByCleanName(string $attributeCleanName): ?StructAttribute
188
    {
189 42
        return $this->attributes->getStructAttributeByCleanName($attributeCleanName);
190
    }
191
192 240
    public function isRestriction(): bool
193
    {
194 240
        return $this->isRestriction;
195
    }
196
197 430
    public function setRestriction($isRestriction = true): self
198
    {
199 430
        $this->isRestriction = $isRestriction;
200
201 430
        return $this;
202
    }
203
204 274
    public function isStruct(): bool
205
    {
206 274
        return $this->isStruct;
207
    }
208
209 430
    public function setStruct(bool $isStruct = true): self
210
    {
211 430
        $this->isStruct = $isStruct;
212
213 430
        return $this;
214
    }
215
216 10
    public function getList(): string
217
    {
218 10
        return $this->list;
219
    }
220
221 106
    public function isList(): bool
222
    {
223 106
        return !empty($this->list);
224
    }
225
226 218
    public function setList(string $list = ''): self
227
    {
228 218
        $this->list = $list;
229
230 218
        return $this;
231
    }
232
233 66
    public function getValues(): StructValueContainer
234
    {
235 66
        return $this->values;
236
    }
237
238 34
    public function addValue($value): self
239
    {
240 34
        if (is_null($this->getValue($value))) {
241
            // issue #177, rare case: a struct and an enum has the same name and the enum is not detected by the SoapClient,
242
            // then we need to create the enumeration struct in order to deduplicate the two structs
243
            // this is why enumerations has to be parsed before any other elements by the WSDL parsers
244 34
            if (0 < $this->countOwnAttributes()) {
245
                $enum = new Struct($this->getGenerator(), $this->getName(), true, true);
246
                $enum->setInheritance(self::DEFAULT_ENUM_TYPE);
247
                $enum->getValues()->add(new StructValue($enum->getGenerator(), $value, $enum->getValues()->count(), $enum));
248
                $this->getGenerator()->getStructs()->add($enum);
249
250
                return $enum;
251
            }
252 34
            $this
253 34
                ->setStruct(true)
254 34
                ->setRestriction(true)
255 34
                ->getValues()->add(new StructValue($this->getGenerator(), $value, $this->getValues()->count(), $this));
256
        }
257
258 34
        return $this;
259
    }
260
261 66
    public function getValue($value): ?StructValue
262
    {
263 66
        return $this->values->getStructValueByName($value);
264
    }
265
266 126
    public function getExtends(bool $short = false): string
267
    {
268 126
        if ($this->isArray()) {
269 16
            $extends = $this->getGenerator()->getOptionStructArrayClass();
270 116
        } elseif ($this->isRestriction()) {
271 44
            $extends = $this->getGenerator()->getOptionStructEnumClass();
272
        } else {
273 98
            $extends = $this->getGenerator()->getOptionStructClass();
274
        }
275
276 126
        return $short ? Utils::removeNamespace($extends) : $extends;
277
    }
278
279 180
    public function getInheritanceStruct(): ?Struct
280
    {
281 180
        return $this->getName() === $this->getInheritance() ? null : $this->getGenerator()->getStructByName(str_replace('[]', '', $this->getInheritance()));
282
    }
283
284 116
    public function getTopInheritance(): string
285
    {
286 116
        $inheritance = $this->getInheritance();
287 116
        if (!empty($inheritance)) {
288 90
            $struct = $this->getInheritanceStruct();
289 90
            while ($struct instanceof Struct) {
290 46
                $structInheritance = $struct->getInheritance();
291 46
                if (!empty($structInheritance)) {
292 28
                    $inheritance = $structInheritance;
293
                }
294 46
                $struct = $struct->getInheritanceStruct();
295
            }
296
        }
297
298 116
        return $inheritance;
299
    }
300
301 8
    public function getTopInheritanceStruct(): ?Struct
302
    {
303 8
        $struct = $this->getInheritanceStruct();
304 8
        $latestValidStruct = $struct;
305 8
        while ($struct instanceof Struct) {
306 4
            $struct = $struct->getInheritanceStruct();
307 4
            if ($struct instanceof Struct) {
308
                $latestValidStruct = $struct;
309
            }
310
        }
311
312 8
        return $latestValidStruct;
313
    }
314
315 166
    public function getMeta(): array
316
    {
317 166
        $inheritanceStruct = $this->getInheritanceStruct();
318
319 166
        return $this->mergeMeta(($inheritanceStruct && !$inheritanceStruct->isStruct()) ? $inheritanceStruct->getMeta() : [], parent::getMeta());
320
    }
321
322 112
    public function getReservedMethodsInstance(?string $filename = null): AbstractReservedWord
323
    {
324 112
        $instance = StructReservedMethod::instance($filename);
325 112
        if ($this->isArray()) {
326 18
            $instance = StructArrayReservedMethod::instance($filename);
327
        }
328
329 112
        return $instance;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $instance returns the type WsdlToPhp\PackageGenerat...ader\AbstractYamlReader which includes types incompatible with the type-hinted return WsdlToPhp\PackageGenerat...er\AbstractReservedWord.
Loading history...
330
    }
331
332 2
    public function getTypes(): array
333
    {
334 2
        return $this->types;
335
    }
336
337 104
    public function isUnion(): bool
338
    {
339 104
        return 0 < count($this->types);
340
    }
341
342 230
    public function setTypes(array $types): self
343
    {
344 230
        $this->types = $types;
345
346 230
        return $this;
347
    }
348
349 210
    public function setAttributesFromSerializedJson(array $attributes): void
350
    {
351 210
        foreach ($attributes as $attribute) {
352 208
            $this->attributes->add(self::instanceFromSerializedJson($this->generator, $attribute)->setOwner($this));
353
        }
354
    }
355
356 210
    public function setValuesFromSerializedJson(array $values): void
357
    {
358 210
        foreach ($values as $value) {
359 194
            $this->values->add(self::instanceFromSerializedJson($this->generator, $value)->setOwner($this));
360
        }
361
    }
362
363 236
    protected function getAllAttributes(bool $includeInheritanceAttributes, bool $requiredFirst): StructAttributeContainer
364
    {
365 236
        $allAttributes = new StructAttributeContainer($this->getGenerator());
366 236
        if ($includeInheritanceAttributes) {
367 236
            $this->addInheritanceAttributes($allAttributes);
368
        }
369
370 236
        foreach ($this->attributes as $attribute) {
371 206
            $allAttributes->add($attribute);
372
        }
373
374 236
        if ($requiredFirst) {
375 2
            $attributes = $this->putRequiredAttributesFirst($allAttributes);
376
        } else {
377 236
            $attributes = $allAttributes;
378
        }
379
380 236
        return $attributes;
381
    }
382
383 236
    protected function addInheritanceAttributes(StructAttributeContainer $attributes): void
384
    {
385 236
        if (!empty($this->getInheritance()) && ($model = $this->getInheritanceStruct()) instanceof Struct) {
386 46
            while ($model instanceof Struct && $model->isStruct()) {
387 38
                foreach ($model->getAttributes() as $attribute) {
388 36
                    $attributes->add($attribute);
389
                }
390 38
                $model = $model->getInheritanceStruct();
391
            }
392
        }
393
    }
394
395 126
    protected function putRequiredAttributesFirst(StructAttributeContainer $allAttributes): StructAttributeContainer
396
    {
397 126
        $attributes = new StructAttributeContainer($this->getGenerator());
398 126
        $requiredAttributes = [];
399 126
        $notRequiredAttributes = [];
400 126
        $nullableNotRequiredAttributes = [];
401
402
        /** @var StructAttribute $attribute */
403 126
        foreach ($allAttributes as $attribute) {
404 104
            if ($attribute->isRequired() && !$attribute->isNullable()) {
405 32
                $requiredAttributes[] = $attribute;
406 94
            } elseif (!$attribute->isNullable()) {
407 90
                $notRequiredAttributes[] = $attribute;
408
            } else {
409 18
                $nullableNotRequiredAttributes[] = $attribute;
410
            }
411
        }
412
413 126
        array_walk($requiredAttributes, [$attributes, 'add']);
414 126
        array_walk($notRequiredAttributes, [$attributes, 'add']);
415 126
        array_walk($nullableNotRequiredAttributes, [$attributes, 'add']);
416
417 126
        unset($requiredAttributes, $notRequiredAttributes, $nullableNotRequiredAttributes);
418
419 126
        return $attributes;
420
    }
421
422 430
    protected function setValues(StructValueContainer $structValueContainer): self
423
    {
424 430
        $this->values = $structValueContainer;
425
426 430
        return $this;
427
    }
428
429 2
    protected function toJsonSerialize(): array
430
    {
431 2
        return [
432 2
            'attributes' => $this->attributes,
433 2
            'restriction' => $this->isRestriction,
434 2
            'struct' => $this->isStruct,
435 2
            'types' => $this->types,
436 2
            'values' => $this->values,
437 2
            'list' => $this->list,
438 2
        ];
439
    }
440
}
441