Passed
Push — feature/issue-217 ( 9d7f83 )
by Mikaël
23:14
created

Struct::putRequiredAttributesFirst()   A

Complexity

Conditions 5
Paths 12

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 5
eloc 14
c 1
b 0
f 1
nc 12
nop 1
dl 0
loc 20
ccs 18
cts 18
cp 1
crap 5
rs 9.4888
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 1242
    public function __construct(Generator $generator, $name, $isStruct = true, $isRestriction = false)
74
    {
75 1242
        parent::__construct($generator, $name);
76 618
        $this
77 1242
            ->setStruct($isStruct)
78 1242
            ->setRestriction($isRestriction)
79 1242
            ->setAttributes(new StructAttributeContainer($generator))
80 1242
            ->setValues(new StructValueContainer($generator))
81 1242
            ->setTypes([]);
82 1242
    }
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 633
    public function getContextualPart()
90
    {
91 633
        $part = $this->getGenerator()->getOptionStructsFolder();
92 633
        if ($this->isRestriction()) {
93 216
            $part = $this->getGenerator()->getOptionEnumsFolder();
94 603
        } elseif ($this->isArray()) {
95 90
            $part = $this->getGenerator()->getOptionArraysFolder();
96 45
        }
97 633
        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 363
    public function getDocSubPackages()
107
    {
108 363
        $package = self::DOC_SUB_PACKAGE_STRUCTS;
109 363
        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 24
        }
114
        return [
115 363
            $package,
116 180
        ];
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 687
    public function isArray()
125
    {
126
        return
127
        (
128
            (
129
                (
130 687
                    ($this->isStruct() && $this->countAllAttributes() === 1) ||
131 663
                    (!$this->isStruct() && $this->countOwnAttributes() <= 1)
132 342
                ) &&
133 408
                mb_stripos($this->getName(), 'array') !== false
134 204
            ) ||
135 672
            (!$this->isStruct() && $this->getMetaValueFirstSet(['arraytype', 'arrayType'], false) !== false)
136 342
        );
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 786
    public function getAttributes($includeInheritanceAttributes = false, $requiredFirst = false)
148
    {
149 786
        if (!$includeInheritanceAttributes && !$requiredFirst) {
150 534
            $attributes = $this->attributes;
151 264
        } else {
152 660
            $attributes = $this->getAllAttributes($includeInheritanceAttributes, $requiredFirst);
153
        }
154 786
        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 366
    public function getProperAttributes($requiredFirst = false)
164
    {
165 366
        $properAttributes = new StructAttributeContainer($this->getGenerator());
166 366
        $parentAttributes = new StructAttributeContainer($this->getGenerator());
167
168 366
        if ($this->getInheritance() != '' && ($model = $this->getInheritanceStruct()) instanceof Struct) {
169 54
            while ($model instanceof Struct && $model->isStruct()) {
170 48
                foreach ($model->getAttributes() as $attribute) {
171 42
                    $parentAttributes->add($attribute);
172 21
                }
173 48
                $model = $model->getInheritanceStruct();
174 21
            }
175 24
        }
176
177
        /** @var StructAttribute $attribute */
178 366
        foreach ($this->getAttributes() as $attribute) {
179 312
            if ($parentAttributes->getStructAttributeByName($attribute->getName())) {
180 6
                continue;
181
            }
182 306
            $properAttributes->add($attribute);
183 180
        }
184
185 366
        return $requiredFirst ? $this->putRequiredAttributesFirst($properAttributes) : $properAttributes;
186
    }
187
    /**
188
     * @param bool $includeInheritanceAttributes
189
     * @param bool $requiredFirst
190
     * @return StructAttributeContainer
191
     */
192 660
    protected function getAllAttributes($includeInheritanceAttributes, $requiredFirst)
193
    {
194 660
        $allAttributes = new StructAttributeContainer($this->getGenerator());
195 660
        if ($includeInheritanceAttributes) {
196 660
            $this->addInheritanceAttributes($allAttributes);
197 327
        }
198 660
        foreach ($this->attributes as $attribute) {
199 594
            $allAttributes->add($attribute);
200 327
        }
201 660
        if ($requiredFirst) {
202 6
            $attributes = $this->putRequiredAttributesFirst($allAttributes);
203 3
        } else {
204 660
            $attributes = $allAttributes;
205
        }
206 660
        return $attributes;
207
    }
208
    /**
209
     * @param StructAttributeContainer $attributes
210
     */
211 660
    protected function addInheritanceAttributes(StructAttributeContainer $attributes)
212
    {
213 660
        if ($this->getInheritance() != '' && ($model = $this->getInheritanceStruct()) instanceof Struct) {
214 132
            while ($model instanceof Struct && $model->isStruct()) {
215 108
                foreach ($model->getAttributes() as $attribute) {
216 102
                    $attributes->add($attribute);
217 51
                }
218 108
                $model = $model->getInheritanceStruct();
219 51
            }
220 63
        }
221 660
    }
222
    /**
223
     * @param StructAttributeContainer $allAttributes
224
     * @return StructAttributeContainer
225
     */
226 351
    protected function putRequiredAttributesFirst(StructAttributeContainer $allAttributes)
227
    {
228 351
        $attributes = new StructAttributeContainer($this->getGenerator());
229 351
        $requiredAttributes = new StructAttributeContainer($this->getGenerator());
230 351
        $notRequiredAttributes = new StructAttributeContainer($this->getGenerator());
231 351
        foreach ($allAttributes as $attribute) {
232 294
            if ($attribute->isRequired()) {
233 90
                $requiredAttributes->add($attribute);
234 45
            } else {
235 278
                $notRequiredAttributes->add($attribute);
236
            }
237 174
        }
238 351
        foreach ($requiredAttributes as $attribute) {
239 90
            $attributes->add($attribute);
240 174
        }
241 351
        foreach ($notRequiredAttributes as $attribute) {
242 270
            $attributes->add($attribute);
243 174
        }
244 351
        unset($requiredAttributes, $notRequiredAttributes);
245 351
        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 651
    public function countAllAttributes()
262
    {
263 651
        return $this->getAttributes(true)->count();
264
    }
265
    /**
266
     * Sets the attributes of the struct
267
     * @param StructAttributeContainer $structAttributeContainer
268
     * @return Struct
269
     */
270 1242
    public function setAttributes(StructAttributeContainer $structAttributeContainer)
271
    {
272 1242
        $this->attributes = $structAttributeContainer;
273 1242
        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 675
    public function isRestriction()
318
    {
319 675
        return $this->isRestriction;
320
    }
321
    /**
322
     * Sets the isRestriction value
323
     * @param bool $isRestriction
324
     * @return Struct
325
     */
326 1242
    public function setRestriction($isRestriction = true)
327
    {
328 1242
        $this->isRestriction = $isRestriction;
329 1242
        return $this;
330
    }
331
    /**
332
     * Returns the isStruct value
333
     * @return bool
334
     */
335 762
    public function isStruct()
336
    {
337 762
        return $this->isStruct;
338
    }
339
    /**
340
     * Sets the isStruct value
341
     * @param bool $isStruct
342
     * @return Struct
343
     */
344 1242
    public function setStruct($isStruct = true)
345
    {
346 1242
        $this->isStruct = $isStruct;
347 1242
        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 594
    public function setList($list = '')
372
    {
373 594
        $this->list = $list;
374 594
        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 1242
    protected function setValues(StructValueContainer $structValueContainer)
390
    {
391 1242
        $this->values = $structValueContainer;
392 1242
        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 351
    public function getExtends($short = false)
440
    {
441 351
        $extends = '';
442 351
        if ($this->isArray()) {
443 48
            $extends = $this->getGenerator()->getOptionStructArrayClass();
444 336
        } elseif (!$this->isRestriction()) {
445 273
            $extends = $this->getGenerator()->getOptionStructClass();
446 135
        }
447 351
        return $short ? Utils::removeNamespace($extends) : $extends;
448
    }
449
    /**
450
     * @return Struct|null
451
     */
452 504
    public function getInheritanceStruct()
453
    {
454 504
        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 465
    public function getMeta()
494
    {
495 465
        $inheritanceStruct = $this->getInheritanceStruct();
496 465
        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 1242
    public function setTypes(array $types)
529
    {
530 1242
        $this->types = $types;
531 1242
        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 570
    public function setAttributesFromSerializedJson(array $attributes)
552
    {
553 570
        foreach ($attributes as $attribute) {
554 564
            $this->attributes->add(self::instanceFromSerializedJson($this->generator, $attribute)->setOwner($this));
555 282
        }
556 570
    }
557
    /**
558
     * @param array $values
559
     */
560 570
    public function setValuesFromSerializedJson(array $values)
561
    {
562 570
        foreach ($values as $value) {
563 534
            $this->values->add(self::instanceFromSerializedJson($this->generator, $value)->setOwner($this));
564 282
        }
565 570
    }
566
}
567