Passed
Push — master ( 5d2c29...59c75b )
by Mikaël
76:36 queued 50:13
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 0
Metric Value
cc 9
eloc 12
nc 12
nop 1
dl 0
loc 23
ccs 17
cts 17
cp 1
crap 9
rs 8.0555
c 0
b 0
f 0
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 1252
    public function __construct(Generator $generator, $name, $isStruct = true, $isRestriction = false)
74
    {
75 1252
        parent::__construct($generator, $name);
76 418
        $this
77 1252
            ->setStruct($isStruct)
78 1252
            ->setRestriction($isRestriction)
79 1252
            ->setAttributes(new StructAttributeContainer($generator))
80 1252
            ->setValues(new StructValueContainer($generator))
81 1252
            ->setTypes([]);
82 1252
    }
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 642
    public function getContextualPart()
90
    {
91 642
        $part = $this->getGenerator()->getOptionStructsFolder();
92 642
        if ($this->isRestriction()) {
93 216
            $part = $this->getGenerator()->getOptionEnumsFolder();
94 602
        } elseif ($this->isArray()) {
95 90
            $part = $this->getGenerator()->getOptionArraysFolder();
96 30
        }
97 642
        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 372
    public function getDocSubPackages()
107
    {
108 372
        $package = self::DOC_SUB_PACKAGE_STRUCTS;
109 372
        if ($this->isRestriction()) {
110 132
            $package = self::DOC_SUB_PACKAGE_ENUMERATIONS;
111 336
        } elseif ($this->isArray()) {
112 48
            $package = self::DOC_SUB_PACKAGE_ARRAYS;
113 16
        }
114
        return [
115 372
            $package,
116 124
        ];
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 696
    public function isArray()
125
    {
126
        return
127
        (
128
            (
129
                (
130 696
                    ($this->isStruct() && $this->countAllAttributes() === 1) ||
131 680
                    (!$this->isStruct() && $this->countOwnAttributes() <= 1)
132 232
                ) &&
133 414
                mb_stripos($this->getName(), 'array') !== false
134 138
            ) ||
135 686
            (!$this->isStruct() && $this->getMetaValueFirstSet(['arraytype', 'arrayType'], false) !== false)
136 232
        );
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 804
    public function getAttributes($includeInheritanceAttributes = false, $requiredFirst = false)
148
    {
149 804
        if (!$includeInheritanceAttributes && !$requiredFirst) {
150 552
            $attributes = $this->attributes;
151 184
        } else {
152 672
            $attributes = $this->getAllAttributes($includeInheritanceAttributes, $requiredFirst);
153
        }
154 804
        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 378
    public function getProperAttributes($requiredFirst = false)
164
    {
165 378
        $properAttributes = new StructAttributeContainer($this->getGenerator());
166 378
        $parentAttributes = new StructAttributeContainer($this->getGenerator());
167
168 378
        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 18
                }
173 54
                $model = $model->getInheritanceStruct();
174 18
            }
175 20
        }
176
177
        /** @var StructAttribute $attribute */
178 378
        foreach ($this->getAttributes() as $attribute) {
179 324
            if ($parentAttributes->getStructAttributeByName($attribute->getName())) {
180 12
                continue;
181
            }
182 312
            $properAttributes->add($attribute);
183 126
        }
184
185 378
        return $requiredFirst ? $this->putRequiredAttributesFirst($properAttributes) : $properAttributes;
186
    }
187
    /**
188
     * @param bool $includeInheritanceAttributes
189
     * @param bool $requiredFirst
190
     * @return StructAttributeContainer
191
     */
192 672
    protected function getAllAttributes($includeInheritanceAttributes, $requiredFirst)
193
    {
194 672
        $allAttributes = new StructAttributeContainer($this->getGenerator());
195 672
        if ($includeInheritanceAttributes) {
196 672
            $this->addInheritanceAttributes($allAttributes);
197 224
        }
198 672
        foreach ($this->attributes as $attribute) {
199 606
            $allAttributes->add($attribute);
200 224
        }
201 672
        if ($requiredFirst) {
202 6
            $attributes = $this->putRequiredAttributesFirst($allAttributes);
203 2
        } else {
204 672
            $attributes = $allAttributes;
205
        }
206 672
        return $attributes;
207
    }
208
    /**
209
     * @param StructAttributeContainer $attributes
210
     */
211 672
    protected function addInheritanceAttributes(StructAttributeContainer $attributes)
212
    {
213 672
        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 38
                }
218 114
                $model = $model->getInheritanceStruct();
219 38
            }
220 46
        }
221 672
    }
222
    /**
223
     * @param StructAttributeContainer $allAttributes
224
     * @return StructAttributeContainer
225
     */
226 360
    protected function putRequiredAttributesFirst(StructAttributeContainer $allAttributes)
227
    {
228 360
        $attributes = new StructAttributeContainer($this->getGenerator());
229 360
        $requiredAttributes = new StructAttributeContainer($this->getGenerator());
230 360
        $notRequiredAttributes = new StructAttributeContainer($this->getGenerator());
231 360
        foreach ($allAttributes as $attribute) {
232 300
            if ($attribute->isRequired()) {
233 96
                $requiredAttributes->add($attribute);
234 32
            } else {
235 280
                $notRequiredAttributes->add($attribute);
236
            }
237 120
        }
238 360
        foreach ($requiredAttributes as $attribute) {
239 96
            $attributes->add($attribute);
240 120
        }
241 360
        foreach ($notRequiredAttributes as $attribute) {
242 270
            $attributes->add($attribute);
243 120
        }
244 360
        unset($requiredAttributes, $notRequiredAttributes);
245 360
        return $attributes;
246
    }
247
    /**
248
     * Returns the number of own attributes
249
     * @uses Struct::getAttributes()
250
     * @return int
251
     */
252 306
    public function countOwnAttributes()
253
    {
254 306
        return $this->getAttributes()->count();
255
    }
256
    /**
257
     * Returns the number of all attributes
258
     * @uses Struct::getAttributes()
259
     * @return int
260
     */
261 660
    public function countAllAttributes()
262
    {
263 660
        return $this->getAttributes(true)->count();
264
    }
265
    /**
266
     * Sets the attributes of the struct
267
     * @param StructAttributeContainer $structAttributeContainer
268
     * @return Struct
269
     */
270 1252
    public function setAttributes(StructAttributeContainer $structAttributeContainer)
271
    {
272 1252
        $this->attributes = $structAttributeContainer;
273 1252
        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 142
        }
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 456
    public function getAttribute($attributeName)
300
    {
301 456
        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 684
    public function isRestriction()
318
    {
319 684
        return $this->isRestriction;
320
    }
321
    /**
322
     * Sets the isRestriction value
323
     * @param bool $isRestriction
324
     * @return Struct
325
     */
326 1252
    public function setRestriction($isRestriction = true)
327
    {
328 1252
        $this->isRestriction = $isRestriction;
329 1252
        return $this;
330
    }
331
    /**
332
     * Returns the isStruct value
333
     * @return bool
334
     */
335 774
    public function isStruct()
336
    {
337 774
        return $this->isStruct;
338
    }
339
    /**
340
     * Sets the isStruct value
341
     * @param bool $isStruct
342
     * @return Struct
343
     */
344 1252
    public function setStruct($isStruct = true)
345
    {
346 1252
        $this->isStruct = $isStruct;
347 1252
        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 312
    public function isList()
363
    {
364 312
        return !empty($this->list);
365
    }
366
    /**
367
     * Sets the list value
368
     * @param string $list
369
     * @return Struct
370
     */
371 606
    public function setList($list = '')
372
    {
373 606
        $this->list = $list;
374 606
        return $this;
375
    }
376
    /**
377
     * Returns the values for an enumeration
378
     * @return StructValueContainer
379
     */
380 198
    public function getValues()
381
    {
382 198
        return $this->values;
383
    }
384
    /**
385
     * Sets the values for an enumeration
386
     * @param StructValueContainer $structValueContainer
387
     * @return Struct
388
     */
389 1252
    protected function setValues(StructValueContainer $structValueContainer)
390
    {
391 1252
        $this->values = $structValueContainer;
392 1252
        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 108
    public function addValue($value)
402
    {
403 108
        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 108
            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 36
                $this
416 108
                    ->setStruct(true)
417 108
                    ->setRestriction(true)
418 108
                    ->getValues()->add(new StructValue($this->getGenerator(), $value, $this->getValues()->count(), $this));
419
            }
420 36
        }
421 108
        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 198
    public function getValue($value)
431
    {
432 198
        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 360
    public function getExtends($short = false)
440
    {
441 360
        $extends = '';
442 360
        if ($this->isArray()) {
443 48
            $extends = $this->getGenerator()->getOptionStructArrayClass();
444 340
        } elseif ($this->isRestriction()) {
445 126
            $extends = $this->getGenerator()->getOptionStructEnumClass();
446 42
        } else {
447 282
            $extends = $this->getGenerator()->getOptionStructClass();
448
        }
449 360
        return $short ? Utils::removeNamespace($extends) : $extends;
450
    }
451
    /**
452
     * @return Struct|null
453
     */
454 516
    public function getInheritanceStruct()
455
    {
456 516
        return $this->getName() === $this->getInheritance() ? null : $this->getGenerator()->getStructByName(str_replace('[]', '', $this->getInheritance()));
457
    }
458
    /**
459
     * @return string
460
     */
461 336
    public function getTopInheritance()
462
    {
463 336
        $inheritance = $this->getInheritance();
464 336
        if (!empty($inheritance)) {
465 258
            $struct = $this->getInheritanceStruct();
466 258
            while ($struct instanceof Struct) {
467 138
                $structInheritance = $struct->getInheritance();
468 138
                if (!empty($structInheritance)) {
469 84
                    $inheritance = $structInheritance;
470 28
                }
471 138
                $struct = $struct->getInheritanceStruct();
472 46
            }
473 86
        }
474 336
        return $inheritance;
475
    }
476
    /**
477
     * @return Struct|null
478
     */
479 24
    public function getTopInheritanceStruct()
480
    {
481 24
        $struct = $this->getInheritanceStruct();
482 24
        $latestValidStruct = $struct;
483 24
        while ($struct instanceof Struct) {
484 12
            $struct = $struct->getInheritanceStruct();
485 12
            if ($struct instanceof Struct) {
486
                $latestValidStruct = $struct;
487
            }
488 4
        }
489 24
        return $latestValidStruct;
490
    }
491
    /**
492
     * @see \WsdlToPhp\PackageGenerator\Model\AbstractModel::getMeta()
493
     * @return string[]
494
     */
495 474
    public function getMeta()
496
    {
497 474
        $inheritanceStruct = $this->getInheritanceStruct();
498 474
        return $this->mergeMeta(($inheritanceStruct && !$inheritanceStruct->isStruct()) ? $inheritanceStruct->getMeta() : [], parent::getMeta());
499
    }
500
    /**
501
     * @param $filename
502
     * @return StructReservedMethod|StructArrayReservedMethod
503
     */
504 324
    public function getReservedMethodsInstance($filename = null)
505
    {
506 324
        $instance = StructReservedMethod::instance($filename);
507 324
        if ($this->isArray()) {
508 54
            $instance = StructArrayReservedMethod::instance($filename);
509 18
        }
510 324
        return $instance;
511
    }
512
    /**
513
     * @return string[]
514
     */
515 6
    public function getTypes()
516
    {
517 6
        return $this->types;
518
    }
519
    /**
520
     * @return boolean
521
     */
522 300
    public function isUnion()
523
    {
524 300
        return count($this->types) > 0;
525
    }
526
    /**
527
     * @param string[] $types
528
     * @return Struct
529
     */
530 1252
    public function setTypes(array $types)
531
    {
532 1252
        $this->types = $types;
533 1252
        return $this;
534
    }
535
    /**
536
     * {@inheritDoc}
537
     * @see \WsdlToPhp\PackageGenerator\Model\AbstractModel::toJsonSerialize()
538
     */
539 6
    protected function toJsonSerialize()
540
    {
541
        return [
542 6
            'attributes' => $this->attributes,
543 6
            'restriction' => $this->isRestriction,
544 6
            'struct' => $this->isStruct,
545 6
            'types' => $this->types,
546 6
            'values' => $this->values,
547 6
            'list' => $this->list,
548 2
        ];
549
    }
550
    /**
551
     * @param array $attributes
552
     */
553 582
    public function setAttributesFromSerializedJson(array $attributes)
554
    {
555 582
        foreach ($attributes as $attribute) {
556 576
            $this->attributes->add(self::instanceFromSerializedJson($this->generator, $attribute)->setOwner($this));
557 194
        }
558 582
    }
559
    /**
560
     * @param array $values
561
     */
562 582
    public function setValuesFromSerializedJson(array $values)
563
    {
564 582
        foreach ($values as $value) {
565 546
            $this->values->add(self::instanceFromSerializedJson($this->generator, $value)->setOwner($this));
566 194
        }
567 582
    }
568
}
569