Passed
Push — static-analysis ( 1958c5...5a8282 )
by SignpostMarv
10:41 queued 08:16
created

SchemaReader::loadImport()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 2
dl 0
loc 21
ccs 10
cts 10
cp 1
crap 2
rs 9.3142
c 0
b 0
f 0
1
<?php
2
3
namespace GoetasWebservices\XML\XSDReader;
4
5
use Closure;
6
use DOMDocument;
7
use DOMElement;
8
use DOMNode;
9
use GoetasWebservices\XML\XSDReader\Exception\IOException;
10
use GoetasWebservices\XML\XSDReader\Exception\TypeException;
11
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Attribute;
12
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeContainer;
13
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeDef;
14
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
15
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup;
16
use GoetasWebservices\XML\XSDReader\Schema\Element\Element;
17
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer;
18
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
19
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem;
20
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef;
21
use GoetasWebservices\XML\XSDReader\Schema\Element\Group;
22
use GoetasWebservices\XML\XSDReader\Schema\Element\InterfaceSetMinMax;
23
use GoetasWebservices\XML\XSDReader\Schema\Element\GroupRef;
24
use GoetasWebservices\XML\XSDReader\Schema\Exception\TypeNotFoundException;
25
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Base;
26
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Extension;
27
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Restriction;
28
use GoetasWebservices\XML\XSDReader\Schema\Item;
29
use GoetasWebservices\XML\XSDReader\Schema\Schema;
30
use GoetasWebservices\XML\XSDReader\Schema\SchemaItem;
31
use GoetasWebservices\XML\XSDReader\Schema\Type\BaseComplexType;
32
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
33
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexTypeSimpleContent;
34
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType;
35
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
36
use GoetasWebservices\XML\XSDReader\Utils\UrlUtils;
37
use RuntimeException;
38
39
class SchemaReader
40
{
41
    /**
42
     * @param string $typeName
43
     *
44
     * @return mixed[]
45
     */
46 45
    protected 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
    protected 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
    protected 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
    protected 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
    protected 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
    protected function loadSequenceChildNode(
142
        ElementContainer $elementContainer,
143
        DOMElement $node,
144
        DOMElement $childNode,
145
        $max
146
    ) {
147
        $commonMethods = [
148
            [
149 45
                ['sequence', 'choice', 'all'],
150 45
                [$this, 'loadSequenceChildNodeLoadSequence'],
151
                [
152 45
                    $elementContainer,
153 45
                    $childNode,
154 45
                    $max,
155 45
                ],
156 45
            ],
157 45
        ];
158
        $methods = [
159
            'element' => [
160 45
                [$this, 'loadSequenceChildNodeLoadElement'],
161
                [
162 45
                    $elementContainer,
163 45
                    $node,
164 45
                    $childNode,
165 45
                    $max,
166 45
                ],
167 45
            ],
168
            'group' => [
169 45
                [$this, 'loadSequenceChildNodeLoadGroup'],
170
                [
171 45
                    $elementContainer,
172 45
                    $node,
173 45
                    $childNode,
174 45
                ],
175 45
            ],
176 45
        ];
177
178 45
        $this->maybeCallCallableWithArgs($childNode, $commonMethods, $methods);
179 45
    }
180
181
    /**
182
     * @param int|null $max
183
     */
184 45
    protected function loadSequenceChildNodeLoadSequence(
185
        ElementContainer $elementContainer,
186
        DOMElement $childNode,
187
        $max
188
    ) {
189 45
        $this->loadSequence($elementContainer, $childNode, $max);
190 45
    }
191
192
    /**
193
     * @param int|null $max
194
     */
195 45
    protected function loadSequenceChildNodeLoadElement(
196
        ElementContainer $elementContainer,
197
        DOMElement $node,
198
        DOMElement $childNode,
199
        $max
200
    ) {
201 45
        if ($childNode->hasAttribute('ref')) {
202
            /**
203
             * @var ElementDef $referencedElement
204
             */
205 45
            $referencedElement = $this->findSomething('findElement', $elementContainer->getSchema(), $node, $childNode->getAttribute('ref'));
206 45
            $element = static::loadElementRef(
207 45
                $referencedElement,
208
                $childNode
209 45
            );
210 45
        } else {
211 45
            $element = $this->loadElement(
212 45
                $elementContainer->getSchema(),
213
                $childNode
214 45
            );
215
        }
216 45
        if (is_int($max) && (bool) $max) {
217 45
            $element->setMax($max);
218 45
        }
219 45
        $elementContainer->addElement($element);
220 45
    }
221
222 45
    protected function loadSequenceChildNodeLoadGroup(
223
        ElementContainer $elementContainer,
224
        DOMElement $node,
225
        DOMElement $childNode
226
    ) {
227 45
        $this->addGroupAsElement(
228 45
            $elementContainer->getSchema(),
229 45
            $node,
230 45
            $childNode,
231
            $elementContainer
232 45
        );
233 45
    }
234
235 45
    protected function addGroupAsElement(
236
        Schema $schema,
237
        DOMElement $node,
238
        DOMElement $childNode,
239
        ElementContainer $elementContainer
240
    ) {
241
        /**
242
         * @var Group
243
         */
244 45
        $referencedGroup = $this->findSomething(
245 45
            'findGroup',
246 45
            $schema,
247 45
            $node,
248 45
            $childNode->getAttribute('ref')
249 45
        );
250
251 45
        $group = $this->loadGroupRef($referencedGroup, $childNode);
252 45
        $elementContainer->addElement($group);
253 45
    }
254
255
    /**
256
     * @return Closure
257
     */
258 45
    protected function loadGroup(Schema $schema, DOMElement $node)
259
    {
260 45
        $group = static::loadGroupBeforeCheckingChildNodes(
261 45
            $schema,
262
            $node
263 45
        );
264
        static $methods = [
265
            'sequence' => 'loadSequence',
266
            'choice' => 'loadSequence',
267
            'all' => 'loadSequence',
268 45
        ];
269
270
        return function () use ($group, $node, $methods) {
271
            /**
272
             * @var string[]
273
             */
274 45
            $methods = $methods;
275 45
            $this->maybeCallMethodAgainstDOMNodeList(
276 45
                $node,
277 45
                $group,
278
                $methods
279 45
            );
280 45
        };
281
    }
282
283
    /**
284
     * @return Group|GroupRef
285
     */
286 45
    protected static function loadGroupBeforeCheckingChildNodes(
287
        Schema $schema,
288
        DOMElement $node
289
    ) {
290 45
        $group = new Group($schema, $node->getAttribute('name'));
291 45
        $group->setDoc(self::getDocumentation($node));
292
293 45
        if ($node->hasAttribute('maxOccurs')) {
294
            /**
295
             * @var GroupRef
296
             */
297
            $group = self::maybeSetMax(new GroupRef($group), $node);
298
        }
299 45
        if ($node->hasAttribute('minOccurs')) {
300
            /**
301
             * @var GroupRef
302
             */
303
            $group = self::maybeSetMin(
304
                $group instanceof GroupRef ? $group : new GroupRef($group),
305
                $node
306
            );
307
        }
308
309 45
        $schema->addGroup($group);
310
311 45
        return $group;
312
    }
313
314
    /**
315
     * @return GroupRef
316
     */
317 45
    public function loadGroupRef(Group $referenced, DOMElement $node)
318
    {
319 45
        $ref = new GroupRef($referenced);
320 45
        $ref->setDoc(self::getDocumentation($node));
321
322 45
        self::maybeSetMax($ref, $node);
323 45
        self::maybeSetMin($ref, $node);
324
325 45
        return $ref;
326
    }
327
328
    /**
329
     * @return BaseComplexType
330
     */
331 45
    protected function loadComplexTypeBeforeCallbackCallback(
332
        Schema $schema,
333
        DOMElement $node
334
    ) {
335
        /**
336
         * @var bool
337
         */
338 45
        $isSimple = false;
339
340 45
        static::againstDOMNodeList(
341 45
            $node,
342
            function (
343
                DOMElement $node,
344
                DOMElement $childNode
345
            ) use (
346
                &$isSimple
347
            ) {
348 45
                if ($isSimple) {
349 1
                    return;
350
                }
351 45
                if ($childNode->localName === 'simpleContent') {
352 2
                    $isSimple = true;
353 2
                }
354 45
            }
355 45
        );
356
357 45
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute('name')) : new ComplexType($schema, $node->getAttribute('name'));
358
359 45
        $type->setDoc(static::getDocumentation($node));
360 45
        if ($node->getAttribute('name')) {
361 45
            $schema->addType($type);
362 45
        }
363
364 45
        return $type;
365
    }
366
367
    /**
368
     * @param Closure|null $callback
369
     *
370
     * @return Closure
371
     */
372 45
    protected function loadComplexType(Schema $schema, DOMElement $node, $callback = null)
373
    {
374 45
        $type = $this->loadComplexTypeBeforeCallbackCallback($schema, $node);
375
376 45
        return $this->makeCallbackCallback(
377 45
            $type,
378 45
            $node,
379
            function (
380
                DOMElement $node,
381
                DOMElement $childNode
382
            ) use (
383 45
                $schema,
384 45
                $type
385
            ) {
386 45
                $this->loadComplexTypeFromChildNode(
387 45
                    $type,
388 45
                    $node,
389 45
                    $childNode,
390
                    $schema
391 45
                );
392 45
            },
393
            $callback
394 45
        );
395
    }
396
397 45
    protected function loadComplexTypeFromChildNode(
398
        BaseComplexType $type,
399
        DOMElement $node,
400
        DOMElement $childNode,
401
        Schema $schema
402
    ) {
403
        $commonMethods = [
404
            [
405 45
                ['sequence', 'choice', 'all'],
406 45
                [$this, 'maybeLoadSequenceFromElementContainer'],
407
                [
408 45
                    $type,
409 45
                    $childNode,
410 45
                ],
411 45
            ],
412 45
        ];
413
        $methods = [
414
            'attribute' => [
415 45
                [$this, 'addAttributeFromAttributeOrRef'],
416
                [
417 45
                    $type,
418 45
                    $childNode,
419 45
                    $schema,
420 45
                    $node,
421 45
                ],
422 45
            ],
423
            'attributeGroup' => [
424 45
                [$this, 'findSomethingLikeAttributeGroup'],
425
                [
426 45
                    $schema,
427 45
                    $node,
428 45
                    $childNode,
429 45
                    $type,
430 45
                ],
431 45
            ],
432 45
        ];
433
        if (
434
            $type instanceof ComplexType
435 45
        ) {
436 45
            $methods['group'] = [
437 45
                [$this, 'addGroupAsElement'],
438
                [
439 45
                    $schema,
440 45
                    $node,
441 45
                    $childNode,
442 45
                    $type,
443 45
                ],
444
            ];
445 45
        }
446
447 45
        $this->maybeCallCallableWithArgs($childNode, $commonMethods, $methods);
448 45
    }
449
450
    /**
451
     * @param Closure|null $callback
452
     *
453
     * @return Closure
454
     */
455 45
    protected function loadSimpleType(Schema $schema, DOMElement $node, $callback = null)
456
    {
457 45
        $type = new SimpleType($schema, $node->getAttribute('name'));
458 45
        $type->setDoc(static::getDocumentation($node));
459 45
        if ($node->getAttribute('name')) {
460 45
            $schema->addType($type);
461 45
        }
462
463 45
        return $this->makeCallbackCallback(
464 45
            $type,
465 45
            $node,
466 45
            $this->CallbackGeneratorMaybeCallMethodAgainstDOMNodeList(
467 45
                $type,
468
                [
469 45
                    'union' => 'loadUnion',
470 45
                    'list' => 'loadList',
471
                ]
472 45
            ),
473
            $callback
474 45
        );
475
    }
476
477 45
    protected function loadList(SimpleType $type, DOMElement $node)
478
    {
479 45
        if ($node->hasAttribute('itemType')) {
480
            /**
481
             * @var SimpleType
482
             */
483 45
            $listType = $this->findSomeType($type, $node, 'itemType');
484 45
            $type->setList($listType);
485 45
        } else {
486
            $addCallback = function (SimpleType $list) use ($type) {
487 45
                $type->setList($list);
488 45
            };
489
490 45
            $this->loadTypeWithCallbackOnChildNodes(
491 45
                $type->getSchema(),
492 45
                $node,
493
                $addCallback
494 45
            );
495
        }
496 45
    }
497
498 45
    protected function loadUnion(SimpleType $type, DOMElement $node)
499
    {
500 45
        if ($node->hasAttribute('memberTypes')) {
501 45
            $types = preg_split('/\s+/', $node->getAttribute('memberTypes'));
502 45
            foreach ($types as $typeName) {
503
                /**
504
                 * @var SimpleType
505
                 */
506 45
                $unionType = $this->findSomeTypeFromAttribute(
507 45
                    $type,
508 45
                    $node,
509
                    $typeName
510 45
                );
511 45
                $type->addUnion($unionType);
512 45
            }
513 45
        }
514
        $addCallback = function (SimpleType $unType) use ($type) {
515 45
            $type->addUnion($unType);
516 45
        };
517
518 45
        $this->loadTypeWithCallbackOnChildNodes(
519 45
            $type->getSchema(),
520 45
            $node,
521
            $addCallback
522 45
        );
523 45
    }
524
525 45
    protected function loadExtensionChildNodes(
526
        BaseComplexType $type,
527
        DOMElement $node
528
    ) {
529 45
        static::againstDOMNodeList(
530 45
            $node,
531
            function (
532
                DOMElement $node,
533
                DOMElement $childNode
534
            ) use (
535 45
                $type
536
            ) {
537
                $commonMethods = [
538
                    [
539 45
                        ['sequence', 'choice', 'all'],
540 45
                        [$this, 'maybeLoadSequenceFromElementContainer'],
541
                        [
542 45
                            $type,
543 45
                            $childNode,
544 45
                        ],
545 45
                    ],
546 45
                ];
547
                $methods = [
548
                    'attribute' => [
549 45
                        [$this, 'addAttributeFromAttributeOrRef'],
550
                        [
551 45
                            $type,
552 45
                            $childNode,
553 45
                            $type->getSchema(),
554 45
                            $node,
555 45
                        ],
556 45
                    ],
557
                    'attributeGroup' => [
558 45
                        [$this, 'findSomethingLikeAttributeGroup'],
559
                        [
560 45
                            $type->getSchema(),
561 45
                            $node,
562 45
                            $childNode,
563 45
                            $type,
564 45
                        ],
565 45
                    ],
566 45
                ];
567
568 45
                $this->maybeCallCallableWithArgs(
569 45
                    $childNode,
570 45
                    $commonMethods,
571
                    $methods
572 45
                );
573 45
            }
574 45
        );
575 45
    }
576
577 45
    protected function loadExtension(BaseComplexType $type, DOMElement $node)
578
    {
579 45
        $extension = new Extension();
580 45
        $type->setExtension($extension);
581
582 45
        if ($node->hasAttribute('base')) {
583 45
            $this->findAndSetSomeBase(
584 45
                $type,
585 45
                $extension,
586
                $node
587 45
            );
588 45
        }
589 45
        $this->loadExtensionChildNodes($type, $node);
590 45
    }
591
592 45
    protected function loadRestriction(Type $type, DOMElement $node)
593
    {
594 45
        $restriction = new Restriction();
595 45
        $type->setRestriction($restriction);
596 45
        if ($node->hasAttribute('base')) {
597 45
            $this->findAndSetSomeBase($type, $restriction, $node);
598 45
        } else {
599
            $addCallback = function (Type $restType) use ($restriction) {
600 45
                $restriction->setBase($restType);
601 45
            };
602
603 45
            $this->loadTypeWithCallbackOnChildNodes(
604 45
                $type->getSchema(),
605 45
                $node,
606
                $addCallback
607 45
            );
608
        }
609 45
        self::againstDOMNodeList(
610 45
            $node,
611
            function (
612
                DOMElement $node,
613
                DOMElement $childNode
614
            ) use (
615 45
                $restriction
616
            ) {
617 45
                static::maybeLoadRestrictionOnChildNode(
618 45
                    $restriction,
619
                    $childNode
620 45
                );
621 45
            }
622 45
        );
623 45
    }
624
625 45
    protected static function maybeLoadRestrictionOnChildNode(
626
        Restriction $restriction,
627
        DOMElement $childNode
628
    ) {
629
        if (
630 45
            in_array(
631 45
                $childNode->localName,
632
                [
633 45
                    'enumeration',
634 45
                    'pattern',
635 45
                    'length',
636 45
                    'minLength',
637 45
                    'maxLength',
638 45
                    'minInclusive',
639 45
                    'maxInclusive',
640 45
                    'minExclusive',
641 45
                    'maxExclusive',
642 45
                    'fractionDigits',
643 45
                    'totalDigits',
644 45
                    'whiteSpace',
645 45
                ],
646
                true
647 45
            )
648 45
        ) {
649 45
            static::definitelyLoadRestrictionOnChildNode(
650 45
                $restriction,
651
                $childNode
652 45
            );
653 45
        }
654 45
    }
655
656 45
    protected static function definitelyLoadRestrictionOnChildNode(
657
        Restriction $restriction,
658
        DOMElement $childNode
659
    ) {
660 45
        $restriction->addCheck(
661 45
            $childNode->localName,
662
            [
663 45
                'value' => $childNode->getAttribute('value'),
664 45
                'doc' => self::getDocumentation($childNode),
665
            ]
666 45
        );
667 45
    }
668
669
    /**
670
     * @return Closure
671
     */
672 45
    protected function loadElementDef(Schema $schema, DOMElement $node)
673
    {
674 45
        return $this->loadAttributeOrElementDef($schema, $node, false);
675
    }
676
677
    /**
678
     * @param bool $checkAbstract
679
     */
680 45
    protected function fillTypeNode(Type $type, DOMElement $node, $checkAbstract = false)
681
    {
682 45
        if ($checkAbstract) {
683 45
            $type->setAbstract($node->getAttribute('abstract') === 'true' || $node->getAttribute('abstract') === '1');
684 45
        }
685
        static $methods = [
686
            'restriction' => 'loadRestriction',
687
            'extension' => 'maybeLoadExtensionFromBaseComplexType',
688
            'simpleContent' => 'fillTypeNode',
689
            'complexContent' => 'fillTypeNode',
690 45
        ];
691
692
        /**
693
         * @var string[]
694
         */
695 45
        $methods = $methods;
696
697 45
        $this->maybeCallMethodAgainstDOMNodeList($node, $type, $methods);
698 45
    }
699
700 45
    protected function fillItemNonLocalType(Item $element, DOMElement $node)
701
    {
702 45
        if ($node->getAttribute('type')) {
703
            /**
704
             * @var Type
705
             */
706 45
            $type = $this->findSomeType($element, $node, 'type');
707 45
        } else {
708
            /**
709
             * @var Type
710
             */
711 45
            $type = $this->findSomeTypeFromAttribute(
712 45
                $element,
713 45
                $node,
714 45
                ($node->lookupPrefix(self::XSD_NS).':anyType')
715 45
            );
716
        }
717
718 45
        $element->setType($type);
719 45
    }
720
721
    /**
722
     * @param string $attributeName
723
     *
724
     * @return SchemaItem
725
     */
726 45
    protected function findSomeType(
727
        SchemaItem $fromThis,
728
        DOMElement $node,
729
        $attributeName
730
    ) {
731 45
        return $this->findSomeTypeFromAttribute(
732 45
            $fromThis,
733 45
            $node,
734 45
            $node->getAttribute($attributeName)
735 45
        );
736
    }
737
738
    /**
739
     * @param string $attributeName
740
     *
741
     * @return SchemaItem
742
     */
743 45
    protected function findSomeTypeFromAttribute(
744
        SchemaItem $fromThis,
745
        DOMElement $node,
746
        $attributeName
747
    ) {
748
        /**
749
         * @var SchemaItem
750
         */
751 45
        $out = $this->findSomething(
752 45
            'findType',
753 45
            $fromThis->getSchema(),
754 45
            $node,
755
            $attributeName
756 45
        );
757
758 45
        return $out;
759
    }
760
761
    /**
762
     * @param mixed[][] $commonMethods
763
     * @param mixed[][] $methods
764
     * @param mixed[][] $commonArguments
765
     *
766
     * @return mixed
767
     */
768 45
    protected function maybeCallCallableWithArgs(
769
        DOMElement $childNode,
770
        array $commonMethods = [],
771
        array $methods = [],
772
        array $commonArguments = []
773
    ) {
774 45
        foreach ($commonMethods as $commonMethodsSpec) {
775 45
            list($localNames, $callable, $args) = $commonMethodsSpec;
776
777
            /**
778
             * @var string[]
779
             */
780 45
            $localNames = $localNames;
781
782
            /**
783
             * @var callable
784
             */
785 45
            $callable = $callable;
786
787
            /**
788
             * @var mixed[]
789
             */
790 45
            $args = $args;
791
792 45
            if (in_array($childNode->localName, $localNames)) {
793 45
                return call_user_func_array($callable, $args);
794
            }
795 45
        }
796 45
        foreach ($commonArguments as $commonArgumentSpec) {
797
            /*
798
            * @var mixed[] $commonArgumentSpec
799
            */
800 45
            list($callables, $args) = $commonArgumentSpec;
801
802
            /**
803
             * @var callable[]
804
             */
805 45
            $callables = $callables;
806
807
            /**
808
             * @var mixed[]
809
             */
810 45
            $args = $args;
811
812 45
            if (isset($callables[$childNode->localName])) {
813 45
                return call_user_func_array(
814 45
                    $callables[$childNode->localName],
815
                    $args
816 45
                );
817
            }
818 45
        }
819 45
        if (isset($methods[$childNode->localName])) {
820 45
            list($callable, $args) = $methods[$childNode->localName];
821
822
            /**
823
             * @var callable
824
             */
825 45
            $callable = $callable;
826
827
            /**
828
             * @var mixed[]
829
             */
830 45
            $args = $args;
831
832 45
            return call_user_func_array($callable, $args);
833
        }
834 45
    }
835
836 45
    protected function maybeLoadSequenceFromElementContainer(
837
        BaseComplexType $type,
838
        DOMElement $childNode
839
    ) {
840 45
        $this->maybeLoadThingFromThing(
841 45
            $type,
842 45
            $childNode,
843 45
            ElementContainer::class,
844
            'loadSequence'
845 45
        );
846 45
    }
847
848
    /**
849
     * @param string $instanceof
850
     * @param string $passTo
851
     */
852 45
    protected function maybeLoadThingFromThing(
853
        Type $type,
854
        DOMElement $childNode,
855
        $instanceof,
856
        $passTo
857
    ) {
858 45
        if (!is_a($type, $instanceof, true)) {
859
            /**
860
             * @var string
861
             */
862
            $class = static::class;
863
            throw new RuntimeException(
864
                'Argument 1 passed to '.
865
                __METHOD__.
866
                ' needs to be an instance of '.
867
                $instanceof.
868
                ' when passed onto '.
869
                $class.
870
                '::'.
871
                $passTo.
872
                '(), '.
873
                (string) get_class($type).
874
                ' given.'
875
            );
876
        }
877
878 45
        $this->$passTo($type, $childNode);
879 45
    }
880
881
    /**
882
     * @param Closure|null $callback
883
     *
884
     * @return Closure
885
     */
886 45
    protected function makeCallbackCallback(
887
        Type $type,
888
        DOMElement $node,
889
        Closure $callbackCallback,
890
        $callback = null
891
    ) {
892
        return function (
893
        ) use (
894 45
            $type,
895 45
            $node,
896 45
            $callbackCallback,
897 45
            $callback
898
        ) {
899 45
            $this->runCallbackAgainstDOMNodeList(
900 45
                $type,
901 45
                $node,
902 45
                $callbackCallback,
903
                $callback
904 45
            );
905 45
        };
906
    }
907
908
    /**
909
     * @param Closure|null $callback
910
     */
911 45
    protected function runCallbackAgainstDOMNodeList(
912
        Type $type,
913
        DOMElement $node,
914
        Closure $againstNodeList,
915
        $callback = null
916
    ) {
917 45
        $this->fillTypeNode($type, $node, true);
918
919 45
        static::againstDOMNodeList($node, $againstNodeList);
920
921 45
        if ($callback) {
922 45
            call_user_func($callback, $type);
923 45
        }
924 45
    }
925
926 45
    protected function maybeLoadExtensionFromBaseComplexType(
927
        Type $type,
928
        DOMElement $childNode
929
    ) {
930 45
        $this->maybeLoadThingFromThing(
931 45
            $type,
932 45
            $childNode,
933 45
            BaseComplexType::class,
934
            'loadExtension'
935 45
        );
936 45
    }
937
938
    const XSD_NS = 'http://www.w3.org/2001/XMLSchema';
939
940
    const XML_NS = 'http://www.w3.org/XML/1998/namespace';
941
942
    /**
943
     * @var string[]
944
     */
945
    protected $knownLocationSchemas = [
946
        'http://www.w3.org/2001/xml.xsd' => (
947
            __DIR__.'/Resources/xml.xsd'
948
        ),
949
        'http://www.w3.org/2001/XMLSchema.xsd' => (
950
            __DIR__.'/Resources/XMLSchema.xsd'
951
        ),
952
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' => (
953
            __DIR__.'/Resources/oasis-200401-wss-wssecurity-secext-1.0.xsd'
954
        ),
955
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' => (
956
            __DIR__.'/Resources/oasis-200401-wss-wssecurity-utility-1.0.xsd'
957
        ),
958
        'https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
959
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
960
        ),
961
        'http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
962
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
963
        ),
964
    ];
965
966
    /**
967
     * @var string[]
968
     */
969
    protected static $globalSchemaInfo = array(
970
        self::XML_NS => 'http://www.w3.org/2001/xml.xsd',
971
        self::XSD_NS => 'http://www.w3.org/2001/XMLSchema.xsd',
972
    );
973
974
    /**
975
     * @param string $remote
976
     * @param string $local
977
     */
978
    public function addKnownSchemaLocation($remote, $local)
979
    {
980
        $this->knownLocationSchemas[$remote] = $local;
981
    }
982
983
    /**
984
     * @param string $remote
985
     *
986
     * @return bool
987
     */
988 1
    public function hasKnownSchemaLocation($remote)
989
    {
990 1
        return isset($this->knownLocationSchemas[$remote]);
991
    }
992
993
    /**
994
     * @param string $remote
995
     *
996
     * @return string
997
     */
998
    public function getKnownSchemaLocation($remote)
999
    {
1000
        return $this->knownLocationSchemas[$remote];
1001
    }
1002
1003
    /**
1004
     * @param DOMElement $node
1005
     *
1006
     * @return string
1007
     */
1008 45
    public static function getDocumentation(DOMElement $node)
1009
    {
1010 45
        $doc = '';
1011 45
        static::againstDOMNodeList(
1012 45
            $node,
1013
            function (
1014
                DOMElement $node,
1015
                DOMElement $childNode
1016
            ) use (
1017
                &$doc
1018
            ) {
1019 45
                if ($childNode->localName == 'annotation') {
1020 45
                    $doc .= static::getDocumentation($childNode);
1021 45
                } elseif ($childNode->localName == 'documentation') {
1022 45
                    $doc .= $childNode->nodeValue;
1023 45
                }
1024 45
            }
1025 45
        );
1026 45
        $doc = preg_replace('/[\t ]+/', ' ', $doc);
1027
1028 45
        return trim($doc);
1029
    }
1030
1031
    /**
1032
     * @param string[] $methods
1033
     * @param string   $key
1034
     *
1035
     * @return Closure|null
1036
     */
1037 45
    public function maybeCallMethod(
1038
        array $methods,
1039
        $key,
1040
        DOMNode $childNode,
1041
        ...$args
1042
    ) {
1043 45
        if ($childNode instanceof DOMElement && isset($methods[$key])) {
1044 45
            $method = $methods[$key];
1045
1046
            /**
1047
             * @var Closure|null
1048
             */
1049 45
            $append = $this->$method(...$args);
1050
1051 45
            if ($append instanceof Closure) {
1052 45
                return $append;
1053
            }
1054 45
        }
1055 45
    }
1056
1057
    /**
1058
     * @param Schema     $schema
1059
     * @param DOMElement $node
1060
     * @param Schema     $parent
1061
     *
1062
     * @return Closure[]
1063
     */
1064 45
    public function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null)
1065
    {
1066 45
        $schema->setSchemaThingsFromNode($node, $parent);
1067 45
        $functions = array();
1068
1069
        $thisMethods = [
1070 45
            'attributeGroup' => [$this, 'loadAttributeGroup'],
1071 45
            'include' => [$this, 'loadImport'],
1072 45
            'import' => [$this, 'loadImport'],
1073 45
            'element' => [$this, 'loadElementDef'],
1074 45
            'attribute' => [$this, 'loadAttributeDef'],
1075 45
            'group' => [$this, 'loadGroup'],
1076 45
            'complexType' => [$this, 'loadComplexType'],
1077 45
            'simpleType' => [$this, 'loadSimpleType'],
1078 45
        ];
1079
1080 45
        static::againstDOMNodeList(
1081 45
            $node,
1082
            function (
1083
                DOMElement $node,
1084
                DOMElement $childNode
1085
            ) use (
1086 45
                $schema,
1087 45
                $thisMethods,
1088
                &$functions
1089
            ) {
1090
                /**
1091
                 * @var Closure|null
1092
                 */
1093 45
                $callback = $this->maybeCallCallableWithArgs(
1094 45
                    $childNode,
1095 45
                    [],
1096 45
                    [],
1097
                    [
1098
                        [
1099 45
                            $thisMethods,
1100
                            [
1101 45
                                $schema,
1102 45
                                $childNode,
1103 45
                            ],
1104 45
                        ],
1105
                    ]
1106 45
                );
1107
1108 45
                if ($callback instanceof Closure) {
1109 45
                    $functions[] = $callback;
1110 45
                }
1111 45
            }
1112 45
        );
1113
1114 45
        return $functions;
1115
    }
1116
1117
    /**
1118
     * @return InterfaceSetMinMax
1119
     */
1120 45
    public static function maybeSetMax(InterfaceSetMinMax $ref, DOMElement $node)
1121
    {
1122
        if (
1123 45
            $node->hasAttribute('maxOccurs')
1124 45
        ) {
1125 45
            $ref->setMax($node->getAttribute('maxOccurs') == 'unbounded' ? -1 : (int) $node->getAttribute('maxOccurs'));
1126 45
        }
1127
1128 45
        return $ref;
1129
    }
1130
1131
    /**
1132
     * @return InterfaceSetMinMax
1133
     */
1134 45
    public static function maybeSetMin(InterfaceSetMinMax $ref, DOMElement $node)
1135
    {
1136 45
        if ($node->hasAttribute('minOccurs')) {
1137 45
            $ref->setMin((int) $node->getAttribute('minOccurs'));
1138 45
        }
1139
1140 45
        return $ref;
1141
    }
1142
1143 45
    public function findAndSetSomeBase(
1144
        Type $type,
1145
        Base $setBaseOnThis,
1146
        DOMElement $node
1147
    ) {
1148
        /**
1149
         * @var Type
1150
         */
1151 45
        $parent = $this->findSomeType($type, $node, 'base');
1152 45
        $setBaseOnThis->setBase($parent);
1153 45
    }
1154
1155
    /**
1156
     * @param string     $finder
1157
     * @param Schema     $schema
1158
     * @param DOMElement $node
1159
     * @param string     $typeName
1160
     *
1161
     * @throws TypeException
1162
     *
1163
     * @return ElementItem|Group|AttributeItem|AttributeGroup|Type
1164
     */
1165 45
    public function findSomething($finder, Schema $schema, DOMElement $node, $typeName)
1166
    {
1167 45
        list($name, $namespace) = static::splitParts($node, $typeName);
1168
1169
        /**
1170
         * @var string|null
1171
         */
1172 45
        $namespace = $namespace ?: $schema->getTargetNamespace();
1173
1174
        try {
1175
            /**
1176
             * @var ElementItem|Group|AttributeItem|AttributeGroup|Type
1177
             */
1178 45
            $out = $schema->$finder($name, $namespace);
1179
1180 45
            return $out;
1181
        } catch (TypeNotFoundException $e) {
1182
            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);
1183
        }
1184
    }
1185
1186 45
    public function fillItem(Item $element, DOMElement $node)
1187
    {
1188
        /**
1189
         * @var bool
1190
         */
1191 45
        $skip = false;
1192 45
        static::againstDOMNodeList(
1193 45
            $node,
1194
            function (
1195
                DOMElement $node,
1196
                DOMElement $childNode
1197
            ) use (
1198 45
                $element,
1199
                &$skip
1200
            ) {
1201
                if (
1202 45
                    !$skip &&
1203 45
                    in_array(
1204 45
                        $childNode->localName,
1205
                        [
1206 45
                            'complexType',
1207 45
                            'simpleType',
1208
                        ]
1209 45
                    )
1210 45
                ) {
1211 45
                    $this->loadTypeWithCallback(
1212 45
                        $element->getSchema(),
1213 45
                        $childNode,
1214
                        function (Type $type) use ($element) {
1215 45
                            $element->setType($type);
1216 45
                        }
1217 45
                    );
1218 45
                    $skip = true;
1219 45
                }
1220 45
            }
1221 45
        );
1222 45
        if ($skip) {
1223 45
            return;
1224
        }
1225 45
        $this->fillItemNonLocalType($element, $node);
1226 45
    }
1227
1228
    /**
1229
     * @var Schema|null
1230
     */
1231
    protected $globalSchema;
1232
1233
    /**
1234
     * @return Schema[]
1235
     */
1236 45
    protected function setupGlobalSchemas(array &$callbacks)
1237
    {
1238 45
        $globalSchemas = array();
1239 45
        foreach (self::$globalSchemaInfo as $namespace => $uri) {
1240 45
            Schema::setLoadedFile(
1241 45
                $uri,
1242 45
                $globalSchemas[$namespace] = $schema = new Schema()
1243 45
            );
1244 45
            if ($namespace === self::XSD_NS) {
1245 45
                $this->globalSchema = $schema;
1246 45
            }
1247 45
            $xml = $this->getDOM($this->knownLocationSchemas[$uri]);
1248 45
            $callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement));
1249 45
        }
1250
1251 45
        return $globalSchemas;
1252
    }
1253
1254
    /**
1255
     * @return string[]
1256
     */
1257 45
    public function getGlobalSchemaInfo()
1258
    {
1259 45
        return self::$globalSchemaInfo;
1260
    }
1261
1262
    /**
1263
     * @return Schema
1264
     */
1265 45
    public function getGlobalSchema()
1266
    {
1267 45
        if (!$this->globalSchema) {
1268 45
            $callbacks = array();
1269 45
            $globalSchemas = $this->setupGlobalSchemas($callbacks);
1270
1271 45
            $globalSchemas[static::XSD_NS]->addType(new SimpleType($globalSchemas[static::XSD_NS], 'anySimpleType'));
1272 45
            $globalSchemas[static::XSD_NS]->addType(new SimpleType($globalSchemas[static::XSD_NS], 'anyType'));
1273
1274 45
            $globalSchemas[static::XML_NS]->addSchema(
1275 45
                $globalSchemas[static::XSD_NS],
1276
                (string) static::XSD_NS
1277 45
            );
1278 45
            $globalSchemas[static::XSD_NS]->addSchema(
1279 45
                $globalSchemas[static::XML_NS],
1280
                (string) static::XML_NS
1281 45
            );
1282
1283
            /**
1284
             * @var Closure
1285
             */
1286 45
            foreach ($callbacks as $callback) {
1287 45
                $callback();
1288 45
            }
1289 45
        }
1290
1291
        /**
1292
         * @var Schema
1293
         */
1294 45
        $out = $this->globalSchema;
1295
1296 45
        return $out;
1297
    }
1298
1299
    /**
1300
     * @param DOMElement $node
1301
     * @param string     $file
1302
     *
1303
     * @return Schema
1304
     */
1305 45
    public function readNode(DOMElement $node, $file = 'schema.xsd')
1306
    {
1307 45
        $fileKey = $node->hasAttribute('targetNamespace') ? $this->getNamespaceSpecificFileIndex($file, $node->getAttribute('targetNamespace')) : $file;
1308 45
        Schema::setLoadedFile($fileKey, $rootSchema = new Schema());
1309
1310 45
        $rootSchema->addSchema($this->getGlobalSchema());
1311 45
        $callbacks = $this->schemaNode($rootSchema, $node);
1312
1313 45
        foreach ($callbacks as $callback) {
1314 39
            call_user_func($callback);
1315 45
        }
1316
1317 45
        return $rootSchema;
1318
    }
1319
1320
    /**
1321
     * It is possible that a single file contains multiple <xsd:schema/> nodes, for instance in a WSDL file.
1322
     *
1323
     * Each of these  <xsd:schema/> nodes typically target a specific namespace. Append the target namespace to the
1324
     * file to distinguish between multiple schemas in a single file.
1325
     *
1326
     * @param string $file
1327
     * @param string $targetNamespace
1328
     *
1329
     * @return string
1330
     */
1331 45
    public function getNamespaceSpecificFileIndex($file, $targetNamespace)
1332
    {
1333 45
        return $file.'#'.$targetNamespace;
1334
    }
1335
1336
    /**
1337
     * @param string $content
1338
     * @param string $file
1339
     *
1340
     * @return Schema
1341
     *
1342
     * @throws IOException
1343
     */
1344 44
    public function readString($content, $file = 'schema.xsd')
1345
    {
1346 44
        $xml = new DOMDocument('1.0', 'UTF-8');
1347 44
        if (!$xml->loadXML($content)) {
1348
            throw new IOException("Can't load the schema");
1349
        }
1350 44
        $xml->documentURI = $file;
1351
1352 44
        return $this->readNode($xml->documentElement, $file);
1353
    }
1354
1355
    /**
1356
     * @param string $file
1357
     *
1358
     * @return Schema
1359
     */
1360 1
    public function readFile($file)
1361
    {
1362 1
        $xml = $this->getDOM($file);
1363
1364 1
        return $this->readNode($xml->documentElement, $file);
1365
    }
1366
1367
    /**
1368
     * @param string $file
1369
     *
1370
     * @return DOMDocument
1371
     *
1372
     * @throws IOException
1373
     */
1374 45
    public function getDOM($file)
1375
    {
1376 45
        $xml = new DOMDocument('1.0', 'UTF-8');
1377 45
        if (!$xml->load($file)) {
1378
            throw new IOException("Can't load the file $file");
1379
        }
1380
1381 45
        return $xml;
1382
    }
1383
1384 45
    public static function againstDOMNodeList(
1385
        DOMElement $node,
1386
        Closure $againstNodeList
1387
    ) {
1388 45
        $limit = $node->childNodes->length;
1389 45
        for ($i = 0; $i < $limit; $i += 1) {
1390
            /**
1391
             * @var DOMNode
1392
             */
1393 45
            $childNode = $node->childNodes->item($i);
1394
1395 45
            if ($childNode instanceof DOMElement) {
1396 45
                $againstNodeList(
1397 45
                    $node,
1398
                    $childNode
1399 45
                );
1400 45
            }
1401 45
        }
1402 45
    }
1403
1404 45
    public function maybeCallMethodAgainstDOMNodeList(
1405
        DOMElement $node,
1406
        SchemaItem $type,
1407
        array $methods
1408
    ) {
1409 45
        static::againstDOMNodeList(
1410 45
            $node,
1411 45
            $this->CallbackGeneratorMaybeCallMethodAgainstDOMNodeList(
1412 45
                $type,
1413
                $methods
1414 45
            )
1415 45
        );
1416 45
    }
1417
1418
    /**
1419
     * @return Closure
1420
     */
1421 45
    public function CallbackGeneratorMaybeCallMethodAgainstDOMNodeList(
1422
        SchemaItem $type,
1423
        array $methods
1424
    ) {
1425
        return function (
1426
            DOMElement $node,
1427
            DOMElement $childNode
1428
        ) use (
1429 45
            $methods,
1430 45
            $type
1431
        ) {
1432
            /**
1433
             * @var string[]
1434
             */
1435 45
            $methods = $methods;
1436
1437 45
            $this->maybeCallMethod(
1438 45
                $methods,
1439 45
                $childNode->localName,
1440 45
                $childNode,
1441 45
                $type,
1442
                $childNode
1443 45
            );
1444 45
        };
1445
    }
1446
1447 45
    public function loadTypeWithCallbackOnChildNodes(
1448
        Schema $schema,
1449
        DOMElement $node,
1450
        Closure $callback
1451
    ) {
1452 45
        self::againstDOMNodeList(
1453 45
            $node,
1454
            function (
1455
                DOMElement $node,
1456
                DOMElement $childNode
1457
            ) use (
1458 45
                $schema,
1459 45
                $callback
1460
            ) {
1461 45
                $this->loadTypeWithCallback(
1462 45
                    $schema,
1463 45
                    $childNode,
1464
                    $callback
1465 45
                );
1466 45
            }
1467 45
        );
1468 45
    }
1469
1470 45
    public function loadTypeWithCallback(
1471
        Schema $schema,
1472
        DOMElement $childNode,
1473
        Closure $callback
1474
    ) {
1475
        $methods = [
1476 45
            'complexType' => 'loadComplexType',
1477 45
            'simpleType' => 'loadSimpleType',
1478 45
        ];
1479
1480
        /**
1481
         * @var Closure|null
1482
         */
1483 45
        $func = $this->maybeCallMethod(
1484 45
            $methods,
1485 45
            $childNode->localName,
1486 45
            $childNode,
1487 45
            $schema,
1488 45
            $childNode,
1489
            $callback
1490 45
        );
1491
1492 45
        if ($func instanceof Closure) {
1493 45
            call_user_func($func);
1494 45
        }
1495 45
    }
1496
1497
    /**
1498
     * @param string $file
1499
     * @param string $namespace
1500
     *
1501
     * @return Closure
1502
     */
1503 45
    public function loadImport(
1504
        Schema $schema,
1505
        DOMElement $node
1506
    ) {
1507 45
        $base = urldecode($node->ownerDocument->documentURI);
1508 45
        $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute('schemaLocation'));
1509
1510 45
        $namespace = $node->getAttribute('namespace');
1511
1512 45
        $keys = $this->loadImportFreshKeys($namespace, $file);
1513
1514
        if (
1515 45
            Schema::hasLoadedFile(...$keys)
1516 45
        ) {
1517 45
            $schema->addSchema(Schema::getLoadedFile(...$keys));
1518
1519
            return function () {
1520 45
            };
1521
        }
1522
1523 1
        return $this->loadImportFresh($namespace, $schema, $file);
1524
    }
1525
1526
    /**
1527
     * @param string $namespace
1528
     * @param string $file
1529
     *
1530
     * @return mixed[]
1531
     */
1532 45
    protected function loadImportFreshKeys(
1533
        $namespace,
1534
        $file
1535
    ) {
1536 45
        $globalSchemaInfo = $this->getGlobalSchemaInfo();
1537
1538 45
        $keys = [];
1539
1540 45
        if (isset($globalSchemaInfo[$namespace])) {
1541 45
            $keys[] = $globalSchemaInfo[$namespace];
1542 45
        }
1543
1544 45
        $keys[] = $this->getNamespaceSpecificFileIndex(
1545 45
            $file,
1546
            $namespace
1547 45
        );
1548
1549 45
        $keys[] = $file;
1550
1551 45
        return $keys;
1552
    }
1553
1554
    /**
1555
     * @param string $namespace
1556
     * @param string $file
1557
     *
1558
     * @return Schema
1559
     */
1560 1
    protected function loadImportFreshCallbacksNewSchema(
1561
        $namespace,
1562
        Schema $schema,
1563
        $file
1564
    ) {
1565
        /**
1566
         * @var Schema $newSchema
1567
         */
1568 1
        $newSchema = Schema::setLoadedFile(
1569 1
            $file,
1570 1
            ($namespace ? new Schema() : $schema)
1571 1
        );
1572
1573 1
        if ($namespace) {
1574
            $newSchema->addSchema($this->getGlobalSchema());
1575
            $schema->addSchema($newSchema);
1576
        }
1577
1578 1
        return $newSchema;
1579
    }
1580
1581
    /**
1582
     * @param string $namespace
1583
     * @param string $file
1584
     *
1585
     * @return Closure[]
1586
     */
1587 1
    protected function loadImportFreshCallbacks(
1588
        $namespace,
1589
        Schema $schema,
1590
        $file
1591
    ) {
1592
        /**
1593
         * @var string
1594
         */
1595 1
        $file = $file;
1596
1597 1
        return $this->schemaNode(
1598 1
            $this->loadImportFreshCallbacksNewSchema(
1599 1
                $namespace,
1600 1
                $schema,
1601
                $file
1602 1
            ),
1603 1
            $this->getDOM(
1604 1
                $this->hasKnownSchemaLocation($file)
1605 1
                    ? $this->getKnownSchemaLocation($file)
1606
                    : $file
1607 1
            )->documentElement,
1608
            $schema
1609 1
        );
1610
    }
1611
1612
    /**
1613
     * @param string $namespace
1614
     * @param string $file
1615
     *
1616
     * @return Closure
1617
     */
1618 1
    protected function loadImportFresh(
1619
        $namespace,
1620
        Schema $schema,
1621
        $file
1622
    ) {
1623
        return function () use ($namespace, $schema, $file) {
1624
            foreach (
1625 1
                $this->loadImportFreshCallbacks(
1626 1
                    $namespace,
1627 1
                    $schema,
1628
                    $file
1629 1
                ) as $callback
1630 1
            ) {
1631 1
                $callback();
1632 1
            }
1633 1
        };
1634
    }
1635
1636
    /**
1637
     * @return Element
1638
     */
1639 45
    public function loadElement(
1640
        Schema $schema,
1641
        DOMElement $node
1642
    ) {
1643 45
        $element = new Element($schema, $node->getAttribute('name'));
1644 45
        $element->setDoc(self::getDocumentation($node));
1645
1646 45
        $this->fillItem($element, $node);
1647
1648 45
        self::maybeSetMax($element, $node);
1649 45
        self::maybeSetMin($element, $node);
1650
1651 45
        $xp = new \DOMXPath($node->ownerDocument);
1652 45
        $xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema');
1653
1654 45
        if ($xp->query('ancestor::xs:choice', $node)->length) {
1655 45
            $element->setMin(0);
1656 45
        }
1657
1658 45
        if ($node->hasAttribute('nillable')) {
1659 3
            $element->setNil($node->getAttribute('nillable') == 'true');
1660 3
        }
1661 45
        if ($node->hasAttribute('form')) {
1662 3
            $element->setQualified($node->getAttribute('form') == 'qualified');
1663 3
        }
1664
1665 45
        return $element;
1666
    }
1667
1668
    /**
1669
     * @return ElementRef
1670
     */
1671 45
    public static function loadElementRef(
1672
        ElementDef $referenced,
1673
        DOMElement $node
1674
    ) {
1675 45
        $ref = new ElementRef($referenced);
1676 45
        $ref->setDoc(self::getDocumentation($node));
1677
1678 45
        self::maybeSetMax($ref, $node);
1679 45
        self::maybeSetMin($ref, $node);
1680 45
        if ($node->hasAttribute('nillable')) {
1681
            $ref->setNil($node->getAttribute('nillable') == 'true');
1682
        }
1683 45
        if ($node->hasAttribute('form')) {
1684
            $ref->setQualified($node->getAttribute('form') == 'qualified');
1685
        }
1686
1687 45
        return $ref;
1688
    }
1689
1690
    /**
1691
     * @return \Closure
1692
     */
1693 45
    public function loadAttributeGroup(
1694
        Schema $schema,
1695
        DOMElement $node
1696
    ) {
1697 45
        $attGroup = new AttributeGroup($schema, $node->getAttribute('name'));
1698 45
        $attGroup->setDoc(self::getDocumentation($node));
1699 45
        $schema->addAttributeGroup($attGroup);
1700
1701
        return function () use ($schema, $node, $attGroup) {
1702 45
            SchemaReader::againstDOMNodeList(
1703 45
                $node,
1704 45
                function (
1705
                    DOMElement $node,
1706
                    DOMElement $childNode
1707
                ) use (
1708 45
                    $schema,
1709 45
                    $attGroup
1710
                ) {
1711 45
                    switch ($childNode->localName) {
1712 45
                        case 'attribute':
1713 45
                            $attribute = $this->getAttributeFromAttributeOrRef(
1714 45
                                $childNode,
1715 45
                                $schema,
1716
                                $node
1717 45
                            );
1718 45
                            $attGroup->addAttribute($attribute);
1719 45
                            break;
1720 45
                        case 'attributeGroup':
1721 1
                            $this->findSomethingLikeAttributeGroup(
1722 1
                                $schema,
1723 1
                                $node,
1724 1
                                $childNode,
1725
                                $attGroup
1726 1
                            );
1727 1
                            break;
1728 45
                    }
1729 45
                }
1730 45
            );
1731 45
        };
1732
    }
1733
1734
    /**
1735
     * @return AttributeItem
1736
     */
1737 45
    public function getAttributeFromAttributeOrRef(
1738
        DOMElement $childNode,
1739
        Schema $schema,
1740
        DOMElement $node
1741
    ) {
1742 45
        if ($childNode->hasAttribute('ref')) {
1743
            /**
1744
             * @var AttributeItem
1745
             */
1746 45
            $attribute = $this->findSomething('findAttribute', $schema, $node, $childNode->getAttribute('ref'));
1747 45
        } else {
1748
            /**
1749
             * @var Attribute
1750
             */
1751 45
            $attribute = $this->loadAttribute($schema, $childNode);
1752
        }
1753
1754 45
        return $attribute;
1755
    }
1756
1757
    /**
1758
     * @return Attribute
1759
     */
1760 45
    public function loadAttribute(
1761
        Schema $schema,
1762
        DOMElement $node
1763
    ) {
1764 45
        $attribute = new Attribute($schema, $node->getAttribute('name'));
1765 45
        $attribute->setDoc(self::getDocumentation($node));
1766 45
        $this->fillItem($attribute, $node);
1767
1768 45
        if ($node->hasAttribute('nillable')) {
1769 1
            $attribute->setNil($node->getAttribute('nillable') == 'true');
1770 1
        }
1771 45
        if ($node->hasAttribute('form')) {
1772 1
            $attribute->setQualified($node->getAttribute('form') == 'qualified');
1773 1
        }
1774 45
        if ($node->hasAttribute('use')) {
1775 45
            $attribute->setUse($node->getAttribute('use'));
1776 45
        }
1777
1778 45
        return $attribute;
1779
    }
1780
1781 45
    public function addAttributeFromAttributeOrRef(
1782
        BaseComplexType $type,
1783
        DOMElement $childNode,
1784
        Schema $schema,
1785
        DOMElement $node
1786
    ) {
1787 45
        $attribute = $this->getAttributeFromAttributeOrRef(
1788 45
            $childNode,
1789 45
            $schema,
1790
            $node
1791 45
        );
1792
1793 45
        $type->addAttribute($attribute);
1794 45
    }
1795
1796 45
    public function findSomethingLikeAttributeGroup(
1797
        Schema $schema,
1798
        DOMElement $node,
1799
        DOMElement $childNode,
1800
        AttributeContainer $addToThis
1801
    ) {
1802
        /**
1803
         * @var AttributeItem
1804
         */
1805 45
        $attribute = $this->findSomething('findAttributeGroup', $schema, $node, $childNode->getAttribute('ref'));
1806 45
        $addToThis->addAttribute($attribute);
1807 45
    }
1808
}
1809