Passed
Push — feature/issue-124 ( e82cdf...9eff56 )
by Mikaël
08:25
created

Struct   F

Complexity

Total Complexity 89

Size/Duplication

Total Lines 444
Duplicated Lines 0 %

Test Coverage

Coverage 96.79%

Importance

Changes 6
Bugs 1 Features 3
Metric Value
eloc 166
c 6
b 1
f 3
dl 0
loc 444
ccs 181
cts 187
cp 0.9679
rs 2
wmc 89

38 Methods

Rating   Name   Duplication   Size   Complexity  
A setList() 0 5 1
A getReservedMethodsInstance() 0 8 2
A getTypes() 0 3 1
A __construct() 0 9 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 setValues() 0 5 1
A countAllAttributes() 0 3 1
A getList() 0 3 1
A putRequiredAttributesFirst() 0 21 5
A addAttribute() 0 11 4
A getAttributes() 0 9 3
A setAttributes() 0 5 1
B getProperAttributes() 0 23 9
A toJsonSerialize() 0 9 1
A addValue() 0 22 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

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