Passed
Push — static-analysis ( 89afd5...c1a154 )
by SignpostMarv
02:18
created

maybeLoadExtensionFromBaseComplexType()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 2
crap 2
1
<?php
2
3
namespace GoetasWebservices\XML\XSDReader;
4
5
use Closure;
6
use DOMDocument;
7
use DOMElement;
8
use DOMNode;
9
use GoetasWebservices\XML\XSDReader\Exception\IOException;
10
use GoetasWebservices\XML\XSDReader\Exception\TypeException;
11
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Attribute;
12
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeContainer;
13
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeDef;
14
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
15
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup;
16
use GoetasWebservices\XML\XSDReader\Schema\Element\Element;
17
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer;
18
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
19
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem;
20
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef;
21
use GoetasWebservices\XML\XSDReader\Schema\Element\Group;
22
use GoetasWebservices\XML\XSDReader\Schema\Element\InterfaceSetMinMax;
23
use GoetasWebservices\XML\XSDReader\Schema\Element\GroupRef;
24
use GoetasWebservices\XML\XSDReader\Schema\Exception\TypeNotFoundException;
25
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Base;
26
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Extension;
27
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Restriction;
28
use GoetasWebservices\XML\XSDReader\Schema\Item;
29
use GoetasWebservices\XML\XSDReader\Schema\Schema;
30
use GoetasWebservices\XML\XSDReader\Schema\SchemaItem;
31
use GoetasWebservices\XML\XSDReader\Schema\Type\BaseComplexType;
32
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
33
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexTypeSimpleContent;
34
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType;
35
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
36
use GoetasWebservices\XML\XSDReader\Utils\UrlUtils;
37
use RuntimeException;
38
39
class SchemaReader
40
{
41
    /**
42
     * @param string $typeName
43
     *
44
     * @return mixed[]
45
     */
46 45
    private static function splitParts(DOMElement $node, $typeName)
47
    {
48 45
        $prefix = null;
49 45
        $name = $typeName;
50 45
        if (strpos($typeName, ':') !== false) {
51 45
            list($prefix, $name) = explode(':', $typeName);
52 45
        }
53
54 45
        $namespace = $node->lookupNamespaceUri($prefix ?: '');
55
56
        return array(
57 45
            $name,
58 45
            $namespace,
59 45
            $prefix,
60 45
        );
61
    }
62
63
    /**
64
     * @param bool $attributeDef
65
     *
66
     * @return Closure
67
     */
68 45
    private function loadAttributeOrElementDef(
69
        Schema $schema,
70
        DOMElement $node,
71
        $attributeDef
72
    ) {
73 45
        $name = $node->getAttribute('name');
74 45
        if ($attributeDef) {
75 45
            $attribute = new AttributeDef($schema, $name);
76 45
            $schema->addAttribute($attribute);
77 45
        } else {
78 45
            $attribute = new ElementDef($schema, $name);
79 45
            $schema->addElement($attribute);
80
        }
81
82
        return function () use ($attribute, $node) {
83 45
            $this->fillItem($attribute, $node);
84 45
        };
85
    }
86
87
    /**
88
     * @return Closure
89
     */
90 45
    private function loadAttributeDef(Schema $schema, DOMElement $node)
91
    {
92 45
        return $this->loadAttributeOrElementDef($schema, $node, true);
93
    }
94
95
    /**
96
     * @param int|null $max
97
     *
98
     * @return int|null
99
     */
100 45
    private static function loadSequenceNormaliseMax(DOMElement $node, $max)
101
    {
102
        return
103
        (
104 45
            (is_int($max) && (bool) $max) ||
105 45
            $node->getAttribute('maxOccurs') == 'unbounded' ||
106 45
            $node->getAttribute('maxOccurs') > 1
107 45
        )
108 45
            ? 2
109 45
            : null;
110
    }
111
112
    /**
113
     * @param int|null $max
114
     */
115 45
    private function loadSequence(ElementContainer $elementContainer, DOMElement $node, $max = null)
116
    {
117 45
        $max = static::loadSequenceNormaliseMax($node, $max);
118
119 45
        static::againstDOMNodeList(
120 45
            $node,
121
            function (
122
                DOMElement $node,
123
                DOMElement $childNode
124
            ) use (
125 45
                $elementContainer,
126 45
                $max
127
            ) {
128 45
                $this->loadSequenceChildNode(
129 45
                    $elementContainer,
130 45
                    $node,
131 45
                    $childNode,
132
                    $max
133 45
                );
134 45
            }
135 45
        );
136 45
    }
137
138
    /**
139
     * @param int|null $max
140
     */
141 45 View Code Duplication
    private function loadSequenceChildNode(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
142
        ElementContainer $elementContainer,
143
        DOMElement $node,
144
        DOMElement $childNode,
145
        $max
146
    ) {
147 45
        switch ($childNode->localName) {
148 45
            case 'sequence':
149 45
            case 'choice':
150 45
            case 'all':
151 45
                $this->loadSequence(
152 45
                    $elementContainer,
153 45
                    $childNode,
154
                    $max
155 45
                );
156 45
                break;
157 45
            case 'element':
158 45
                $this->loadSequenceChildNodeLoadElement(
159 45
                    $elementContainer,
160 45
                    $node,
161 45
                    $childNode,
162
                    $max
163 45
                );
164 45
                break;
165 45
            case 'group':
166 45
                $this->addGroupAsElement(
167 45
                    $elementContainer->getSchema(),
168 45
                    $node,
169 45
                    $childNode,
170
                    $elementContainer
171 45
                );
172 45
                break;
173 45
        }
174 45
    }
175
176
    /**
177
     * @param int|null $max
178
     */
179 45
    private function loadSequenceChildNodeLoadElement(
180
        ElementContainer $elementContainer,
181
        DOMElement $node,
182
        DOMElement $childNode,
183
        $max
184
    ) {
185 45
        if ($childNode->hasAttribute('ref')) {
186
            /**
187
             * @var ElementDef $referencedElement
188
             */
189 45
            $referencedElement = $this->findSomething('findElement', $elementContainer->getSchema(), $node, $childNode->getAttribute('ref'));
190 45
            $element = static::loadElementRef(
191 45
                $referencedElement,
192
                $childNode
193 45
            );
194 45
        } else {
195 45
            $element = $this->loadElement(
196 45
                $elementContainer->getSchema(),
197
                $childNode
198 45
            );
199
        }
200 45
        if ($max > 1) {
201
            /*
202
            * although one might think the typecast is not needed with $max being `? int $max` after passing > 1,
203
            * phpstan@a4f89fa still thinks it's possibly null.
204
            * see https://github.com/phpstan/phpstan/issues/577 for related issue
205
            */
206 45
            $element->setMax((int) $max);
207 45
        }
208 45
        $elementContainer->addElement($element);
209 45
    }
210
211 45
    private function addGroupAsElement(
212
        Schema $schema,
213
        DOMElement $node,
214
        DOMElement $childNode,
215
        ElementContainer $elementContainer
216
    ) {
217
        /**
218
         * @var Group
219
         */
220 45
        $referencedGroup = $this->findSomething(
221 45
            'findGroup',
222 45
            $schema,
223 45
            $node,
224 45
            $childNode->getAttribute('ref')
225 45
        );
226
227 45
        $group = $this->loadGroupRef($referencedGroup, $childNode);
228 45
        $elementContainer->addElement($group);
229 45
    }
230
231
    /**
232
     * @return Closure
233
     */
234 45
    private function loadGroup(Schema $schema, DOMElement $node)
235
    {
236 45
        $group = static::loadGroupBeforeCheckingChildNodes(
237 45
            $schema,
238
            $node
239 45
        );
240
        static $methods = [
241
            'sequence' => 'loadSequence',
242
            'choice' => 'loadSequence',
243
            'all' => 'loadSequence',
244 45
        ];
245
246
        return function () use ($group, $node, $methods) {
247
            /**
248
             * @var string[]
249
             */
250 45
            $methods = $methods;
251 45
            static::againstDOMNodeList(
252 45
                $node,
253 45
                $this->CallbackGeneratorMaybeCallMethodAgainstDOMNodeList(
254 45
                    $group,
255
                    $methods
256 45
                )
257 45
            );
258 45
        };
259
    }
260
261
    /**
262
     * @return Group|GroupRef
263
     */
264 45
    private static function loadGroupBeforeCheckingChildNodes(
265
        Schema $schema,
266
        DOMElement $node
267
    ) {
268 45
        $group = new Group($schema, $node->getAttribute('name'));
269 45
        $group->setDoc(self::getDocumentation($node));
270
271 45
        if ($node->hasAttribute('maxOccurs')) {
272
            /**
273
             * @var GroupRef
274
             */
275
            $group = self::maybeSetMax(new GroupRef($group), $node);
276
        }
277 45
        if ($node->hasAttribute('minOccurs')) {
278
            /**
279
             * @var GroupRef
280
             */
281
            $group = self::maybeSetMin(
282
                $group instanceof GroupRef ? $group : new GroupRef($group),
283
                $node
284
            );
285
        }
286
287 45
        $schema->addGroup($group);
288
289 45
        return $group;
290
    }
291
292
    /**
293
     * @return GroupRef
294
     */
295 45
    private function loadGroupRef(Group $referenced, DOMElement $node)
296
    {
297 45
        $ref = new GroupRef($referenced);
298 45
        $ref->setDoc(self::getDocumentation($node));
299
300 45
        self::maybeSetMax($ref, $node);
301 45
        self::maybeSetMin($ref, $node);
302
303 45
        return $ref;
304
    }
305
306
    /**
307
     * @return BaseComplexType
308
     */
309 45
    private function loadComplexTypeBeforeCallbackCallback(
310
        Schema $schema,
311
        DOMElement $node
312
    ) {
313
        /**
314
         * @var bool
315
         */
316 45
        $isSimple = false;
317
318 45
        static::againstDOMNodeList(
319 45
            $node,
320
            function (
321
                DOMElement $node,
322
                DOMElement $childNode
323
            ) use (
324
                &$isSimple
325
            ) {
326 45
                if ($isSimple) {
327 1
                    return;
328
                }
329 45
                if ($childNode->localName === 'simpleContent') {
330 2
                    $isSimple = true;
331 2
                }
332 45
            }
333 45
        );
334
335 45
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute('name')) : new ComplexType($schema, $node->getAttribute('name'));
336
337 45
        $type->setDoc(static::getDocumentation($node));
338 45
        if ($node->getAttribute('name')) {
339 45
            $schema->addType($type);
340 45
        }
341
342 45
        return $type;
343
    }
344
345
    /**
346
     * @param Closure|null $callback
347
     *
348
     * @return Closure
349
     */
350 45
    private function loadComplexType(Schema $schema, DOMElement $node, $callback = null)
351
    {
352 45
        $type = $this->loadComplexTypeBeforeCallbackCallback($schema, $node);
353
354 45
        return $this->makeCallbackCallback(
355 45
            $type,
356 45
            $node,
357
            function (
358
                DOMElement $node,
359
                DOMElement $childNode
360
            ) use (
361 45
                $schema,
362 45
                $type
363
            ) {
364 45
                $this->loadComplexTypeFromChildNode(
365 45
                    $type,
366 45
                    $node,
367 45
                    $childNode,
368
                    $schema
369 45
                );
370 45
            },
371
            $callback
372 45
        );
373
    }
374
375 45
    private function loadComplexTypeFromChildNode(
376
        BaseComplexType $type,
377
        DOMElement $node,
378
        DOMElement $childNode,
379
        Schema $schema
380
    ) {
381 45
        switch ($childNode->localName) {
382 45
            case 'sequence':
383 45
            case 'choice':
384 45
            case 'all':
385 45
                if ($type instanceof ElementContainer) {
386 45
                    $this->loadSequence(
387 45
                        $type,
388
                        $childNode
389 45
                    );
390 45
                }
391 45
                break;
392 45
            case 'attribute':
393 45
                $this->addAttributeFromAttributeOrRef(
394 45
                    $type,
395 45
                    $childNode,
396 45
                    $schema,
397
                    $node
398 45
                );
399 45
                break;
400 45
            case 'attributeGroup':
401 2
                $this->findSomethingLikeAttributeGroup(
402 2
                    $schema,
403 2
                    $node,
404 2
                    $childNode,
405
                    $type
406 2
                );
407 2
                break;
408 45
            case 'group':
409
                if (
410
                    $type instanceof ComplexType
411 1
                ) {
412 1
                    $this->addGroupAsElement(
413 1
                        $schema,
414 1
                        $node,
415 1
                        $childNode,
416
                        $type
417 1
                    );
418 1
                }
419 1
                break;
420 45
        }
421 45
    }
422
423
    /**
424
     * @param Closure|null $callback
425
     *
426
     * @return Closure
427
     */
428 45
    private function loadSimpleType(Schema $schema, DOMElement $node, $callback = null)
429
    {
430 45
        $type = new SimpleType($schema, $node->getAttribute('name'));
431 45
        $type->setDoc(static::getDocumentation($node));
432 45
        if ($node->getAttribute('name')) {
433 45
            $schema->addType($type);
434 45
        }
435
436 45
        return $this->makeCallbackCallback(
437 45
            $type,
438 45
            $node,
439 45
            $this->CallbackGeneratorMaybeCallMethodAgainstDOMNodeList(
440 45
                $type,
441
                [
442 45
                    'union' => 'loadUnion',
443 45
                    'list' => 'loadList',
444
                ]
445 45
            ),
446
            $callback
447 45
        );
448
    }
449
450 45
    private function loadList(SimpleType $type, DOMElement $node)
451
    {
452 45
        if ($node->hasAttribute('itemType')) {
453
            /**
454
             * @var SimpleType
455
             */
456 45
            $listType = $this->findSomeType($type, $node, 'itemType');
457 45
            $type->setList($listType);
458 45
        } else {
459
            $addCallback = function (SimpleType $list) use ($type) {
460 45
                $type->setList($list);
461 45
            };
462
463 45
            $this->loadTypeWithCallbackOnChildNodes(
464 45
                $type->getSchema(),
465 45
                $node,
466
                $addCallback
467 45
            );
468
        }
469 45
    }
470
471 45
    private function loadUnion(SimpleType $type, DOMElement $node)
472
    {
473 45
        if ($node->hasAttribute('memberTypes')) {
474 45
            $types = preg_split('/\s+/', $node->getAttribute('memberTypes'));
475 45
            foreach ($types as $typeName) {
476
                /**
477
                 * @var SimpleType
478
                 */
479 45
                $unionType = $this->findSomeTypeFromAttribute(
480 45
                    $type,
481 45
                    $node,
482
                    $typeName
483 45
                );
484 45
                $type->addUnion($unionType);
485 45
            }
486 45
        }
487
        $addCallback = function (SimpleType $unType) use ($type) {
488 45
            $type->addUnion($unType);
489 45
        };
490
491 45
        $this->loadTypeWithCallbackOnChildNodes(
492 45
            $type->getSchema(),
493 45
            $node,
494
            $addCallback
495 45
        );
496 45
    }
497
498 45
    private function loadExtensionChildNodes(
499
        BaseComplexType $type,
500
        DOMElement $node
501
    ) {
502 45
        static::againstDOMNodeList(
503 45
            $node,
504 View Code Duplication
            function (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
505
                DOMElement $node,
506
                DOMElement $childNode
507
            ) use (
508 45
                $type
509
            ) {
510 45
                switch ($childNode->localName) {
511 45
                    case 'sequence':
512 45
                    case 'choice':
513 45
                    case 'all':
514 45
                        if ($type instanceof ElementContainer) {
515 45
                            $this->loadSequence(
516 45
                                $type,
517
                                $childNode
518 45
                            );
519 45
                        }
520 45
                        break;
521 45
                    case 'attribute':
522 45
                        $this->addAttributeFromAttributeOrRef(
523 45
                            $type,
524 45
                            $childNode,
525 45
                            $type->getSchema(),
526
                            $node
527 45
                        );
528 45
                        break;
529 45
                    case 'attributeGroup':
530 45
                        $this->findSomethingLikeAttributeGroup(
531 45
                            $type->getSchema(),
532 45
                            $node,
533 45
                            $childNode,
534
                            $type
535 45
                        );
536 45
                        break;
537 45
                }
538 45
            }
539 45
        );
540 45
    }
541
542 45
    private function loadExtension(BaseComplexType $type, DOMElement $node)
543
    {
544 45
        $extension = new Extension();
545 45
        $type->setExtension($extension);
546
547 45
        if ($node->hasAttribute('base')) {
548 45
            $this->findAndSetSomeBase(
549 45
                $type,
550 45
                $extension,
551
                $node
552 45
            );
553 45
        }
554 45
        $this->loadExtensionChildNodes($type, $node);
555 45
    }
556
557 45
    private function loadRestriction(Type $type, DOMElement $node)
558
    {
559 45
        $restriction = new Restriction();
560 45
        $type->setRestriction($restriction);
561 45
        if ($node->hasAttribute('base')) {
562 45
            $this->findAndSetSomeBase($type, $restriction, $node);
563 45
        } else {
564
            $addCallback = function (Type $restType) use ($restriction) {
565 45
                $restriction->setBase($restType);
566 45
            };
567
568 45
            $this->loadTypeWithCallbackOnChildNodes(
569 45
                $type->getSchema(),
570 45
                $node,
571
                $addCallback
572 45
            );
573
        }
574 45
        self::againstDOMNodeList(
575 45
            $node,
576
            function (
577
                DOMElement $node,
578
                DOMElement $childNode
579
            ) use (
580 45
                $restriction
581
            ) {
582
                if (
583 45
                    in_array(
584 45
                        $childNode->localName,
585
                        [
586 45
                            'enumeration',
587 45
                            'pattern',
588 45
                            'length',
589 45
                            'minLength',
590 45
                            'maxLength',
591 45
                            'minInclusive',
592 45
                            'maxInclusive',
593 45
                            'minExclusive',
594 45
                            'maxExclusive',
595 45
                            'fractionDigits',
596 45
                            'totalDigits',
597 45
                            'whiteSpace',
598 45
                        ],
599
                        true
600 45
                    )
601 45
                ) {
602 45
                    $restriction->addCheck(
603 45
                        $childNode->localName,
604
                        [
605 45
                            'value' => $childNode->getAttribute('value'),
606 45
                            'doc' => self::getDocumentation($childNode),
607
                        ]
608 45
                    );
609 45
                }
610 45
            }
611 45
        );
612 45
    }
613
614
    /**
615
     * @return Closure
616
     */
617 45
    private function loadElementDef(Schema $schema, DOMElement $node)
618
    {
619 45
        return $this->loadAttributeOrElementDef($schema, $node, false);
620
    }
621
622
    /**
623
     * @param bool $checkAbstract
624
     */
625 45
    private function fillTypeNode(Type $type, DOMElement $node, $checkAbstract = false)
626
    {
627 45
        if ($checkAbstract) {
628 45
            $type->setAbstract($node->getAttribute('abstract') === 'true' || $node->getAttribute('abstract') === '1');
629 45
        }
630
        static $methods = [
631
            'restriction' => 'loadRestriction',
632
            'extension' => 'maybeLoadExtensionFromBaseComplexType',
633
            'simpleContent' => 'fillTypeNode',
634
            'complexContent' => 'fillTypeNode',
635 45
        ];
636
637
        /**
638
         * @var string[]
639
         */
640 45
        $methods = $methods;
641
642 45
        static::againstDOMNodeList(
643 45
            $node,
644 45
            $this->CallbackGeneratorMaybeCallMethodAgainstDOMNodeList(
645 45
                $type,
646
                $methods
647 45
            )
648 45
        );
649 45
    }
650
651 45
    private function fillItemNonLocalType(Item $element, DOMElement $node)
652
    {
653 45
        if ($node->getAttribute('type')) {
654
            /**
655
             * @var Type
656
             */
657 45
            $type = $this->findSomeType($element, $node, 'type');
658 45
        } else {
659
            /**
660
             * @var Type
661
             */
662 45
            $type = $this->findSomeTypeFromAttribute(
663 45
                $element,
664 45
                $node,
665 45
                ($node->lookupPrefix(self::XSD_NS).':anyType')
666 45
            );
667
        }
668
669 45
        $element->setType($type);
670 45
    }
671
672
    /**
673
     * @param string $attributeName
674
     *
675
     * @return SchemaItem
676
     */
677 45
    private function findSomeType(
678
        SchemaItem $fromThis,
679
        DOMElement $node,
680
        $attributeName
681
    ) {
682 45
        return $this->findSomeTypeFromAttribute(
683 45
            $fromThis,
684 45
            $node,
685 45
            $node->getAttribute($attributeName)
686 45
        );
687
    }
688
689
    /**
690
     * @param string $attributeName
691
     *
692
     * @return SchemaItem
693
     */
694 45
    private function findSomeTypeFromAttribute(
695
        SchemaItem $fromThis,
696
        DOMElement $node,
697
        $attributeName
698
    ) {
699
        /**
700
         * @var SchemaItem
701
         */
702 45
        $out = $this->findSomething(
703 45
            'findType',
704 45
            $fromThis->getSchema(),
705 45
            $node,
706
            $attributeName
707 45
        );
708
709 45
        return $out;
710
    }
711
712
    /**
713
     * @param Closure|null $callback
714
     *
715
     * @return Closure
716
     */
717 45
    private function makeCallbackCallback(
718
        Type $type,
719
        DOMElement $node,
720
        Closure $callbackCallback,
721
        $callback = null
722
    ) {
723
        return function (
724
        ) use (
725 45
            $type,
726 45
            $node,
727 45
            $callbackCallback,
728 45
            $callback
729
        ) {
730 45
            $this->fillTypeNode($type, $node, true);
731
732 45
            static::againstDOMNodeList($node, $callbackCallback);
733
734 45
            if ($callback) {
735 45
                call_user_func($callback, $type);
736 45
            }
737 45
        };
738
    }
739
740 45
    private function maybeLoadExtensionFromBaseComplexType(
741
        Type $type,
742
        DOMElement $childNode
743
    ) {
744 45
        if ($type instanceof BaseComplexType) {
745 45
            $this->loadExtension($type, $childNode);
746 45
        }
747 45
    }
748
749
    const XSD_NS = 'http://www.w3.org/2001/XMLSchema';
750
751
    const XML_NS = 'http://www.w3.org/XML/1998/namespace';
752
753
    /**
754
     * @var string[]
755
     */
756
    protected $knownLocationSchemas = [
757
        'http://www.w3.org/2001/xml.xsd' => (
758
            __DIR__.'/Resources/xml.xsd'
759
        ),
760
        'http://www.w3.org/2001/XMLSchema.xsd' => (
761
            __DIR__.'/Resources/XMLSchema.xsd'
762
        ),
763
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' => (
764
            __DIR__.'/Resources/oasis-200401-wss-wssecurity-secext-1.0.xsd'
765
        ),
766
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' => (
767
            __DIR__.'/Resources/oasis-200401-wss-wssecurity-utility-1.0.xsd'
768
        ),
769
        'https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
770
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
771
        ),
772
        'http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
773
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
774
        ),
775
    ];
776
777
    /**
778
     * @var string[]
779
     */
780
    protected static $globalSchemaInfo = array(
781
        self::XML_NS => 'http://www.w3.org/2001/xml.xsd',
782
        self::XSD_NS => 'http://www.w3.org/2001/XMLSchema.xsd',
783
    );
784
785
    /**
786
     * @param string $remote
787
     * @param string $local
788
     */
789
    public function addKnownSchemaLocation($remote, $local)
790
    {
791
        $this->knownLocationSchemas[$remote] = $local;
792
    }
793
794
    /**
795
     * @param string $remote
796
     *
797
     * @return bool
798
     */
799 1
    private function hasKnownSchemaLocation($remote)
800
    {
801 1
        return isset($this->knownLocationSchemas[$remote]);
802
    }
803
804
    /**
805
     * @param string $remote
806
     *
807
     * @return string
808
     */
809
    private function getKnownSchemaLocation($remote)
810
    {
811
        return $this->knownLocationSchemas[$remote];
812
    }
813
814
    /**
815
     * @param DOMElement $node
816
     *
817
     * @return string
818
     */
819 45
    private static function getDocumentation(DOMElement $node)
820
    {
821 45
        $doc = '';
822 45
        static::againstDOMNodeList(
823 45
            $node,
824
            function (
825
                DOMElement $node,
826
                DOMElement $childNode
827
            ) use (
828
                &$doc
829
            ) {
830 45
                if ($childNode->localName == 'annotation') {
831 45
                    $doc .= static::getDocumentation($childNode);
832 45
                } elseif ($childNode->localName == 'documentation') {
833 45
                    $doc .= $childNode->nodeValue;
834 45
                }
835 45
            }
836 45
        );
837 45
        $doc = preg_replace('/[\t ]+/', ' ', $doc);
838
839 45
        return trim($doc);
840
    }
841
842
    /**
843
     * @param string[] $methods
844
     * @param string   $key
845
     *
846
     * @return Closure|null
847
     */
848 45
    private function maybeCallMethod(
849
        array $methods,
850
        $key,
851
        DOMNode $childNode,
852
        ...$args
853
    ) {
854 45
        if ($childNode instanceof DOMElement && isset($methods[$key])) {
855 45
            $method = $methods[$key];
856
857
            /**
858
             * @var Closure|null
859
             */
860 45
            $append = $this->$method(...$args);
861
862 45
            if ($append instanceof Closure) {
863 45
                return $append;
864
            }
865 45
        }
866 45
    }
867
868
    /**
869
     * @param Schema     $schema
870
     * @param DOMElement $node
871
     * @param Schema     $parent
872
     *
873
     * @return Closure[]
874
     */
875 45
    private function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null)
876
    {
877 45
        $this->setSchemaThingsFromNode($schema, $node, $parent);
878 45
        $functions = array();
879
880 45
        static::againstDOMNodeList(
881 45
            $node,
882
            function (
883
                DOMElement $node,
884
                DOMElement $childNode
885
            ) use (
886 45
                $schema,
887
                &$functions
888
            ) {
889 45
                $callback = null;
890
891 45
                switch ($childNode->localName) {
892 45
                    case 'attributeGroup':
893 45
                        $callback = $this->loadAttributeGroup($schema, $childNode);
894 45
                        break;
895 45
                    case 'include':
896 45
                    case 'import':
897 45
                        $callback = $this->loadImport($schema, $childNode);
898 45
                        break;
899 45
                    case 'element':
900 45
                        $callback = $this->loadElementDef($schema, $childNode);
901 45
                        break;
902 45
                    case 'attribute':
903 45
                        $callback = $this->loadAttributeDef($schema, $childNode);
904 45
                        break;
905 45
                    case 'group':
906 45
                        $callback = $this->loadGroup($schema, $childNode);
907 45
                        break;
908 45
                    case 'complexType':
909 45
                        $callback = $this->loadComplexType($schema, $childNode);
910 45
                        break;
911 45
                    case 'simpleType':
912 45
                        $callback = $this->loadSimpleType($schema, $childNode);
913 45
                        break;
914 45
                }
915
916 45
                if ($callback instanceof Closure) {
917 45
                    $functions[] = $callback;
918 45
                }
919 45
            }
920 45
        );
921
922 45
        return $functions;
923
    }
924
925
    /**
926
     * @return InterfaceSetMinMax
927
     */
928 45
    private static function maybeSetMax(InterfaceSetMinMax $ref, DOMElement $node)
929
    {
930
        if (
931 45
            $node->hasAttribute('maxOccurs')
932 45
        ) {
933 45
            $ref->setMax($node->getAttribute('maxOccurs') == 'unbounded' ? -1 : (int) $node->getAttribute('maxOccurs'));
934 45
        }
935
936 45
        return $ref;
937
    }
938
939
    /**
940
     * @return InterfaceSetMinMax
941
     */
942 45
    private static function maybeSetMin(InterfaceSetMinMax $ref, DOMElement $node)
943
    {
944 45
        if ($node->hasAttribute('minOccurs')) {
945 45
            $ref->setMin((int) $node->getAttribute('minOccurs'));
946 45
        }
947
948 45
        return $ref;
949
    }
950
951 45
    private function findAndSetSomeBase(
952
        Type $type,
953
        Base $setBaseOnThis,
954
        DOMElement $node
955
    ) {
956
        /**
957
         * @var Type
958
         */
959 45
        $parent = $this->findSomeType($type, $node, 'base');
960 45
        $setBaseOnThis->setBase($parent);
961 45
    }
962
963
    /**
964
     * @param string     $finder
965
     * @param Schema     $schema
966
     * @param DOMElement $node
967
     * @param string     $typeName
968
     *
969
     * @throws TypeException
970
     *
971
     * @return ElementItem|Group|AttributeItem|AttributeGroup|Type
972
     */
973 45
    private function findSomething($finder, Schema $schema, DOMElement $node, $typeName)
974
    {
975 45
        list($name, $namespace) = static::splitParts($node, $typeName);
976
977
        /**
978
         * @var string|null
979
         */
980 45
        $namespace = $namespace ?: $schema->getTargetNamespace();
981
982
        try {
983
            /**
984
             * @var ElementItem|Group|AttributeItem|AttributeGroup|Type
985
             */
986 45
            $out = $schema->$finder($name, $namespace);
987
988 45
            return $out;
989
        } catch (TypeNotFoundException $e) {
990
            throw new TypeException(sprintf("Can't find %s named {%s}#%s, at line %d in %s ", strtolower(substr($finder, 4)), $namespace, $name, $node->getLineNo(), $node->ownerDocument->documentURI), 0, $e);
991
        }
992
    }
993
994 45
    private function fillItem(Item $element, DOMElement $node)
995
    {
996
        /**
997
         * @var bool
998
         */
999 45
        $skip = false;
1000 45
        static::againstDOMNodeList(
1001 45
            $node,
1002
            function (
1003
                DOMElement $node,
1004
                DOMElement $childNode
1005
            ) use (
1006 45
                $element,
1007
                &$skip
1008
            ) {
1009
                if (
1010 45
                    !$skip &&
1011 45
                    in_array(
1012 45
                        $childNode->localName,
1013
                        [
1014 45
                            'complexType',
1015 45
                            'simpleType',
1016
                        ]
1017 45
                    )
1018 45
                ) {
1019 45
                    $this->loadTypeWithCallback(
1020 45
                        $element->getSchema(),
1021 45
                        $childNode,
1022
                        function (Type $type) use ($element) {
1023 45
                            $element->setType($type);
1024 45
                        }
1025 45
                    );
1026 45
                    $skip = true;
1027 45
                }
1028 45
            }
1029 45
        );
1030 45
        if ($skip) {
1031 45
            return;
1032
        }
1033 45
        $this->fillItemNonLocalType($element, $node);
1034 45
    }
1035
1036
    /**
1037
     * @var Schema|null
1038
     */
1039
    protected $globalSchema;
1040
1041
    /**
1042
     * @return Schema[]
1043
     */
1044 45
    private function setupGlobalSchemas(array &$callbacks)
1045
    {
1046 45
        $globalSchemas = array();
1047 45
        foreach (self::$globalSchemaInfo as $namespace => $uri) {
1048 45
            self::setLoadedFile(
1049 45
                $uri,
1050 45
                $globalSchemas[$namespace] = $schema = new Schema()
1051 45
            );
1052 45
            if ($namespace === self::XSD_NS) {
1053 45
                $this->globalSchema = $schema;
1054 45
            }
1055 45
            $xml = $this->getDOM($this->knownLocationSchemas[$uri]);
1056 45
            $callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement));
1057 45
        }
1058
1059 45
        return $globalSchemas;
1060
    }
1061
1062
    /**
1063
     * @return string[]
1064
     */
1065 45
    public function getGlobalSchemaInfo()
1066
    {
1067 45
        return self::$globalSchemaInfo;
1068
    }
1069
1070
    /**
1071
     * @return Schema
1072
     */
1073 45
    public function getGlobalSchema()
1074
    {
1075 45
        if (!$this->globalSchema) {
1076 45
            $callbacks = array();
1077 45
            $globalSchemas = $this->setupGlobalSchemas($callbacks);
1078
1079 45
            $globalSchemas[static::XSD_NS]->addType(new SimpleType($globalSchemas[static::XSD_NS], 'anySimpleType'));
1080 45
            $globalSchemas[static::XSD_NS]->addType(new SimpleType($globalSchemas[static::XSD_NS], 'anyType'));
1081
1082 45
            $globalSchemas[static::XML_NS]->addSchema(
1083 45
                $globalSchemas[static::XSD_NS],
1084
                (string) static::XSD_NS
1085 45
            );
1086 45
            $globalSchemas[static::XSD_NS]->addSchema(
1087 45
                $globalSchemas[static::XML_NS],
1088
                (string) static::XML_NS
1089 45
            );
1090
1091
            /**
1092
             * @var Closure
1093
             */
1094 45
            foreach ($callbacks as $callback) {
1095 45
                $callback();
1096 45
            }
1097 45
        }
1098
1099
        /**
1100
         * @var Schema
1101
         */
1102 45
        $out = $this->globalSchema;
1103
1104 45
        return $out;
1105
    }
1106
1107
    /**
1108
     * @param DOMElement $node
1109
     * @param string     $file
1110
     *
1111
     * @return Schema
1112
     */
1113 45
    private function readNode(DOMElement $node, $file = 'schema.xsd')
1114
    {
1115 45
        $fileKey = $node->hasAttribute('targetNamespace') ? $this->getNamespaceSpecificFileIndex($file, $node->getAttribute('targetNamespace')) : $file;
1116 45
        self::setLoadedFile($fileKey, $rootSchema = new Schema());
1117
1118 45
        $rootSchema->addSchema($this->getGlobalSchema());
1119 45
        $callbacks = $this->schemaNode($rootSchema, $node);
1120
1121 45
        foreach ($callbacks as $callback) {
1122 39
            call_user_func($callback);
1123 45
        }
1124
1125 45
        return $rootSchema;
1126
    }
1127
1128
    /**
1129
     * It is possible that a single file contains multiple <xsd:schema/> nodes, for instance in a WSDL file.
1130
     *
1131
     * Each of these  <xsd:schema/> nodes typically target a specific namespace. Append the target namespace to the
1132
     * file to distinguish between multiple schemas in a single file.
1133
     *
1134
     * @param string $file
1135
     * @param string $targetNamespace
1136
     *
1137
     * @return string
1138
     */
1139 45
    private function getNamespaceSpecificFileIndex($file, $targetNamespace)
1140
    {
1141 45
        return $file.'#'.$targetNamespace;
1142
    }
1143
1144
    /**
1145
     * @param string $content
1146
     * @param string $file
1147
     *
1148
     * @return Schema
1149
     *
1150
     * @throws IOException
1151
     */
1152 44
    public function readString($content, $file = 'schema.xsd')
1153
    {
1154 44
        $xml = new DOMDocument('1.0', 'UTF-8');
1155 44
        if (!$xml->loadXML($content)) {
1156
            throw new IOException("Can't load the schema");
1157
        }
1158 44
        $xml->documentURI = $file;
1159
1160 44
        return $this->readNode($xml->documentElement, $file);
1161
    }
1162
1163
    /**
1164
     * @param string $file
1165
     *
1166
     * @return Schema
1167
     */
1168 1
    public function readFile($file)
1169
    {
1170 1
        $xml = $this->getDOM($file);
1171
1172 1
        return $this->readNode($xml->documentElement, $file);
1173
    }
1174
1175
    /**
1176
     * @param string $file
1177
     *
1178
     * @return DOMDocument
1179
     *
1180
     * @throws IOException
1181
     */
1182 45
    private function getDOM($file)
1183
    {
1184 45
        $xml = new DOMDocument('1.0', 'UTF-8');
1185 45
        if (!$xml->load($file)) {
1186
            throw new IOException("Can't load the file $file");
1187
        }
1188
1189 45
        return $xml;
1190
    }
1191
1192 45
    private static function againstDOMNodeList(
1193
        DOMElement $node,
1194
        Closure $againstNodeList
1195
    ) {
1196 45
        $limit = $node->childNodes->length;
1197 45
        for ($i = 0; $i < $limit; $i += 1) {
1198
            /**
1199
             * @var DOMNode
1200
             */
1201 45
            $childNode = $node->childNodes->item($i);
1202
1203 45
            if ($childNode instanceof DOMElement) {
1204 45
                $againstNodeList(
1205 45
                    $node,
1206
                    $childNode
1207 45
                );
1208 45
            }
1209 45
        }
1210 45
    }
1211
1212
    /**
1213
     * @return Closure
1214
     */
1215 45
    private function CallbackGeneratorMaybeCallMethodAgainstDOMNodeList(
1216
        SchemaItem $type,
1217
        array $methods
1218
    ) {
1219
        return function (
1220
            DOMElement $node,
1221
            DOMElement $childNode
1222
        ) use (
1223 45
            $methods,
1224 45
            $type
1225
        ) {
1226
            /**
1227
             * @var string[]
1228
             */
1229 45
            $methods = $methods;
1230
1231 45
            $this->maybeCallMethod(
1232 45
                $methods,
1233 45
                $childNode->localName,
1234 45
                $childNode,
1235 45
                $type,
1236
                $childNode
1237 45
            );
1238 45
        };
1239
    }
1240
1241 45
    private function loadTypeWithCallbackOnChildNodes(
1242
        Schema $schema,
1243
        DOMElement $node,
1244
        Closure $callback
1245
    ) {
1246 45
        self::againstDOMNodeList(
1247 45
            $node,
1248
            function (
1249
                DOMElement $node,
1250
                DOMElement $childNode
1251
            ) use (
1252 45
                $schema,
1253 45
                $callback
1254
            ) {
1255 45
                $this->loadTypeWithCallback(
1256 45
                    $schema,
1257 45
                    $childNode,
1258
                    $callback
1259 45
                );
1260 45
            }
1261 45
        );
1262 45
    }
1263
1264 45
    private function loadTypeWithCallback(
1265
        Schema $schema,
1266
        DOMElement $childNode,
1267
        Closure $callback
1268
    ) {
1269
        $methods = [
1270 45
            'complexType' => 'loadComplexType',
1271 45
            'simpleType' => 'loadSimpleType',
1272 45
        ];
1273
1274
        /**
1275
         * @var Closure|null
1276
         */
1277 45
        $func = $this->maybeCallMethod(
1278 45
            $methods,
1279 45
            $childNode->localName,
1280 45
            $childNode,
1281 45
            $schema,
1282 45
            $childNode,
1283
            $callback
1284 45
        );
1285
1286 45
        if ($func instanceof Closure) {
1287 45
            call_user_func($func);
1288 45
        }
1289 45
    }
1290
1291
    /**
1292
     * @param string $file
1293
     * @param string $namespace
1294
     *
1295
     * @return Closure
1296
     */
1297 45
    private function loadImport(
1298
        Schema $schema,
1299
        DOMElement $node
1300
    ) {
1301 45
        $base = urldecode($node->ownerDocument->documentURI);
1302 45
        $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute('schemaLocation'));
1303
1304 45
        $namespace = $node->getAttribute('namespace');
1305
1306 45
        $keys = $this->loadImportFreshKeys($namespace, $file);
1307
1308
        if (
1309 45
            self::hasLoadedFile(...$keys)
1310 45
        ) {
1311 45
            $schema->addSchema(self::getLoadedFile(...$keys));
1312
1313
            return function () {
1314 45
            };
1315
        }
1316
1317 1
        return $this->loadImportFresh($namespace, $schema, $file);
1318
    }
1319
1320
    /**
1321
     * @param string $namespace
1322
     * @param string $file
1323
     *
1324
     * @return mixed[]
1325
     */
1326 45
    private function loadImportFreshKeys(
1327
        $namespace,
1328
        $file
1329
    ) {
1330 45
        $globalSchemaInfo = $this->getGlobalSchemaInfo();
1331
1332 45
        $keys = [];
1333
1334 45
        if (isset($globalSchemaInfo[$namespace])) {
1335 45
            $keys[] = $globalSchemaInfo[$namespace];
1336 45
        }
1337
1338 45
        $keys[] = $this->getNamespaceSpecificFileIndex(
1339 45
            $file,
1340
            $namespace
1341 45
        );
1342
1343 45
        $keys[] = $file;
1344
1345 45
        return $keys;
1346
    }
1347
1348
    /**
1349
     * @param string $namespace
1350
     * @param string $file
1351
     *
1352
     * @return Schema
1353
     */
1354 1
    private function loadImportFreshCallbacksNewSchema(
1355
        $namespace,
1356
        Schema $schema,
1357
        $file
1358
    ) {
1359
        /**
1360
         * @var Schema $newSchema
1361
         */
1362 1
        $newSchema = self::setLoadedFile(
1363 1
            $file,
1364 1
            ($namespace ? new Schema() : $schema)
1365 1
        );
1366
1367 1
        if ($namespace) {
1368
            $newSchema->addSchema($this->getGlobalSchema());
1369
            $schema->addSchema($newSchema);
1370
        }
1371
1372 1
        return $newSchema;
1373
    }
1374
1375
    /**
1376
     * @param string $namespace
1377
     * @param string $file
1378
     *
1379
     * @return Closure[]
1380
     */
1381 1
    private function loadImportFreshCallbacks(
1382
        $namespace,
1383
        Schema $schema,
1384
        $file
1385
    ) {
1386
        /**
1387
         * @var string
1388
         */
1389 1
        $file = $file;
1390
1391 1
        return $this->schemaNode(
1392 1
            $this->loadImportFreshCallbacksNewSchema(
1393 1
                $namespace,
1394 1
                $schema,
1395
                $file
1396 1
            ),
1397 1
            $this->getDOM(
1398 1
                $this->hasKnownSchemaLocation($file)
1399 1
                    ? $this->getKnownSchemaLocation($file)
1400
                    : $file
1401 1
            )->documentElement,
1402
            $schema
1403 1
        );
1404
    }
1405
1406
    /**
1407
     * @param string $namespace
1408
     * @param string $file
1409
     *
1410
     * @return Closure
1411
     */
1412 1
    private function loadImportFresh(
1413
        $namespace,
1414
        Schema $schema,
1415
        $file
1416
    ) {
1417
        return function () use ($namespace, $schema, $file) {
1418
            foreach (
1419 1
                $this->loadImportFreshCallbacks(
1420 1
                    $namespace,
1421 1
                    $schema,
1422
                    $file
1423 1
                ) as $callback
1424 1
            ) {
1425 1
                $callback();
1426 1
            }
1427 1
        };
1428
    }
1429
1430
    /**
1431
     * @return Element
1432
     */
1433 45
    private function loadElement(
1434
        Schema $schema,
1435
        DOMElement $node
1436
    ) {
1437 45
        $element = new Element($schema, $node->getAttribute('name'));
1438 45
        $element->setDoc(self::getDocumentation($node));
1439
1440 45
        $this->fillItem($element, $node);
1441
1442 45
        self::maybeSetMax($element, $node);
1443 45
        self::maybeSetMin($element, $node);
1444
1445 45
        $xp = new \DOMXPath($node->ownerDocument);
1446 45
        $xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema');
1447
1448 45
        if ($xp->query('ancestor::xs:choice', $node)->length) {
1449 45
            $element->setMin(0);
1450 45
        }
1451
1452 45
        if ($node->hasAttribute('nillable')) {
1453 3
            $element->setNil($node->getAttribute('nillable') == 'true');
1454 3
        }
1455 45
        if ($node->hasAttribute('form')) {
1456 3
            $element->setQualified($node->getAttribute('form') == 'qualified');
1457 3
        }
1458
1459 45
        return $element;
1460
    }
1461
1462
    /**
1463
     * @return ElementRef
1464
     */
1465 45
    private static function loadElementRef(
1466
        ElementDef $referenced,
1467
        DOMElement $node
1468
    ) {
1469 45
        $ref = new ElementRef($referenced);
1470 45
        $ref->setDoc(self::getDocumentation($node));
1471
1472 45
        self::maybeSetMax($ref, $node);
1473 45
        self::maybeSetMin($ref, $node);
1474 45
        if ($node->hasAttribute('nillable')) {
1475
            $ref->setNil($node->getAttribute('nillable') == 'true');
1476
        }
1477 45
        if ($node->hasAttribute('form')) {
1478
            $ref->setQualified($node->getAttribute('form') == 'qualified');
1479
        }
1480
1481 45
        return $ref;
1482
    }
1483
1484
    /**
1485
     * @return \Closure
1486
     */
1487 45
    private function loadAttributeGroup(
1488
        Schema $schema,
1489
        DOMElement $node
1490
    ) {
1491 45
        $attGroup = new AttributeGroup($schema, $node->getAttribute('name'));
1492 45
        $attGroup->setDoc(self::getDocumentation($node));
1493 45
        $schema->addAttributeGroup($attGroup);
1494
1495
        return function () use ($schema, $node, $attGroup) {
1496 45
            SchemaReader::againstDOMNodeList(
1497 45
                $node,
1498 45
                function (
1499
                    DOMElement $node,
1500
                    DOMElement $childNode
1501
                ) use (
1502 45
                    $schema,
1503 45
                    $attGroup
1504
                ) {
1505 45
                    switch ($childNode->localName) {
1506 45
                        case 'attribute':
1507 45
                            $attribute = $this->getAttributeFromAttributeOrRef(
1508 45
                                $childNode,
1509 45
                                $schema,
1510
                                $node
1511 45
                            );
1512 45
                            $attGroup->addAttribute($attribute);
1513 45
                            break;
1514 45
                        case 'attributeGroup':
1515 1
                            $this->findSomethingLikeAttributeGroup(
1516 1
                                $schema,
1517 1
                                $node,
1518 1
                                $childNode,
1519
                                $attGroup
1520 1
                            );
1521 1
                            break;
1522 45
                    }
1523 45
                }
1524 45
            );
1525 45
        };
1526
    }
1527
1528
    /**
1529
     * @return AttributeItem
1530
     */
1531 45
    private function getAttributeFromAttributeOrRef(
1532
        DOMElement $childNode,
1533
        Schema $schema,
1534
        DOMElement $node
1535
    ) {
1536 45
        if ($childNode->hasAttribute('ref')) {
1537
            /**
1538
             * @var AttributeItem
1539
             */
1540 45
            $attribute = $this->findSomething('findAttribute', $schema, $node, $childNode->getAttribute('ref'));
1541 45
        } else {
1542
            /**
1543
             * @var Attribute
1544
             */
1545 45
            $attribute = $this->loadAttribute($schema, $childNode);
1546
        }
1547
1548 45
        return $attribute;
1549
    }
1550
1551
    /**
1552
     * @return Attribute
1553
     */
1554 45
    private function loadAttribute(
1555
        Schema $schema,
1556
        DOMElement $node
1557
    ) {
1558 45
        $attribute = new Attribute($schema, $node->getAttribute('name'));
1559 45
        $attribute->setDoc(self::getDocumentation($node));
1560 45
        $this->fillItem($attribute, $node);
1561
1562 45
        if ($node->hasAttribute('nillable')) {
1563 1
            $attribute->setNil($node->getAttribute('nillable') == 'true');
1564 1
        }
1565 45
        if ($node->hasAttribute('form')) {
1566 1
            $attribute->setQualified($node->getAttribute('form') == 'qualified');
1567 1
        }
1568 45
        if ($node->hasAttribute('use')) {
1569 45
            $attribute->setUse($node->getAttribute('use'));
1570 45
        }
1571
1572 45
        return $attribute;
1573
    }
1574
1575 45
    private function addAttributeFromAttributeOrRef(
1576
        BaseComplexType $type,
1577
        DOMElement $childNode,
1578
        Schema $schema,
1579
        DOMElement $node
1580
    ) {
1581 45
        $attribute = $this->getAttributeFromAttributeOrRef(
1582 45
            $childNode,
1583 45
            $schema,
1584
            $node
1585 45
        );
1586
1587 45
        $type->addAttribute($attribute);
1588 45
    }
1589
1590 45
    private function findSomethingLikeAttributeGroup(
1591
        Schema $schema,
1592
        DOMElement $node,
1593
        DOMElement $childNode,
1594
        AttributeContainer $addToThis
1595
    ) {
1596
        /**
1597
         * @var AttributeItem
1598
         */
1599 45
        $attribute = $this->findSomething('findAttributeGroup', $schema, $node, $childNode->getAttribute('ref'));
1600 45
        $addToThis->addAttribute($attribute);
1601 45
    }
1602
1603
    /**
1604
     * @var Schema[]
1605
     */
1606
    protected static $loadedFiles = array();
1607
1608
    /**
1609
     * @param string ...$keys
1610
     *
1611
     * @return bool
1612
     */
1613 45
    private static function hasLoadedFile(...$keys)
1614
    {
1615 45
        foreach ($keys as $key) {
1616 45
            if (isset(self::$loadedFiles[$key])) {
1617 45
                return true;
1618
            }
1619 1
        }
1620
1621 1
        return false;
1622
    }
1623
1624
    /**
1625
     * @param string ...$keys
1626
     *
1627
     * @return Schema
1628
     *
1629
     * @throws RuntimeException if loaded file not found
1630
     */
1631 45
    public static function getLoadedFile(...$keys)
1632
    {
1633 45
        foreach ($keys as $key) {
1634 45
            if (isset(self::$loadedFiles[$key])) {
1635 45
                return self::$loadedFiles[$key];
1636
            }
1637
        }
1638
1639
        throw new RuntimeException('Loaded file was not found!');
1640
    }
1641
1642
    /**
1643
     * @param string $key
1644
     *
1645
     * @return Schema
1646
     */
1647 45
    private static function setLoadedFile($key, Schema $schema)
1648
    {
1649 45
        self::$loadedFiles[$key] = $schema;
1650
1651 45
        return $schema;
1652
    }
1653
1654 45
    private function setSchemaThingsFromNode(
1655
        Schema $schema,
1656
        DOMElement $node,
1657
        Schema $parent = null
1658
    ) {
1659 45
        $schema->setDoc(self::getDocumentation($node));
1660
1661 45
        if ($node->hasAttribute('targetNamespace')) {
1662 45
            $schema->setTargetNamespace($node->getAttribute('targetNamespace'));
1663 45
        } elseif ($parent) {
1664
            $schema->setTargetNamespace($parent->getTargetNamespace());
1665
        }
1666 45
        $schema->setElementsQualification($node->getAttribute('elementFormDefault') == 'qualified');
1667 45
        $schema->setAttributesQualification($node->getAttribute('attributeFormDefault') == 'qualified');
1668 45
        $schema->setDoc(self::getDocumentation($node));
1669 45
    }
1670
}
1671