Passed
Push — develop ( 39dbcd...b263cf )
by Mikaël
24:14
created

Struct::addInheritanceAttributes()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 6
eloc 5
c 1
b 0
f 1
nc 4
nop 1
dl 0
loc 8
ccs 7
cts 7
cp 1
crap 6
rs 9.2222
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 1246
    public function __construct(Generator $generator, $name, $isStruct = true, $isRestriction = false)
74
    {
75 1246
        parent::__construct($generator, $name);
76 416
        $this
77 1246
            ->setStruct($isStruct)
78 1246
            ->setRestriction($isRestriction)
79 1246
            ->setAttributes(new StructAttributeContainer($generator))
80 1246
            ->setValues(new StructValueContainer($generator))
81 1246
            ->setTypes([]);
82 1246
    }
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 596
        } elseif ($this->isArray()) {
95 90
            $part = $this->getGenerator()->getOptionArraysFolder();
96 30
        }
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 330
        } elseif ($this->isArray()) {
112 48
            $package = self::DOC_SUB_PACKAGE_ARRAYS;
113 16
        }
114
        return [
115 366
            $package,
116 122
        ];
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 674
                    (!$this->isStruct() && $this->countOwnAttributes() <= 1)
132 230
                ) &&
133 408
                mb_stripos($this->getName(), 'array') !== false
134 136
            ) ||
135 680
            (!$this->isStruct() && $this->getMetaValueFirstSet(['arraytype', 'arrayType'], false) !== false)
136 230
        );
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 798
    public function getAttributes($includeInheritanceAttributes = false, $requiredFirst = false)
148
    {
149 798
        if (!$includeInheritanceAttributes && !$requiredFirst) {
150 546
            $attributes = $this->attributes;
151 182
        } else {
152 666
            $attributes = $this->getAllAttributes($includeInheritanceAttributes, $requiredFirst);
153
        }
154 798
        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 18
                }
173 54
                $model = $model->getInheritanceStruct();
174 18
            }
175 20
        }
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 124
        }
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 222
        }
198 666
        foreach ($this->attributes as $attribute) {
199 600
            $allAttributes->add($attribute);
200 222
        }
201 666
        if ($requiredFirst) {
202 6
            $attributes = $this->putRequiredAttributesFirst($allAttributes);
203 2
        } 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 38
                }
218 114
                $model = $model->getInheritanceStruct();
219 38
            }
220 46
        }
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 30
            } else {
235 278
                $notRequiredAttributes->add($attribute);
236
            }
237 118
        }
238 354
        foreach ($requiredAttributes as $attribute) {
239 90
            $attributes->add($attribute);
240 118
        }
241 354
        foreach ($notRequiredAttributes as $attribute) {
242 270
            $attributes->add($attribute);
243 118
        }
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 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 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 1246
    public function setAttributes(StructAttributeContainer $structAttributeContainer)
271
    {
272 1246
        $this->attributes = $structAttributeContainer;
273 1246
        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 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 1246
    public function setRestriction($isRestriction = true)
327
    {
328 1246
        $this->isRestriction = $isRestriction;
329 1246
        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 1246
    public function setStruct($isStruct = true)
345
    {
346 1246
        $this->isStruct = $isStruct;
347 1246
        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 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 1246
    protected function setValues(StructValueContainer $structValueContainer)
390
    {
391 1246
        $this->values = $structValueContainer;
392 1246
        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 354
    public function getExtends($short = false)
440
    {
441 354
        $extends = '';
442 354
        if ($this->isArray()) {
443 48
            $extends = $this->getGenerator()->getOptionStructArrayClass();
444 334
        } elseif ($this->isRestriction()) {
445 126
            $extends = $this->getGenerator()->getOptionStructEnumClass();
446 42
        } else {
447 276
            $extends = $this->getGenerator()->getOptionStructClass();
448
        }
449 354
        return $short ? Utils::removeNamespace($extends) : $extends;
450
    }
451
    /**
452
     * @return Struct|null
453
     */
454 510
    public function getInheritanceStruct()
455
    {
456 510
        return $this->getName() === $this->getInheritance() ? null : $this->getGenerator()->getStructByName(str_replace('[]', '', $this->getInheritance()));
457
    }
458
    /**
459
     * @return string
460
     */
461 330
    public function getTopInheritance()
462
    {
463 330
        $inheritance = $this->getInheritance();
464 330
        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 330
        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 468
    public function getMeta()
496
    {
497 468
        $inheritanceStruct = $this->getInheritanceStruct();
498 468
        return $this->mergeMeta(($inheritanceStruct && !$inheritanceStruct->isStruct()) ? $inheritanceStruct->getMeta() : [], parent::getMeta());
499
    }
500
    /**
501
     * @param $filename
502
     * @return StructReservedMethod|StructArrayReservedMethod
503
     */
504 318
    public function getReservedMethodsInstance($filename = null)
505
    {
506 318
        $instance = StructReservedMethod::instance($filename);
507 318
        if ($this->isArray()) {
508 54
            $instance = StructArrayReservedMethod::instance($filename);
509 18
        }
510 318
        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 294
    public function isUnion()
523
    {
524 294
        return count($this->types) > 0;
525
    }
526
    /**
527
     * @param string[] $types
528
     * @return Struct
529
     */
530 1246
    public function setTypes(array $types)
531
    {
532 1246
        $this->types = $types;
533 1246
        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 576
    public function setAttributesFromSerializedJson(array $attributes)
554
    {
555 576
        foreach ($attributes as $attribute) {
556 570
            $this->attributes->add(self::instanceFromSerializedJson($this->generator, $attribute)->setOwner($this));
557 192
        }
558 576
    }
559
    /**
560
     * @param array $values
561
     */
562 576
    public function setValuesFromSerializedJson(array $values)
563
    {
564 576
        foreach ($values as $value) {
565 540
            $this->values->add(self::instanceFromSerializedJson($this->generator, $value)->setOwner($this));
566 192
        }
567 576
    }
568
}
569