Struct   F
last analyzed

Complexity

Total Complexity 89

Size/Duplication

Total Lines 419
Duplicated Lines 0 %

Test Coverage

Coverage 96.94%

Importance

Changes 4
Bugs 1 Features 1
Metric Value
eloc 166
dl 0
loc 419
ccs 190
cts 196
cp 0.9694
rs 2
c 4
b 1
f 1
wmc 89

38 Methods

Rating   Name   Duplication   Size   Complexity  
A setValues() 0 5 1
A setList() 0 5 1
A getReservedMethodsInstance() 0 8 2
A getTypes() 0 3 1
A __construct() 0 8 1
A getInheritanceStruct() 0 3 2
A isList() 0 3 1
A setTypes() 0 5 1
A getExtends() 0 11 4
A isUnion() 0 3 1
A isRestriction() 0 3 1
A setRestriction() 0 5 1
A setAttributesFromSerializedJson() 0 4 2
A getAttributeByCleanName() 0 3 1
A getDocSubPackages() 0 11 3
A addInheritanceAttributes() 0 8 6
A getAllAttributes() 0 18 4
A getTopInheritance() 0 15 4
A getMeta() 0 5 3
A countAllAttributes() 0 3 1
A getList() 0 3 1
A addAttribute() 0 11 4
A getAttributes() 0 9 3
A setAttributes() 0 5 1
B getProperAttributes() 0 23 9
A addValue() 0 21 3
A getAttribute() 0 3 1
B isArray() 0 11 7
A isStruct() 0 3 1
A getContextualPart() 0 10 3
A getValue() 0 3 1
A getValues() 0 3 1
A countOwnAttributes() 0 3 1
A setStruct() 0 5 1
A setValuesFromSerializedJson() 0 4 2
A getTopInheritanceStruct() 0 12 3
A toJsonSerialize() 0 9 1
A putRequiredAttributesFirst() 0 25 5

How to fix   Complexity   

Complex Class

Complex classes like Struct often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Struct, and based on these observations, apply Extract Interface, too.

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