Passed
Push — static-analysis ( 1fd97e...c469fb )
by SignpostMarv
04:18
created

SchemaReader::makeCallbackCallback()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

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

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

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

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