Passed
Push — static-analysis ( c469fb...e718b9 )
by SignpostMarv
03:14
created

SchemaReader::loadTypeWithCallback()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

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