Passed
Push — 2.x ( bf6dcc...194582 )
by Mikaël
50:36 queued 24:11
created

Struct::getProperAttributes()   B

Complexity

Conditions 9
Paths 12

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 9

Importance

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