Passed
Push — static-analysis ( 7ddd7f...172fb7 )
by SignpostMarv
03:57
created

SchemaReader::readString()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 6
cp 0.8333
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 5
nc 1
nop 2
crap 2.0185
1
<?php
2
namespace GoetasWebservices\XML\XSDReader;
3
4
use Closure;
5
use DOMDocument;
6
use DOMElement;
7
use DOMNode;
8
use DOMNodeList;
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\AttributeDef;
13
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
14
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup;
15
use GoetasWebservices\XML\XSDReader\Schema\Element\Element;
16
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer;
17
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
18
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem;
19
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef;
20
use GoetasWebservices\XML\XSDReader\Schema\Element\Group;
21
use GoetasWebservices\XML\XSDReader\Schema\Element\GroupRef;
22
use GoetasWebservices\XML\XSDReader\Schema\Element\InterfaceSetMinMax;
23
use GoetasWebservices\XML\XSDReader\Schema\Exception\TypeNotFoundException;
24
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Base;
25
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Extension;
26
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Restriction;
27
use GoetasWebservices\XML\XSDReader\Schema\Item;
28
use GoetasWebservices\XML\XSDReader\Schema\Schema;
29
use GoetasWebservices\XML\XSDReader\Schema\SchemaItem;
30
use GoetasWebservices\XML\XSDReader\Schema\Type\BaseComplexType;
31
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
32
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexTypeSimpleContent;
33
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType;
34
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
35
use GoetasWebservices\XML\XSDReader\Utils\UrlUtils;
36
use RuntimeException;
37
38
class SchemaReader extends AbstractSchemaReader
39
{
40
    /**
41
    * @return Closure
42
    */
43 135
    protected function loadAttributeGroup(Schema $schema, DOMElement $node)
44
    {
45 135
        return AttributeGroup::loadAttributeGroup($this, $schema, $node);
46
    }
47
48
    /**
49
    * @param bool $attributeDef
50
    *
51
    * @return Closure
52
    */
53 135
    protected function loadAttributeOrElementDef(
54
        Schema $schema,
55
        DOMElement $node,
56
        $attributeDef
57
    ) {
58 135
        $name = $node->getAttribute('name');
59 135
        if ($attributeDef) {
60 135
            $attribute = new AttributeDef($schema, $name);
61 135
            $schema->addAttribute($attribute);
62 45
        } else {
63 135
            $attribute = new ElementDef($schema, $name);
64 135
            $schema->addElement($attribute);
65
        }
66
67
68
        return function () use ($attribute, $node) {
69 135
            $this->fillItem($attribute, $node);
70 135
        };
71
    }
72
73
    /**
74
    * @return Closure
75
    */
76 135
    protected function loadAttributeDef(Schema $schema, DOMElement $node)
77
    {
78 135
        return $this->loadAttributeOrElementDef($schema, $node, true);
79
    }
80
81 135
    protected function setSchemaThingsFromNode(
82
        Schema $schema,
83
        DOMElement $node,
84
        Schema $parent = null
85
    ) {
86 135
        $schema->setDoc(static::getDocumentation($node));
87
88 135
        if ($node->hasAttribute("targetNamespace")) {
89 135
            $schema->setTargetNamespace($node->getAttribute("targetNamespace"));
90 45
        } elseif ($parent) {
91
            $schema->setTargetNamespace($parent->getTargetNamespace());
92
        }
93 135
        $schema->setElementsQualification($node->getAttribute("elementFormDefault") == "qualified");
94 135
        $schema->setAttributesQualification($node->getAttribute("attributeFormDefault") == "qualified");
95 135
        $schema->setDoc(static::getDocumentation($node));
96 135
    }
97
98
    /**
99
     *
100
     * @param Schema $schema
101
     * @param DOMElement $node
102
     * @param Schema $parent
103
     * @return Closure[]
104
     */
105 135
    protected function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null)
106
    {
107 135
        $this->setSchemaThingsFromNode($schema, $node, $parent);
108 135
        $functions = array();
109
110 90
        static $methods = [
111
            'include' => 'loadImport',
112
            'import' => 'loadImport',
113
            'element' => 'loadElementDef',
114
            'attribute' => 'loadAttributeDef',
115
            'attributeGroup' => 'loadAttributeGroup',
116
            'group' => 'loadGroup',
117
            'complexType' => 'loadComplexType',
118
            'simpleType' => 'loadSimpleType',
119 45
        ];
120
121 135
        foreach ($node->childNodes as $childNode) {
122 135
            $callback = $this->maybeCallMethod(
123 135
                $methods,
124 135
                (string) $childNode->localName,
125 135
                $childNode,
126 135
                $schema,
127 90
                $childNode
128 45
            );
129
130 135
            if ($callback instanceof Closure) {
131 135
                $functions[] = $callback;
132 45
            }
133 45
        }
134
135 135
        return $functions;
136
    }
137
138
    /**
139
    * @param int|null $max
140
    *
141
    * @return int|null
142
    */
143 135
    protected static function loadSequenceNormaliseMax(DOMElement $node, $max)
144
    {
145
        return
146
        (
147 135
            (is_int($max) && (bool) $max) ||
148 135
            $node->getAttribute("maxOccurs") == "unbounded" ||
149 135
            $node->getAttribute("maxOccurs") > 1
150 45
        )
151 90
            ? 2
152 135
            : null;
153
    }
154
155
    /**
156
    * @param int|null $max
157
    */
158 135
    protected function loadSequence(ElementContainer $elementContainer, DOMElement $node, $max = null)
159
    {
160 135
        $max = static::loadSequenceNormaliseMax($node, $max);
161
162 135
        foreach ($node->childNodes as $childNode) {
163 135
            if ($childNode instanceof DOMElement) {
164 135
                $this->loadSequenceChildNode(
165 135
                    $elementContainer,
166 135
                    $node,
167 135
                    $childNode,
168 90
                    $max
169 45
                );
170 45
            }
171 45
        }
172 135
    }
173
174
    /**
175
    * @param int|null $max
176
    */
177 135
    protected function loadSequenceChildNode(
178
        ElementContainer $elementContainer,
179
        DOMElement $node,
180
        DOMElement $childNode,
181
        $max
182
    ) {
183
        $commonMethods = [
184
            [
185 135
                ['sequence', 'choice', 'all'],
186 135
                [$this, 'loadSequenceChildNodeLoadSequence'],
187
                [
188 135
                    $elementContainer,
189 135
                    $childNode,
190 135
                    $max,
191 45
                ],
192 45
            ],
193 45
        ];
194
        $methods = [
195
            'element' => [
196 135
                [$this, 'loadSequenceChildNodeLoadElement'],
197
                [
198 135
                    $elementContainer,
199 135
                    $node,
200 135
                    $childNode,
201 90
                    $max
202 45
                ]
203 45
            ],
204
            'group' => [
205 135
                [$this, 'loadSequenceChildNodeLoadGroup'],
206
                [
207 135
                    $elementContainer,
208 135
                    $node,
209 90
                    $childNode
210 45
                ]
211 45
            ],
212 45
        ];
213
214 135
        $this->maybeCallCallableWithArgs($childNode, $commonMethods, $methods);
215 135
    }
216
217
    /**
218
    * @param mixed[][] $methods
219
    *
220
    * @return mixed
221
    */
222 135
    protected function maybeCallCallableWithArgs(
223
        DOMElement $childNode,
224
        array $commonMethods = [],
225
        array $methods = []
226
    ) {
227 135
        foreach ($commonMethods as $commonMethodsSpec) {
228 135
            list ($localNames, $callable, $args) = $commonMethodsSpec;
229
230 135
            if (in_array($childNode->localName, $localNames)) {
231 135
                return call_user_func_array($callable, $args);
232
            }
233 45
        }
234 135
        if (isset($methods[$childNode->localName])) {
235 135
            list ($callable, $args) = $methods[$childNode->localName];
236
237 135
            return call_user_func_array($callable, $args);
238
        }
239 135
    }
240
241
    /**
242
    * @param int|null $max
243
    */
244 135
    protected function loadSequenceChildNodeLoadSequence(
245
        ElementContainer $elementContainer,
246
        DOMElement $childNode,
247
        $max
248
    ) {
249 135
        $this->loadSequence($elementContainer, $childNode, $max);
250 135
    }
251
252
    /**
253
    * @param int|null $max
254
    */
255 135
    protected function loadSequenceChildNodeLoadElement(
256
        ElementContainer $elementContainer,
257
        DOMElement $node,
258
        DOMElement $childNode,
259
        $max
260
    ) {
261 135
        if ($childNode->hasAttribute("ref")) {
262
            /**
263
            * @var ElementDef $referencedElement
264
            */
265 135
            $referencedElement = $this->findSomething('findElement', $elementContainer->getSchema(), $node, $childNode->getAttribute("ref"));
266 135
            $element = ElementRef::loadElementRef(
267 135
                $referencedElement,
268 90
                $childNode
269 45
            );
270 45
        } else {
271 135
            $element = Element::loadElement(
272 135
                $this,
273 135
                $elementContainer->getSchema(),
274 90
                $childNode
275 45
            );
276
        }
277 135
        if (is_int($max) && (bool) $max) {
278 135
            $element->setMax($max);
279 45
        }
280 135
        $elementContainer->addElement($element);
281 135
    }
282
283 135
    protected function loadSequenceChildNodeLoadGroup(
284
        ElementContainer $elementContainer,
285
        DOMElement $node,
286
        DOMElement $childNode
287
    ) {
288 135
        $this->addGroupAsElement(
289 135
            $elementContainer->getSchema(),
290 135
            $node,
291 135
            $childNode,
292 90
            $elementContainer
293 45
        );
294 135
    }
295
296 135
    protected function addGroupAsElement(
297
        Schema $schema,
298
        DOMElement $node,
299
        DOMElement $childNode,
300
        ElementContainer $elementContainer
301
    ) {
302
        /**
303
        * @var Group $referencedGroup
304
        */
305 135
        $referencedGroup = $this->findSomething(
306 135
            'findGroup',
307 135
            $schema,
308 135
            $node,
309 135
            $childNode->getAttribute("ref")
310 45
        );
311
312 135
        $group = GroupRef::loadGroupRef($referencedGroup, $childNode);
313 135
        $elementContainer->addElement($group);
314 135
    }
315
316 135
    protected function maybeLoadSequenceFromElementContainer(
317
        BaseComplexType $type,
318
        DOMElement $childNode
319
    ) {
320 135
        if (! ($type instanceof ElementContainer)) {
321
            throw new RuntimeException(
322
                '$type passed to ' .
323
                __FUNCTION__ .
324
                'expected to be an instance of ' .
325
                ElementContainer::class .
326
                ' when child node localName is "group", ' .
327
                get_class($type) .
328
                ' given.'
329
            );
330
        }
331 135
        $this->loadSequence($type, $childNode);
332 135
    }
333
334
    /**
335
    * @return Closure
336
    */
337 135
    protected function loadGroup(Schema $schema, DOMElement $node)
338
    {
339 135
        return Group::loadGroup($this, $schema, $node);
340
    }
341
342
    /**
343
    * @return BaseComplexType
344
    */
345 135
    protected function loadComplexTypeBeforeCallbackCallback(
346
        Schema $schema,
347
        DOMElement $node
348
    ) {
349 135
        $isSimple = false;
350
351 135
        foreach ($node->childNodes as $childNode) {
352 135
            if ($childNode->localName === "simpleContent") {
353 6
                $isSimple = true;
354 49
                break;
355
            }
356 45
        }
357
358 135
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute("name")) : new ComplexType($schema, $node->getAttribute("name"));
359
360 135
        $type->setDoc(static::getDocumentation($node));
361 135
        if ($node->getAttribute("name")) {
362 135
            $schema->addType($type);
363 45
        }
364
365 135
        return $type;
366
    }
367
368
    /**
369
    * @param Closure|null $callback
370
    *
371
    * @return Closure
372
    */
373 135
    protected function loadComplexType(Schema $schema, DOMElement $node, $callback = null)
374
    {
375 135
        $type = $this->loadComplexTypeBeforeCallbackCallback($schema, $node);
376
377 135
        return $this->makeCallbackCallback(
378 135
            $type,
379 135
            $node,
380
            function (
381
                DOMElement $node,
382
                DOMElement $childNode
383
            ) use(
384 135
                $schema,
385 135
                $type
386
            ) {
387 135
                $this->loadComplexTypeFromChildNode(
388 135
                    $type,
389 135
                    $node,
390 135
                    $childNode,
391 90
                    $schema
392 45
                );
393 135
            },
394 90
            $callback
395 45
        );
396
    }
397
398
    /**
399
    * @param Closure|null $callback
400
    *
401
    * @return Closure
402
    */
403 135
    protected function makeCallbackCallback(
404
        Type $type,
405
        DOMElement $node,
406
        Closure $callbackCallback,
407
        $callback = null
408
    ) {
409
        return function (
410
        ) use (
411 135
            $type,
412 135
            $node,
413 135
            $callbackCallback,
414 135
            $callback
415
        ) {
416 135
            $this->runCallbackAgainstDOMNodeList(
417 135
                $type,
418 135
                $node,
419 135
                $callbackCallback,
420 90
                $callback
421 45
            );
422 135
        };
423
    }
424
425
    /**
426
    * @param Closure|null $callback
427
    */
428 135
    protected function runCallbackAgainstDOMNodeList(
429
        Type $type,
430
        DOMElement $node,
431
        Closure $againstNodeList,
432
        $callback = null
433
    ) {
434 135
        $this->fillTypeNode($type, $node, true);
435
436 135
        foreach ($node->childNodes as $childNode) {
437 135
            if ($childNode instanceof DOMElement) {
438 135
                $againstNodeList(
439 135
                    $node,
440 90
                    $childNode
441 45
                );
442 45
            }
443 45
        }
444
445 135
        if ($callback) {
446 135
            call_user_func($callback, $type);
447 45
        }
448 135
    }
449
450 135
    protected function loadComplexTypeFromChildNode(
451
        BaseComplexType $type,
452
        DOMElement $node,
453
        DOMElement $childNode,
454
        Schema $schema
455
    ) {
456
        $commonMethods = [
457
            [
458 135
                ['sequence', 'choice', 'all'],
459 135
                [$this, 'maybeLoadSequenceFromElementContainer'],
460
                [
461 135
                    $type,
462 135
                    $childNode,
463 45
                ],
464 45
            ],
465 45
        ];
466
        $methods = [
467 90
            'attribute' => [
468 135
                [$type, 'addAttributeFromAttributeOrRef'],
469
                [
470 135
                    $this,
471 135
                    $childNode,
472 135
                    $schema,
473 90
                    $node
474 45
                ]
475 45
            ],
476
            'attributeGroup' => [
477 45
                (AttributeGroup::class . '::findSomethingLikeThis'),
478
                [
479 135
                    $this,
480 135
                    $schema,
481 135
                    $node,
482 135
                    $childNode,
483 90
                    $type
484 45
                ]
485 45
            ],
486 45
        ];
487
        if (
488 90
            $type instanceof ComplexType
489 45
        ) {
490 135
            $methods['group'] = [
491 135
                [$this, 'addGroupAsElement'],
492
                [
493 135
                    $schema,
494 135
                    $node,
495 135
                    $childNode,
496 90
                    $type
497 45
                ]
498 45
            ];
499 45
        }
500
501 135
        $this->maybeCallCallableWithArgs($childNode, $commonMethods, $methods);
502 135
    }
503
504
    /**
505
    * @param Closure|null $callback
506
    *
507
    * @return Closure
508
    */
509 135
    protected function loadSimpleType(Schema $schema, DOMElement $node, $callback = null)
510
    {
511 135
        $type = new SimpleType($schema, $node->getAttribute("name"));
512 135
        $type->setDoc(static::getDocumentation($node));
513 135
        if ($node->getAttribute("name")) {
514 135
            $schema->addType($type);
515 45
        }
516
517 90
        static $methods = [
518
            'union' => 'loadUnion',
519
            'list' => 'loadList',
520 45
        ];
521
522 135
        return $this->makeCallbackCallback(
523 135
            $type,
524 135
            $node,
525
            function (
526
                DOMElement $node,
527
                DOMElement $childNode
528
            ) use (
529 135
                $methods,
530 135
                $type
531
            ) {
532 135
                $this->maybeCallMethod(
533 135
                    $methods,
534 135
                    $childNode->localName,
535 135
                    $childNode,
536 135
                    $type,
537 90
                    $childNode
538 45
                );
539 135
            },
540 90
            $callback
541 45
        );
542
    }
543
544 135
    protected function loadList(SimpleType $type, DOMElement $node)
545
    {
546 135
        if ($node->hasAttribute("itemType")) {
547
            /**
548
            * @var SimpleType $listType
549
            */
550 135
            $listType = $this->findSomeType($type, $node, 'itemType');
551 135
            $type->setList($listType);
552 45
        } else {
553
            $addCallback = function (SimpleType $list) use ($type) {
554 135
                $type->setList($list);
555 135
            };
556
557 135
            Type::loadTypeWithCallbackOnChildNodes(
558 135
                $this,
559 135
                $type->getSchema(),
560 135
                $node,
561 90
                $addCallback
562 45
            );
563
        }
564 135
    }
565
566
    /**
567
    * @param string $attributeName
568
    *
569
    * @return SchemaItem
570
    */
571 135
    protected function findSomeType(
572
        SchemaItem $fromThis,
573
        DOMElement $node,
574
        $attributeName
575
    ) {
576 135
        return $this->findSomeTypeFromAttribute(
577 135
            $fromThis,
578 135
            $node,
579 135
            $node->getAttribute($attributeName)
580 45
        );
581
    }
582
583
    /**
584
    * @param string $attributeName
585
    *
586
    * @return SchemaItem
587
    */
588 135
    protected function findSomeTypeFromAttribute(
589
        SchemaItem $fromThis,
590
        DOMElement $node,
591
        $attributeName
592
    ) {
593
        /**
594
        * @var SchemaItem $out
595
        */
596 135
        $out = $this->findSomething(
597 135
            'findType',
598 135
            $fromThis->getSchema(),
599 135
            $node,
600 90
            $attributeName
601 45
        );
602
603 135
        return $out;
604
    }
605
606 135
    protected function loadUnion(SimpleType $type, DOMElement $node)
607
    {
608 135
        if ($node->hasAttribute("memberTypes")) {
609 135
            $types = preg_split('/\s+/', $node->getAttribute("memberTypes"));
610 135
            foreach ($types as $typeName) {
611
                /**
612
                * @var SimpleType $unionType
613
                */
614 135
                $unionType = $this->findSomeTypeFromAttribute(
615 135
                    $type,
616 135
                    $node,
617 90
                    $typeName
618 45
                );
619 135
                $type->addUnion($unionType);
620 45
            }
621 45
        }
622
        $addCallback = function (SimpleType $unType) use ($type) {
623 135
            $type->addUnion($unType);
624 135
        };
625
626 135
        Type::loadTypeWithCallbackOnChildNodes(
627 135
            $this,
628 135
            $type->getSchema(),
629 135
            $node,
630 90
            $addCallback
631 45
        );
632 135
    }
633
634
    /**
635
    * @param bool $checkAbstract
636
    */
637 135
    protected function fillTypeNode(Type $type, DOMElement $node, $checkAbstract = false)
638
    {
639
640 135
        if ($checkAbstract) {
641 135
            $type->setAbstract($node->getAttribute("abstract") === "true" || $node->getAttribute("abstract") === "1");
642 45
        }
643
644 90
        static $methods = [
645
            'restriction' => 'loadRestriction',
646
            'extension' => 'maybeLoadExtensionFromBaseComplexType',
647
            'simpleContent' => 'fillTypeNode',
648
            'complexContent' => 'fillTypeNode',
649 45
        ];
650
651 135
        foreach ($node->childNodes as $childNode) {
652 135
            $this->maybeCallMethod(
653 135
                $methods,
654 135
                (string) $childNode->localName,
655 135
                $childNode,
656 135
                $type,
657 90
                $childNode
658 45
            );
659 45
        }
660 135
    }
661
662 135
    protected function loadExtensionChildNode(
663
        BaseComplexType $type,
664
        DOMElement $node,
665
        DOMElement $childNode
666
    ) {
667
        $commonMethods = [
668
            [
669 135
                ['sequence', 'choice', 'all'],
670 135
                [$this, 'maybeLoadSequenceFromElementContainer'],
671
                [
672 135
                    $type,
673 135
                    $childNode,
674 45
                ],
675 45
            ],
676 45
        ];
677
        $methods = [
678 90
            'attribute' => [
679 135
                [$type, 'addAttributeFromAttributeOrRef'],
680
                [
681 135
                    $this,
682 135
                    $childNode,
683 135
                    $type->getSchema(),
684 90
                    $node
685 45
                ]
686 45
            ],
687
            'attributeGroup' => [
688 45
                (AttributeGroup::class . '::findSomethingLikeThis'),
689
                [
690 135
                    $this,
691 135
                    $type->getSchema(),
692 135
                    $node,
693 135
                    $childNode,
694 90
                    $type
695 45
                ]
696 45
            ],
697 45
        ];
698
699 135
        $this->maybeCallCallableWithArgs($childNode, $commonMethods, $methods);
700 135
    }
701
702 135
    protected function loadExtension(BaseComplexType $type, DOMElement $node)
703
    {
704 135
        $extension = new Extension();
705 135
        $type->setExtension($extension);
706
707 135
        if ($node->hasAttribute("base")) {
708 135
            $this->findAndSetSomeBase(
709 135
                $type,
710 135
                $extension,
711 90
                $node
712 45
            );
713 45
        }
714
715 135
        $this->loadExtensionChildNodes($type, $node->childNodes, $node);
716 135
    }
717
718 135
    protected function loadExtensionChildNodes(
719
        BaseComplexType $type,
720
        DOMNodeList $childNodes,
721
        DOMElement $node
722
    ) {
723 135
        foreach ($childNodes as $childNode) {
724 135
            if ($childNode instanceof DOMElement) {
725 135
                $this->loadExtensionChildNode(
726 135
                    $type,
727 135
                    $node,
728 90
                    $childNode
729 45
                );
730 45
            }
731 45
        }
732 135
    }
733
734 135
    protected function maybeLoadExtensionFromBaseComplexType(
735
        Type $type,
736
        DOMElement $childNode
737
    ) {
738 135
        if (! ($type instanceof BaseComplexType)) {
739
            throw new RuntimeException(
740
                'Argument 1 passed to ' .
741
                __METHOD__ .
742
                ' needs to be an instance of ' .
743
                BaseComplexType::class .
744
                ' when passed onto ' .
745
                static::class .
746
                '::loadExtension(), ' .
747
                get_class($type) .
748
                ' given.'
749
            );
750
        }
751 135
        $this->loadExtension($type, $childNode);
752 135
    }
753
754 135
    protected function loadRestriction(Type $type, DOMElement $node)
755
    {
756 135
        Restriction::loadRestriction($this, $type, $node);
757 135
    }
758
759
    /**
760
    * @param string $typeName
761
    *
762
    * @return mixed[]
763
    */
764 135
    protected static function splitParts(DOMElement $node, $typeName)
765
    {
766 135
        $prefix = null;
767 135
        $name = $typeName;
768 135
        if (strpos($typeName, ':') !== false) {
769 135
            list ($prefix, $name) = explode(':', $typeName);
770 45
        }
771
772 135
        $namespace = $node->lookupNamespaceUri($prefix ?: '');
773
        return array(
774 135
            $name,
775 135
            $namespace,
776 90
            $prefix
777 45
        );
778
    }
779
780
    /**
781
    * @return Closure
782
    */
783 135
    protected function loadElementDef(Schema $schema, DOMElement $node)
784
    {
785 135
        return $this->loadAttributeOrElementDef($schema, $node, false);
786
    }
787
788 135
    protected function fillItemNonLocalType(Item $element, DOMElement $node)
789
    {
790 135
        if ($node->getAttribute("type")) {
791
            /**
792
            * @var Type $type
793
            */
794 135
            $type = $this->findSomeType($element, $node, 'type');
795 45
        } else {
796
            /**
797
            * @var Type $type
798
            */
799 135
            $type = $this->findSomeTypeFromAttribute(
800 135
                $element,
801 135
                $node,
802 135
                ($node->lookupPrefix(self::XSD_NS) . ':anyType')
803 45
            );
804
        }
805
806 135
        $element->setType($type);
807 135
    }
808
809
    /**
810
    * @return Closure
811
    */
812 135
    protected function loadImport(Schema $schema, DOMElement $node)
813
    {
814 135
        $base = urldecode($node->ownerDocument->documentURI);
815 135
        $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute("schemaLocation"));
816
817 135
        $namespace = $node->getAttribute("namespace");
818
819
        if (
820
            (
821 135
                isset(self::$globalSchemaInfo[$namespace]) &&
822 135
                Schema::hasLoadedFile(
823 135
                    $loadedFilesKey = self::$globalSchemaInfo[$namespace]
824 45
                )
825 45
            ) ||
826 9
            Schema::hasLoadedFile(
827 9
                $loadedFilesKey = $this->getNamespaceSpecificFileIndex(
828 9
                    $file,
829 6
                    $namespace
830 3
                )
831 3
            ) ||
832 47
            Schema::hasLoadedFile($loadedFilesKey = $file)
833 45
        ) {
834 135
            $schema->addSchema(Schema::getLoadedFile($loadedFilesKey));
835
836
            return function() {
837 135
            };
838
        }
839
840 3
        return $this->loadImportFresh($schema, $node, $file, $namespace);
841
    }
842
843
    /**
844
    * @param string $file
845
    * @param string $namespace
846
    *
847
    * @return Closure
848
    */
849 3
    protected function loadImportFresh(
850
        Schema $schema,
851
        DOMElement $node,
852
        $file,
853
        $namespace
854
    ) {
855 3
        if (! $namespace) {
856 3
            $newSchema = Schema::setLoadedFile($file, $schema);
857 1
        } else {
858
            $newSchema = Schema::setLoadedFile($file, new Schema());
859
            $newSchema->addSchema($this->getGlobalSchema());
860
        }
861
862 3
        $xml = $this->getDOM(isset($this->knownLocationSchemas[$file]) ? $this->knownLocationSchemas[$file] : $file);
863
864 3
        $callbacks = $this->schemaNode($newSchema, $xml->documentElement, $schema);
865
866 3
        if ($namespace) {
867
            $schema->addSchema($newSchema);
868
        }
869
870
871 3
        return function () use ($callbacks) {
872 3
            foreach ($callbacks as $callback) {
873 3
                $callback();
874 1
            }
875 3
        };
876
    }
877
878
    /**
879
    * @return Schema[]
880
    */
881 135
    protected function setupGlobalSchemas(array & $callbacks)
882
    {
883 135
            $globalSchemas = array();
884 135
            foreach (self::$globalSchemaInfo as $namespace => $uri) {
885 135
                Schema::setLoadedFile(
886 135
                    $uri,
887 135
                    $globalSchemas[$namespace] = $schema = new Schema()
888 45
                );
889 135
                if ($namespace === self::XSD_NS) {
890 135
                    $this->globalSchema = $schema;
891 45
                }
892 135
                $xml = $this->getDOM($this->knownLocationSchemas[$uri]);
893 135
                $callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement));
894 45
            }
895
896 135
        return $globalSchemas;
897
    }
898
899
    /**
900
     * It is possible that a single file contains multiple <xsd:schema/> nodes, for instance in a WSDL file.
901
     *
902
     * Each of these  <xsd:schema/> nodes typically target a specific namespace. Append the target namespace to the
903
     * file to distinguish between multiple schemas in a single file.
904
     *
905
     * @param string $file
906
     * @param string $targetNamespace
907
     *
908
     * @return string
909
     */
910 135
    protected function getNamespaceSpecificFileIndex($file, $targetNamespace)
911
    {
912 135
        return $file . '#' . $targetNamespace;
913
    }
914
915
    /**
916
     * @param string $file
917
     *
918
     * @return DOMDocument
919
     *
920
     * @throws IOException
921
     */
922 135
    protected function getDOM($file)
923
    {
924 135
        $xml = new DOMDocument('1.0', 'UTF-8');
925 135
        if (!$xml->load($file)) {
926
            throw new IOException("Can't load the file $file");
927
        }
928 135
        return $xml;
929
    }
930
}
931