Passed
Push — static-analysis ( e718b9...07c60a )
by SignpostMarv
06:43 queued 04:11
created

SchemaReader::loadTypeWithCallbackOnChildNodes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 12
nc 1
nop 3
dl 0
loc 18
ccs 8
cts 8
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
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
241
        return function () use ($group, $node) {
242 45
            static::againstDOMNodeList(
243 45
                $node,
244
                function (DOMelement $node, DOMElement $childNode) use ($group) {
245 45
                    switch ($childNode->localName) {
246 45
                        case 'sequence':
247 45
                        case 'choice':
248 45
                        case 'all':
249 45
                            $this->loadSequence($group, $childNode);
250 45
                            break;
251 45
                    }
252 45
                }
253 45
            );
254 45
        };
255
    }
256
257
    /**
258
     * @return Group|GroupRef
259
     */
260 45
    private static function loadGroupBeforeCheckingChildNodes(
261
        Schema $schema,
262
        DOMElement $node
263
    ) {
264 45
        $group = new Group($schema, $node->getAttribute('name'));
265 45
        $group->setDoc(self::getDocumentation($node));
266
267 45
        if ($node->hasAttribute('maxOccurs')) {
268
            /**
269
             * @var GroupRef
270
             */
271
            $group = self::maybeSetMax(new GroupRef($group), $node);
272
        }
273 45
        if ($node->hasAttribute('minOccurs')) {
274
            /**
275
             * @var GroupRef
276
             */
277
            $group = self::maybeSetMin(
278
                $group instanceof GroupRef ? $group : new GroupRef($group),
279
                $node
280
            );
281
        }
282
283 45
        $schema->addGroup($group);
284
285 45
        return $group;
286
    }
287
288
    /**
289
     * @return GroupRef
290
     */
291 45
    private function loadGroupRef(Group $referenced, DOMElement $node)
292
    {
293 45
        $ref = new GroupRef($referenced);
294 45
        $ref->setDoc(self::getDocumentation($node));
295
296 45
        self::maybeSetMax($ref, $node);
297 45
        self::maybeSetMin($ref, $node);
298
299 45
        return $ref;
300
    }
301
302
    /**
303
     * @param Closure|null $callback
304
     *
305
     * @return Closure
306
     */
307 45
    private function loadComplexType(Schema $schema, DOMElement $node, $callback = null)
308
    {
309
        /**
310
         * @var bool
311
         */
312 45
        $isSimple = false;
313
314 45
        static::againstDOMNodeList(
315 45
            $node,
316
            function (
317
                DOMElement $node,
318
                DOMElement $childNode
319
            ) use (
320
                &$isSimple
321
            ) {
322 45
                if ($isSimple) {
323 1
                    return;
324
                }
325 45
                if ($childNode->localName === 'simpleContent') {
326 2
                    $isSimple = true;
327 2
                }
328 45
            }
329 45
        );
330
331 45
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute('name')) : new ComplexType($schema, $node->getAttribute('name'));
332
333 45
        $type->setDoc(static::getDocumentation($node));
334 45
        if ($node->getAttribute('name')) {
335 45
            $schema->addType($type);
336 45
        }
337
338
        return function () use ($type, $node, $schema, $callback) {
339 45
            $this->fillTypeNode($type, $node, true);
340
341 45
            static::againstDOMNodeList(
342 45
                $node,
343
                function (
344
                    DOMElement $node,
345
                    DOMElement $childNode
346
                ) use (
347 45
                    $schema,
348 45
                    $type
349
                ) {
350 45
                    $this->loadComplexTypeFromChildNode(
351 45
                        $type,
352 45
                        $node,
353 45
                        $childNode,
354
                        $schema
355 45
                    );
356 45
                }
357 45
            );
358
359 45
            if ($callback) {
360 45
                call_user_func($callback, $type);
361 45
            }
362 45
        };
363
    }
364
365 45
    private function loadComplexTypeFromChildNode(
366
        BaseComplexType $type,
367
        DOMElement $node,
368
        DOMElement $childNode,
369
        Schema $schema
370
    ) {
371 45
        switch ($childNode->localName) {
372 45
            case 'sequence':
373 45
            case 'choice':
374 45
            case 'all':
375 45
                if ($type instanceof ElementContainer) {
376 45
                    $this->loadSequence(
377 45
                        $type,
378
                        $childNode
379 45
                    );
380 45
                }
381 45
                break;
382 45
            case 'attribute':
383 45
                $this->addAttributeFromAttributeOrRef(
384 45
                    $type,
385 45
                    $childNode,
386 45
                    $schema,
387
                    $node
388 45
                );
389 45
                break;
390 45
            case 'attributeGroup':
391 2
                $this->findSomethingLikeAttributeGroup(
392 2
                    $schema,
393 2
                    $node,
394 2
                    $childNode,
395
                    $type
396 2
                );
397 2
                break;
398 45
            case 'group':
399
                if (
400
                    $type instanceof ComplexType
401 1
                ) {
402 1
                    $this->addGroupAsElement(
403 1
                        $schema,
404 1
                        $node,
405 1
                        $childNode,
406
                        $type
407 1
                    );
408 1
                }
409 1
                break;
410 45
        }
411 45
    }
412
413
    /**
414
     * @param Closure|null $callback
415
     *
416
     * @return Closure
417
     */
418 45
    private function loadSimpleType(Schema $schema, DOMElement $node, $callback = null)
419
    {
420 45
        $type = new SimpleType($schema, $node->getAttribute('name'));
421 45
        $type->setDoc(static::getDocumentation($node));
422 45
        if ($node->getAttribute('name')) {
423 45
            $schema->addType($type);
424 45
        }
425
426
        return function () use ($type, $node, $callback) {
427 45
            $this->fillTypeNode($type, $node, true);
428
429 45
            static::againstDOMNodeList(
430 45
                $node,
431
                function (DOMElement $node, DOMElement $childNode) use ($type) {
432 45
                    switch ($childNode->localName) {
433 45
                        case 'union':
434 45
                            $this->loadUnion($type, $childNode);
435 45
                            break;
436 45
                        case 'list':
437 45
                            $this->loadList($type, $childNode);
438 45
                            break;
439 45
                    }
440 45
                }
441 45
            );
442
443 45
            if ($callback) {
444 45
                call_user_func($callback, $type);
445 45
            }
446 45
        };
447
    }
448
449 45
    private function loadList(SimpleType $type, DOMElement $node)
450
    {
451 45
        if ($node->hasAttribute('itemType')) {
452
            /**
453
             * @var SimpleType
454
             */
455 45
            $listType = $this->findSomeType($type, $node, 'itemType');
456 45
            $type->setList($listType);
457 45
        } else {
458 45
            self::againstDOMNodeList(
459 45
                $node,
460
                function (
461
                    DOMElement $node,
462
                    DOMElement $childNode
463
                ) use (
464 45
                    $type
465
                ) {
466 45
                    $this->loadTypeWithCallback(
467 45
                        $type->getSchema(),
468 45
                        $childNode,
469
                        function (SimpleType $list) use ($type) {
470 45
                            $type->setList($list);
471 45
                        }
472 45
                    );
473 45
                }
474 45
            );
475
        }
476 45
    }
477
478 45
    private function loadUnion(SimpleType $type, DOMElement $node)
479
    {
480 45
        if ($node->hasAttribute('memberTypes')) {
481 45
            $types = preg_split('/\s+/', $node->getAttribute('memberTypes'));
482 45
            foreach ($types as $typeName) {
483
                /**
484
                 * @var SimpleType
485
                 */
486 45
                $unionType = $this->findSomeTypeFromAttribute(
487 45
                    $type,
488 45
                    $node,
489
                    $typeName
490 45
                );
491 45
                $type->addUnion($unionType);
492 45
            }
493 45
        }
494 45
        self::againstDOMNodeList(
495 45
            $node,
496
            function (
497
                DOMElement $node,
498
                DOMElement $childNode
499
            ) use (
500 45
                $type
501
            ) {
502 45
                $this->loadTypeWithCallback(
503 45
                    $type->getSchema(),
504 45
                    $childNode,
505
                    function (SimpleType $unType) use ($type) {
506 45
                        $type->addUnion($unType);
507 45
                    }
508 45
                );
509 45
            }
510 45
        );
511 45
    }
512
513 45
    private function loadExtensionChildNodes(
514
        BaseComplexType $type,
515
        DOMElement $node
516
    ) {
517 45
        static::againstDOMNodeList(
518 45
            $node,
519 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...
520
                DOMElement $node,
521
                DOMElement $childNode
522
            ) use (
523 45
                $type
524
            ) {
525 45
                switch ($childNode->localName) {
526 45
                    case 'sequence':
527 45
                    case 'choice':
528 45
                    case 'all':
529 45
                        if ($type instanceof ElementContainer) {
530 45
                            $this->loadSequence(
531 45
                                $type,
532
                                $childNode
533 45
                            );
534 45
                        }
535 45
                        break;
536 45
                    case 'attribute':
537 45
                        $this->addAttributeFromAttributeOrRef(
538 45
                            $type,
539 45
                            $childNode,
540 45
                            $type->getSchema(),
541
                            $node
542 45
                        );
543 45
                        break;
544 45
                    case 'attributeGroup':
545 45
                        $this->findSomethingLikeAttributeGroup(
546 45
                            $type->getSchema(),
547 45
                            $node,
548 45
                            $childNode,
549
                            $type
550 45
                        );
551 45
                        break;
552 45
                }
553 45
            }
554 45
        );
555 45
    }
556
557 45
    private function loadExtension(BaseComplexType $type, DOMElement $node)
558
    {
559 45
        $extension = new Extension();
560 45
        $type->setExtension($extension);
561
562 45
        if ($node->hasAttribute('base')) {
563 45
            $this->findAndSetSomeBase(
564 45
                $type,
565 45
                $extension,
566
                $node
567 45
            );
568 45
        }
569 45
        $this->loadExtensionChildNodes($type, $node);
570 45
    }
571
572 45
    private function loadRestriction(Type $type, DOMElement $node)
573
    {
574 45
        $restriction = new Restriction();
575 45
        $type->setRestriction($restriction);
576 45
        if ($node->hasAttribute('base')) {
577 45
            $this->findAndSetSomeBase($type, $restriction, $node);
578 45
        } else {
579 45
            self::againstDOMNodeList(
580 45
                $node,
581
                function (
582
                    DOMElement $node,
583
                    DOMElement $childNode
584
                ) use (
585 45
                    $type,
586 45
                    $restriction
587
                ) {
588 45
                    $this->loadTypeWithCallback(
589 45
                        $type->getSchema(),
590 45
                        $childNode,
591
                        function (Type $restType) use ($restriction) {
592 45
                            $restriction->setBase($restType);
593 45
                        }
594 45
                    );
595 45
                }
596 45
            );
597
        }
598 45
        self::againstDOMNodeList(
599 45
            $node,
600
            function (
601
                DOMElement $node,
602
                DOMElement $childNode
603
            ) use (
604 45
                $restriction
605
            ) {
606
                if (
607 45
                    in_array(
608 45
                        $childNode->localName,
609
                        [
610 45
                            'enumeration',
611 45
                            'pattern',
612 45
                            'length',
613 45
                            'minLength',
614 45
                            'maxLength',
615 45
                            'minInclusive',
616 45
                            'maxInclusive',
617 45
                            'minExclusive',
618 45
                            'maxExclusive',
619 45
                            'fractionDigits',
620 45
                            'totalDigits',
621 45
                            'whiteSpace',
622 45
                        ],
623
                        true
624 45
                    )
625 45
                ) {
626 45
                    $restriction->addCheck(
627 45
                        $childNode->localName,
628
                        [
629 45
                            'value' => $childNode->getAttribute('value'),
630 45
                            'doc' => self::getDocumentation($childNode),
631
                        ]
632 45
                    );
633 45
                }
634 45
            }
635 45
        );
636 45
    }
637
638
    /**
639
     * @return Closure
640
     */
641 45
    private function loadElementDef(Schema $schema, DOMElement $node)
642
    {
643 45
        return $this->loadAttributeOrElementDef($schema, $node, false);
644
    }
645
646
    /**
647
     * @param bool $checkAbstract
648
     */
649 45
    private function fillTypeNode(Type $type, DOMElement $node, $checkAbstract = false)
650
    {
651 45
        if ($checkAbstract) {
652 45
            $type->setAbstract($node->getAttribute('abstract') === 'true' || $node->getAttribute('abstract') === '1');
653 45
        }
654
655 45
        static::againstDOMNodeList(
656 45
            $node,
657
            function (DOMElement $node, DOMElement $childNode) use ($type) {
658 45
                switch ($childNode->localName) {
659 45
                    case 'restriction':
660 45
                        $this->loadRestriction($type, $childNode);
661 45
                        break;
662 45
                    case 'extension':
663 45
                        if ($type instanceof BaseComplexType) {
664 45
                            $this->loadExtension($type, $childNode);
665 45
                        }
666 45
                        break;
667 45
                    case 'simpleContent':
668 45
                    case 'complexContent':
669 45
                        $this->fillTypeNode($type, $childNode);
670 45
                        break;
671 45
                }
672 45
            }
673 45
        );
674 45
    }
675
676 45
    private function fillItemNonLocalType(Item $element, DOMElement $node)
677
    {
678 45
        if ($node->getAttribute('type')) {
679
            /**
680
             * @var Type
681
             */
682 45
            $type = $this->findSomeType($element, $node, 'type');
683 45
        } else {
684
            /**
685
             * @var Type
686
             */
687 45
            $type = $this->findSomeTypeFromAttribute(
688 45
                $element,
689 45
                $node,
690 45
                ($node->lookupPrefix(self::XSD_NS).':anyType')
691 45
            );
692
        }
693
694 45
        $element->setType($type);
695 45
    }
696
697
    /**
698
     * @param string $attributeName
699
     *
700
     * @return SchemaItem
701
     */
702 45
    private function findSomeType(
703
        SchemaItem $fromThis,
704
        DOMElement $node,
705
        $attributeName
706
    ) {
707 45
        return $this->findSomeTypeFromAttribute(
708 45
            $fromThis,
709 45
            $node,
710 45
            $node->getAttribute($attributeName)
711 45
        );
712
    }
713
714
    /**
715
     * @param string $attributeName
716
     *
717
     * @return SchemaItem
718
     */
719 45
    private function findSomeTypeFromAttribute(
720
        SchemaItem $fromThis,
721
        DOMElement $node,
722
        $attributeName
723
    ) {
724
        /**
725
         * @var SchemaItem
726
         */
727 45
        $out = $this->findSomething(
728 45
            'findType',
729 45
            $fromThis->getSchema(),
730 45
            $node,
731
            $attributeName
732 45
        );
733
734 45
        return $out;
735
    }
736
737
    const XSD_NS = 'http://www.w3.org/2001/XMLSchema';
738
739
    const XML_NS = 'http://www.w3.org/XML/1998/namespace';
740
741
    /**
742
     * @var string[]
743
     */
744
    protected $knownLocationSchemas = [
745
        'http://www.w3.org/2001/xml.xsd' => (
746
            __DIR__.'/Resources/xml.xsd'
747
        ),
748
        'http://www.w3.org/2001/XMLSchema.xsd' => (
749
            __DIR__.'/Resources/XMLSchema.xsd'
750
        ),
751
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' => (
752
            __DIR__.'/Resources/oasis-200401-wss-wssecurity-secext-1.0.xsd'
753
        ),
754
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' => (
755
            __DIR__.'/Resources/oasis-200401-wss-wssecurity-utility-1.0.xsd'
756
        ),
757
        'https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
758
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
759
        ),
760
        'http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
761
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
762
        ),
763
    ];
764
765
    /**
766
     * @var string[]
767
     */
768
    protected static $globalSchemaInfo = array(
769
        self::XML_NS => 'http://www.w3.org/2001/xml.xsd',
770
        self::XSD_NS => 'http://www.w3.org/2001/XMLSchema.xsd',
771
    );
772
773
    /**
774
     * @param string $remote
775
     * @param string $local
776
     */
777
    public function addKnownSchemaLocation($remote, $local)
778
    {
779
        $this->knownLocationSchemas[$remote] = $local;
780
    }
781
782
    /**
783
     * @param string $remote
784
     *
785
     * @return bool
786
     */
787 1
    private function hasKnownSchemaLocation($remote)
788
    {
789 1
        return isset($this->knownLocationSchemas[$remote]);
790
    }
791
792
    /**
793
     * @param string $remote
794
     *
795
     * @return string
796
     */
797
    private function getKnownSchemaLocation($remote)
798
    {
799
        return $this->knownLocationSchemas[$remote];
800
    }
801
802
    /**
803
     * @param DOMElement $node
804
     *
805
     * @return string
806
     */
807 45
    private static function getDocumentation(DOMElement $node)
808
    {
809 45
        $doc = '';
810 45
        static::againstDOMNodeList(
811 45
            $node,
812
            function (
813
                DOMElement $node,
814
                DOMElement $childNode
815
            ) use (
816
                &$doc
817
            ) {
818 45
                if ($childNode->localName == 'annotation') {
819 45
                    $doc .= static::getDocumentation($childNode);
820 45
                } elseif ($childNode->localName == 'documentation') {
821 45
                    $doc .= $childNode->nodeValue;
822 45
                }
823 45
            }
824 45
        );
825 45
        $doc = preg_replace('/[\t ]+/', ' ', $doc);
826
827 45
        return trim($doc);
828
    }
829
830
    /**
831
     * @param Schema     $schema
832
     * @param DOMElement $node
833
     * @param Schema     $parent
834
     *
835
     * @return Closure[]
836
     */
837 45
    private function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null)
838
    {
839 45
        $this->setSchemaThingsFromNode($schema, $node, $parent);
840 45
        $functions = array();
841
842 45
        static::againstDOMNodeList(
843 45
            $node,
844
            function (
845
                DOMElement $node,
846
                DOMElement $childNode
847
            ) use (
848 45
                $schema,
849
                &$functions
850
            ) {
851 45
                $callback = null;
852
853 45
                switch ($childNode->localName) {
854 45
                    case 'attributeGroup':
855 45
                        $callback = $this->loadAttributeGroup($schema, $childNode);
856 45
                        break;
857 45
                    case 'include':
858 45
                    case 'import':
859 45
                        $callback = $this->loadImport($schema, $childNode);
860 45
                        break;
861 45
                    case 'element':
862 45
                        $callback = $this->loadElementDef($schema, $childNode);
863 45
                        break;
864 45
                    case 'attribute':
865 45
                        $callback = $this->loadAttributeDef($schema, $childNode);
866 45
                        break;
867 45
                    case 'group':
868 45
                        $callback = $this->loadGroup($schema, $childNode);
869 45
                        break;
870 45
                    case 'complexType':
871 45
                        $callback = $this->loadComplexType($schema, $childNode);
872 45
                        break;
873 45
                    case 'simpleType':
874 45
                        $callback = $this->loadSimpleType($schema, $childNode);
875 45
                        break;
876 45
                }
877
878 45
                if ($callback instanceof Closure) {
879 45
                    $functions[] = $callback;
880 45
                }
881 45
            }
882 45
        );
883
884 45
        return $functions;
885
    }
886
887
    /**
888
     * @return InterfaceSetMinMax
889
     */
890 45
    private static function maybeSetMax(InterfaceSetMinMax $ref, DOMElement $node)
891
    {
892
        if (
893 45
            $node->hasAttribute('maxOccurs')
894 45
        ) {
895 45
            $ref->setMax($node->getAttribute('maxOccurs') == 'unbounded' ? -1 : (int) $node->getAttribute('maxOccurs'));
896 45
        }
897
898 45
        return $ref;
899
    }
900
901
    /**
902
     * @return InterfaceSetMinMax
903
     */
904 45
    private static function maybeSetMin(InterfaceSetMinMax $ref, DOMElement $node)
905
    {
906 45
        if ($node->hasAttribute('minOccurs')) {
907 45
            $ref->setMin((int) $node->getAttribute('minOccurs'));
908 45
        }
909
910 45
        return $ref;
911
    }
912
913 45
    private function findAndSetSomeBase(
914
        Type $type,
915
        Base $setBaseOnThis,
916
        DOMElement $node
917
    ) {
918
        /**
919
         * @var Type
920
         */
921 45
        $parent = $this->findSomeType($type, $node, 'base');
922 45
        $setBaseOnThis->setBase($parent);
923 45
    }
924
925
    /**
926
     * @param string     $finder
927
     * @param Schema     $schema
928
     * @param DOMElement $node
929
     * @param string     $typeName
930
     *
931
     * @throws TypeException
932
     *
933
     * @return ElementItem|Group|AttributeItem|AttributeGroup|Type
934
     */
935 45
    private function findSomething($finder, Schema $schema, DOMElement $node, $typeName)
936
    {
937 45
        list($name, $namespace) = static::splitParts($node, $typeName);
938
939
        /**
940
         * @var string|null
941
         */
942 45
        $namespace = $namespace ?: $schema->getTargetNamespace();
943
944
        try {
945
            /**
946
             * @var ElementItem|Group|AttributeItem|AttributeGroup|Type
947
             */
948 45
            $out = $schema->$finder($name, $namespace);
949
950 45
            return $out;
951
        } catch (TypeNotFoundException $e) {
952
            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);
953
        }
954
    }
955
956 45
    private function fillItem(Item $element, DOMElement $node)
957
    {
958
        /**
959
         * @var bool
960
         */
961 45
        $skip = false;
962 45
        static::againstDOMNodeList(
963 45
            $node,
964
            function (
965
                DOMElement $node,
966
                DOMElement $childNode
967
            ) use (
968 45
                $element,
969
                &$skip
970
            ) {
971
                if (
972 45
                    !$skip &&
973 45
                    in_array(
974 45
                        $childNode->localName,
975
                        [
976 45
                            'complexType',
977 45
                            'simpleType',
978
                        ]
979 45
                    )
980 45
                ) {
981 45
                    $this->loadTypeWithCallback(
982 45
                        $element->getSchema(),
983 45
                        $childNode,
984
                        function (Type $type) use ($element) {
985 45
                            $element->setType($type);
986 45
                        }
987 45
                    );
988 45
                    $skip = true;
989 45
                }
990 45
            }
991 45
        );
992 45
        if ($skip) {
993 45
            return;
994
        }
995 45
        $this->fillItemNonLocalType($element, $node);
996 45
    }
997
998
    /**
999
     * @var Schema|null
1000
     */
1001
    protected $globalSchema;
1002
1003
    /**
1004
     * @return Schema[]
1005
     */
1006 45
    private function setupGlobalSchemas(array &$callbacks)
1007
    {
1008 45
        $globalSchemas = array();
1009 45
        foreach (self::$globalSchemaInfo as $namespace => $uri) {
1010 45
            self::setLoadedFile(
1011 45
                $uri,
1012 45
                $globalSchemas[$namespace] = $schema = new Schema()
1013 45
            );
1014 45
            if ($namespace === self::XSD_NS) {
1015 45
                $this->globalSchema = $schema;
1016 45
            }
1017 45
            $xml = $this->getDOM($this->knownLocationSchemas[$uri]);
1018 45
            $callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement));
1019 45
        }
1020
1021 45
        return $globalSchemas;
1022
    }
1023
1024
    /**
1025
     * @return string[]
1026
     */
1027 45
    public function getGlobalSchemaInfo()
1028
    {
1029 45
        return self::$globalSchemaInfo;
1030
    }
1031
1032
    /**
1033
     * @return Schema
1034
     */
1035 45
    public function getGlobalSchema()
1036
    {
1037 45
        if (!$this->globalSchema) {
1038 45
            $callbacks = array();
1039 45
            $globalSchemas = $this->setupGlobalSchemas($callbacks);
1040
1041 45
            $globalSchemas[static::XSD_NS]->addType(new SimpleType($globalSchemas[static::XSD_NS], 'anySimpleType'));
1042 45
            $globalSchemas[static::XSD_NS]->addType(new SimpleType($globalSchemas[static::XSD_NS], 'anyType'));
1043
1044 45
            $globalSchemas[static::XML_NS]->addSchema(
1045 45
                $globalSchemas[static::XSD_NS],
1046
                (string) static::XSD_NS
1047 45
            );
1048 45
            $globalSchemas[static::XSD_NS]->addSchema(
1049 45
                $globalSchemas[static::XML_NS],
1050
                (string) static::XML_NS
1051 45
            );
1052
1053
            /**
1054
             * @var Closure
1055
             */
1056 45
            foreach ($callbacks as $callback) {
1057 45
                $callback();
1058 45
            }
1059 45
        }
1060
1061
        /**
1062
         * @var Schema
1063
         */
1064 45
        $out = $this->globalSchema;
1065
1066 45
        return $out;
1067
    }
1068
1069
    /**
1070
     * @param DOMElement $node
1071
     * @param string     $file
1072
     *
1073
     * @return Schema
1074
     */
1075 45
    private function readNode(DOMElement $node, $file = 'schema.xsd')
1076
    {
1077 45
        $fileKey = $node->hasAttribute('targetNamespace') ? $this->getNamespaceSpecificFileIndex($file, $node->getAttribute('targetNamespace')) : $file;
1078 45
        self::setLoadedFile($fileKey, $rootSchema = new Schema());
1079
1080 45
        $rootSchema->addSchema($this->getGlobalSchema());
1081 45
        $callbacks = $this->schemaNode($rootSchema, $node);
1082
1083 45
        foreach ($callbacks as $callback) {
1084 39
            call_user_func($callback);
1085 45
        }
1086
1087 45
        return $rootSchema;
1088
    }
1089
1090
    /**
1091
     * It is possible that a single file contains multiple <xsd:schema/> nodes, for instance in a WSDL file.
1092
     *
1093
     * Each of these  <xsd:schema/> nodes typically target a specific namespace. Append the target namespace to the
1094
     * file to distinguish between multiple schemas in a single file.
1095
     *
1096
     * @param string $file
1097
     * @param string $targetNamespace
1098
     *
1099
     * @return string
1100
     */
1101 45
    private function getNamespaceSpecificFileIndex($file, $targetNamespace)
1102
    {
1103 45
        return $file.'#'.$targetNamespace;
1104
    }
1105
1106
    /**
1107
     * @param string $content
1108
     * @param string $file
1109
     *
1110
     * @return Schema
1111
     *
1112
     * @throws IOException
1113
     */
1114 44
    public function readString($content, $file = 'schema.xsd')
1115
    {
1116 44
        $xml = new DOMDocument('1.0', 'UTF-8');
1117 44
        if (!$xml->loadXML($content)) {
1118
            throw new IOException("Can't load the schema");
1119
        }
1120 44
        $xml->documentURI = $file;
1121
1122 44
        return $this->readNode($xml->documentElement, $file);
1123
    }
1124
1125
    /**
1126
     * @param string $file
1127
     *
1128
     * @return Schema
1129
     */
1130 1
    public function readFile($file)
1131
    {
1132 1
        $xml = $this->getDOM($file);
1133
1134 1
        return $this->readNode($xml->documentElement, $file);
1135
    }
1136
1137
    /**
1138
     * @param string $file
1139
     *
1140
     * @return DOMDocument
1141
     *
1142
     * @throws IOException
1143
     */
1144 45
    private function getDOM($file)
1145
    {
1146 45
        $xml = new DOMDocument('1.0', 'UTF-8');
1147 45
        if (!$xml->load($file)) {
1148
            throw new IOException("Can't load the file $file");
1149
        }
1150
1151 45
        return $xml;
1152
    }
1153
1154 45
    private static function againstDOMNodeList(
1155
        DOMElement $node,
1156
        Closure $againstNodeList
1157
    ) {
1158 45
        $limit = $node->childNodes->length;
1159 45
        for ($i = 0; $i < $limit; $i += 1) {
1160
            /**
1161
             * @var DOMNode
1162
             */
1163 45
            $childNode = $node->childNodes->item($i);
1164
1165 45
            if ($childNode instanceof DOMElement) {
1166 45
                $againstNodeList(
1167 45
                    $node,
1168
                    $childNode
1169 45
                );
1170 45
            }
1171 45
        }
1172 45
    }
1173
1174 45
    private function loadTypeWithCallback(
1175
        Schema $schema,
1176
        DOMElement $childNode,
1177
        Closure $callback
1178
    ) {
1179
        /**
1180
         * @var Closure|null $func
1181
         */
1182 45
        $func = null;
1183
1184 45
        switch ($childNode->localName) {
1185 45
            case 'complexType':
1186 45
                $func = $this->loadComplexType($schema, $childNode, $callback);
1187 45
                break;
1188 45
            case 'simpleType':
1189 45
                $func = $this->loadSimpleType($schema, $childNode, $callback);
1190 45
                break;
1191 45
        }
1192
1193 45
        if ($func instanceof Closure) {
1194 45
            call_user_func($func);
1195 45
        }
1196 45
    }
1197
1198
    /**
1199
     * @param string $file
1200
     * @param string $namespace
1201
     *
1202
     * @return Closure
1203
     */
1204 45
    private function loadImport(
1205
        Schema $schema,
1206
        DOMElement $node
1207
    ) {
1208 45
        $base = urldecode($node->ownerDocument->documentURI);
1209 45
        $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute('schemaLocation'));
1210
1211 45
        $namespace = $node->getAttribute('namespace');
1212
1213 45
        $keys = $this->loadImportFreshKeys($namespace, $file);
1214
1215
        if (
1216 45
            self::hasLoadedFile(...$keys)
1217 45
        ) {
1218 45
            $schema->addSchema(self::getLoadedFile(...$keys));
1219
1220
            return function () {
1221 45
            };
1222
        }
1223
1224 1
        return $this->loadImportFresh($namespace, $schema, $file);
1225
    }
1226
1227
    /**
1228
     * @param string $namespace
1229
     * @param string $file
1230
     *
1231
     * @return mixed[]
1232
     */
1233 45
    private function loadImportFreshKeys(
1234
        $namespace,
1235
        $file
1236
    ) {
1237 45
        $globalSchemaInfo = $this->getGlobalSchemaInfo();
1238
1239 45
        $keys = [];
1240
1241 45
        if (isset($globalSchemaInfo[$namespace])) {
1242 45
            $keys[] = $globalSchemaInfo[$namespace];
1243 45
        }
1244
1245 45
        $keys[] = $this->getNamespaceSpecificFileIndex(
1246 45
            $file,
1247
            $namespace
1248 45
        );
1249
1250 45
        $keys[] = $file;
1251
1252 45
        return $keys;
1253
    }
1254
1255
    /**
1256
     * @param string $namespace
1257
     * @param string $file
1258
     *
1259
     * @return Schema
1260
     */
1261 1
    private function loadImportFreshCallbacksNewSchema(
1262
        $namespace,
1263
        Schema $schema,
1264
        $file
1265
    ) {
1266
        /**
1267
         * @var Schema $newSchema
1268
         */
1269 1
        $newSchema = self::setLoadedFile(
1270 1
            $file,
1271 1
            ($namespace ? new Schema() : $schema)
1272 1
        );
1273
1274 1
        if ($namespace) {
1275
            $newSchema->addSchema($this->getGlobalSchema());
1276
            $schema->addSchema($newSchema);
1277
        }
1278
1279 1
        return $newSchema;
1280
    }
1281
1282
    /**
1283
     * @param string $namespace
1284
     * @param string $file
1285
     *
1286
     * @return Closure[]
1287
     */
1288 1
    private function loadImportFreshCallbacks(
1289
        $namespace,
1290
        Schema $schema,
1291
        $file
1292
    ) {
1293
        /**
1294
         * @var string
1295
         */
1296 1
        $file = $file;
1297
1298 1
        return $this->schemaNode(
1299 1
            $this->loadImportFreshCallbacksNewSchema(
1300 1
                $namespace,
1301 1
                $schema,
1302
                $file
1303 1
            ),
1304 1
            $this->getDOM(
1305 1
                $this->hasKnownSchemaLocation($file)
1306 1
                    ? $this->getKnownSchemaLocation($file)
1307
                    : $file
1308 1
            )->documentElement,
1309
            $schema
1310 1
        );
1311
    }
1312
1313
    /**
1314
     * @param string $namespace
1315
     * @param string $file
1316
     *
1317
     * @return Closure
1318
     */
1319 1
    private function loadImportFresh(
1320
        $namespace,
1321
        Schema $schema,
1322
        $file
1323
    ) {
1324
        return function () use ($namespace, $schema, $file) {
1325
            foreach (
1326 1
                $this->loadImportFreshCallbacks(
1327 1
                    $namespace,
1328 1
                    $schema,
1329
                    $file
1330 1
                ) as $callback
1331 1
            ) {
1332 1
                $callback();
1333 1
            }
1334 1
        };
1335
    }
1336
1337
    /**
1338
     * @return Element
1339
     */
1340 45
    private function loadElement(
1341
        Schema $schema,
1342
        DOMElement $node
1343
    ) {
1344 45
        $element = new Element($schema, $node->getAttribute('name'));
1345 45
        $element->setDoc(self::getDocumentation($node));
1346
1347 45
        $this->fillItem($element, $node);
1348
1349 45
        self::maybeSetMax($element, $node);
1350 45
        self::maybeSetMin($element, $node);
1351
1352 45
        $xp = new \DOMXPath($node->ownerDocument);
1353 45
        $xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema');
1354
1355 45
        if ($xp->query('ancestor::xs:choice', $node)->length) {
1356 45
            $element->setMin(0);
1357 45
        }
1358
1359 45
        if ($node->hasAttribute('nillable')) {
1360 3
            $element->setNil($node->getAttribute('nillable') == 'true');
1361 3
        }
1362 45
        if ($node->hasAttribute('form')) {
1363 3
            $element->setQualified($node->getAttribute('form') == 'qualified');
1364 3
        }
1365
1366 45
        return $element;
1367
    }
1368
1369
    /**
1370
     * @return ElementRef
1371
     */
1372 45
    private static function loadElementRef(
1373
        ElementDef $referenced,
1374
        DOMElement $node
1375
    ) {
1376 45
        $ref = new ElementRef($referenced);
1377 45
        $ref->setDoc(self::getDocumentation($node));
1378
1379 45
        self::maybeSetMax($ref, $node);
1380 45
        self::maybeSetMin($ref, $node);
1381 45
        if ($node->hasAttribute('nillable')) {
1382
            $ref->setNil($node->getAttribute('nillable') == 'true');
1383
        }
1384 45
        if ($node->hasAttribute('form')) {
1385
            $ref->setQualified($node->getAttribute('form') == 'qualified');
1386
        }
1387
1388 45
        return $ref;
1389
    }
1390
1391
    /**
1392
     * @return \Closure
1393
     */
1394 45
    private function loadAttributeGroup(
1395
        Schema $schema,
1396
        DOMElement $node
1397
    ) {
1398 45
        $attGroup = new AttributeGroup($schema, $node->getAttribute('name'));
1399 45
        $attGroup->setDoc(self::getDocumentation($node));
1400 45
        $schema->addAttributeGroup($attGroup);
1401
1402
        return function () use ($schema, $node, $attGroup) {
1403 45
            SchemaReader::againstDOMNodeList(
1404 45
                $node,
1405 45
                function (
1406
                    DOMElement $node,
1407
                    DOMElement $childNode
1408
                ) use (
1409 45
                    $schema,
1410 45
                    $attGroup
1411
                ) {
1412 45
                    switch ($childNode->localName) {
1413 45
                        case 'attribute':
1414 45
                            $attribute = $this->getAttributeFromAttributeOrRef(
1415 45
                                $childNode,
1416 45
                                $schema,
1417
                                $node
1418 45
                            );
1419 45
                            $attGroup->addAttribute($attribute);
1420 45
                            break;
1421 45
                        case 'attributeGroup':
1422 1
                            $this->findSomethingLikeAttributeGroup(
1423 1
                                $schema,
1424 1
                                $node,
1425 1
                                $childNode,
1426
                                $attGroup
1427 1
                            );
1428 1
                            break;
1429 45
                    }
1430 45
                }
1431 45
            );
1432 45
        };
1433
    }
1434
1435
    /**
1436
     * @return AttributeItem
1437
     */
1438 45
    private function getAttributeFromAttributeOrRef(
1439
        DOMElement $childNode,
1440
        Schema $schema,
1441
        DOMElement $node
1442
    ) {
1443 45
        if ($childNode->hasAttribute('ref')) {
1444
            /**
1445
             * @var AttributeItem
1446
             */
1447 45
            $attribute = $this->findSomething('findAttribute', $schema, $node, $childNode->getAttribute('ref'));
1448 45
        } else {
1449
            /**
1450
             * @var Attribute
1451
             */
1452 45
            $attribute = $this->loadAttribute($schema, $childNode);
1453
        }
1454
1455 45
        return $attribute;
1456
    }
1457
1458
    /**
1459
     * @return Attribute
1460
     */
1461 45
    private function loadAttribute(
1462
        Schema $schema,
1463
        DOMElement $node
1464
    ) {
1465 45
        $attribute = new Attribute($schema, $node->getAttribute('name'));
1466 45
        $attribute->setDoc(self::getDocumentation($node));
1467 45
        $this->fillItem($attribute, $node);
1468
1469 45
        if ($node->hasAttribute('nillable')) {
1470 1
            $attribute->setNil($node->getAttribute('nillable') == 'true');
1471 1
        }
1472 45
        if ($node->hasAttribute('form')) {
1473 1
            $attribute->setQualified($node->getAttribute('form') == 'qualified');
1474 1
        }
1475 45
        if ($node->hasAttribute('use')) {
1476 45
            $attribute->setUse($node->getAttribute('use'));
1477 45
        }
1478
1479 45
        return $attribute;
1480
    }
1481
1482 45
    private function addAttributeFromAttributeOrRef(
1483
        BaseComplexType $type,
1484
        DOMElement $childNode,
1485
        Schema $schema,
1486
        DOMElement $node
1487
    ) {
1488 45
        $attribute = $this->getAttributeFromAttributeOrRef(
1489 45
            $childNode,
1490 45
            $schema,
1491
            $node
1492 45
        );
1493
1494 45
        $type->addAttribute($attribute);
1495 45
    }
1496
1497 45
    private function findSomethingLikeAttributeGroup(
1498
        Schema $schema,
1499
        DOMElement $node,
1500
        DOMElement $childNode,
1501
        AttributeContainer $addToThis
1502
    ) {
1503
        /**
1504
         * @var AttributeItem
1505
         */
1506 45
        $attribute = $this->findSomething('findAttributeGroup', $schema, $node, $childNode->getAttribute('ref'));
1507 45
        $addToThis->addAttribute($attribute);
1508 45
    }
1509
1510
    /**
1511
     * @var Schema[]
1512
     */
1513
    protected static $loadedFiles = array();
1514
1515
    /**
1516
     * @param string ...$keys
1517
     *
1518
     * @return bool
1519
     */
1520 45
    private static function hasLoadedFile(...$keys)
1521
    {
1522 45
        foreach ($keys as $key) {
1523 45
            if (isset(self::$loadedFiles[$key])) {
1524 45
                return true;
1525
            }
1526 1
        }
1527
1528 1
        return false;
1529
    }
1530
1531
    /**
1532
     * @param string ...$keys
1533
     *
1534
     * @return Schema
1535
     *
1536
     * @throws RuntimeException if loaded file not found
1537
     */
1538 45
    public static function getLoadedFile(...$keys)
1539
    {
1540 45
        foreach ($keys as $key) {
1541 45
            if (isset(self::$loadedFiles[$key])) {
1542 45
                return self::$loadedFiles[$key];
1543
            }
1544
        }
1545
1546
        throw new RuntimeException('Loaded file was not found!');
1547
    }
1548
1549
    /**
1550
     * @param string $key
1551
     *
1552
     * @return Schema
1553
     */
1554 45
    private static function setLoadedFile($key, Schema $schema)
1555
    {
1556 45
        self::$loadedFiles[$key] = $schema;
1557
1558 45
        return $schema;
1559
    }
1560
1561 45
    private function setSchemaThingsFromNode(
1562
        Schema $schema,
1563
        DOMElement $node,
1564
        Schema $parent = null
1565
    ) {
1566 45
        $schema->setDoc(self::getDocumentation($node));
1567
1568 45
        if ($node->hasAttribute('targetNamespace')) {
1569 45
            $schema->setTargetNamespace($node->getAttribute('targetNamespace'));
1570 45
        } elseif ($parent) {
1571
            $schema->setTargetNamespace($parent->getTargetNamespace());
1572
        }
1573 45
        $schema->setElementsQualification($node->getAttribute('elementFormDefault') == 'qualified');
1574 45
        $schema->setAttributesQualification($node->getAttribute('attributeFormDefault') == 'qualified');
1575 45
        $schema->setDoc(self::getDocumentation($node));
1576 45
    }
1577
}
1578