Passed
Pull Request — master (#48)
by Carsten
09:19
created

SchemaReader::loadRedefine()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 25
ccs 12
cts 12
cp 1
rs 9.8666
c 0
b 0
f 0
cc 4
nc 3
nop 2
crap 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GoetasWebservices\XML\XSDReader;
6
7
use Closure;
8
use DOMDocument;
9
use DOMElement;
10
use DOMNode;
11
use GoetasWebservices\XML\XSDReader\Documentation\DocumentationReader;
12
use GoetasWebservices\XML\XSDReader\Documentation\StandardDocumentationReader;
13
use GoetasWebservices\XML\XSDReader\Exception\IOException;
14
use GoetasWebservices\XML\XSDReader\Exception\TypeException;
15
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Attribute;
16
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeContainer;
17
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeDef;
18
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
19
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup;
20
use GoetasWebservices\XML\XSDReader\Schema\Element\Element;
21
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer;
22
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
23
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef;
24
use GoetasWebservices\XML\XSDReader\Schema\Element\Group;
25
use GoetasWebservices\XML\XSDReader\Schema\Element\GroupRef;
26
use GoetasWebservices\XML\XSDReader\Schema\Element\InterfaceSetMinMax;
27
use GoetasWebservices\XML\XSDReader\Schema\Element\InterfaceSetDefault;
28
use GoetasWebservices\XML\XSDReader\Schema\Exception\TypeNotFoundException;
29
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Base;
30
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Extension;
31
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Restriction;
32
use GoetasWebservices\XML\XSDReader\Schema\Item;
33
use GoetasWebservices\XML\XSDReader\Schema\Schema;
34
use GoetasWebservices\XML\XSDReader\Schema\SchemaItem;
35
use GoetasWebservices\XML\XSDReader\Schema\Type\BaseComplexType;
36
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
37
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexTypeSimpleContent;
38
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType;
39
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
40
use GoetasWebservices\XML\XSDReader\Utils\UrlUtils;
41
42
class SchemaReader
43
{
44
    public const XSD_NS = 'http://www.w3.org/2001/XMLSchema';
45
46
    public const XML_NS = 'http://www.w3.org/XML/1998/namespace';
47
48
    /**
49
     * @var DocumentationReader
50
     */
51
    private $documentationReader;
52
53
    /**
54
     * @var Schema[]
55
     */
56
    private $loadedFiles = array();
57
58
    /**
59
     * @var Schema[][]
60
     */
61
    private $loadedSchemas = array();
62
63
    /**
64
     * @var string[]
65
     */
66
    protected $knownLocationSchemas = [
67
        'http://www.w3.org/2001/xml.xsd' => (
68
            __DIR__.'/Resources/xml.xsd'
69
        ),
70
        'http://www.w3.org/2001/XMLSchema.xsd' => (
71
            __DIR__.'/Resources/XMLSchema.xsd'
72
        ),
73
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' => (
74
            __DIR__.'/Resources/oasis-200401-wss-wssecurity-secext-1.0.xsd'
75
        ),
76
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' => (
77
            __DIR__.'/Resources/oasis-200401-wss-wssecurity-utility-1.0.xsd'
78
        ),
79
        'https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
80
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
81
        ),
82
        'http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
83
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
84
        ),
85
    ];
86
87
    /**
88
     * @var string[]
89
     */
90
    protected $knownNamespaceSchemaLocations = [];
91
92
    /**
93
     * @var string[]
94
     */
95
    protected static $globalSchemaInfo = array(
96
        self::XML_NS => 'http://www.w3.org/2001/xml.xsd',
97
        self::XSD_NS => 'http://www.w3.org/2001/XMLSchema.xsd',
98
    );
99
100 2
    private function extractErrorMessage(): \Exception
101
    {
102 2
        $errors = array();
103
104 2
        foreach (libxml_get_errors() as $error) {
105 1
            $errors[] = sprintf("Error[%s] code %s: %s in '%s' at position %s:%s", $error->level, $error->code, trim($error->message), $error->file, $error->line, $error->column);
106
        }
107 2
        $e = new \Exception(implode('; ', $errors));
108 2
        libxml_use_internal_errors(false);
109
110 2
        return $e;
111
    }
112
113 75
    public function __construct(DocumentationReader $documentationReader = null)
114
    {
115 75
        if (null === $documentationReader) {
116 75
            $documentationReader = new StandardDocumentationReader();
117
        }
118 75
        $this->documentationReader = $documentationReader;
119 75
    }
120
121
    /**
122
     * Override remote location with a local file.
123
     *
124
     * @param string $remote remote schema URL
125
     * @param string $local  local file path
126
     */
127 1
    public function addKnownSchemaLocation(string $remote, string $local): void
128
    {
129 1
        $this->knownLocationSchemas[$remote] = $local;
130 1
    }
131
132
    /**
133
     * Specify schema location by namespace.
134
     * This can be used for schemas which import namespaces but do not specify schemaLocation attributes.
135
     *
136
     * @param string $namespace namespace
137
     * @param string $location  schema URL
138
     */
139 1
    public function addKnownNamespaceSchemaLocation(string $namespace, string $location): void
140
    {
141 1
        $this->knownNamespaceSchemaLocations[$namespace] = $location;
142 1
    }
143
144 64
    private function loadAttributeGroup(
145
        Schema $schema,
146
        DOMElement $node
147
    ): Closure {
148 64
        $attGroup = new AttributeGroup($schema, $node->getAttribute('name'));
149 64
        $attGroup->setDoc($this->getDocumentation($node));
150 64
        $schema->addAttributeGroup($attGroup);
151
152 64
        return function () use ($schema, $node, $attGroup): void {
153 64
            SchemaReader::againstDOMNodeList(
154 64
                $node,
155 64
                function (
156
                    DOMElement $node,
157
                    DOMElement $childNode
158
                ) use (
159 64
                    $schema,
160 64
                    $attGroup
161
                ): void {
162 64
                    switch ($childNode->localName) {
163 64
                        case 'attribute':
164 64
                            $attribute = $this->getAttributeFromAttributeOrRef(
165 64
                                $childNode,
166 64
                                $schema,
167 64
                                $node
168
                            );
169 64
                            $attGroup->addAttribute($attribute);
170 64
                            break;
171 64
                        case 'attributeGroup':
172 1
                            $this->findSomethingLikeAttributeGroup(
173 1
                                $schema,
174 1
                                $node,
175 1
                                $childNode,
176 1
                                $attGroup
177
                            );
178 1
                            break;
179
                    }
180 64
                }
181
            );
182 64
        };
183
    }
184
185 64
    private function getAttributeFromAttributeOrRef(
186
        DOMElement $childNode,
187
        Schema $schema,
188
        DOMElement $node
189
    ): AttributeItem {
190 64
        if ($childNode->hasAttribute('ref')) {
191 64
            $attribute = $this->findAttributeItem($schema, $node, $childNode->getAttribute('ref'));
192
        } else {
193
            /**
194
             * @var Attribute
195
             */
196 64
            $attribute = $this->loadAttribute($schema, $childNode);
197
        }
198
199 64
        return $attribute;
200
    }
201
202 64
    private function loadAttribute(
203
        Schema $schema,
204
        DOMElement $node
205
    ): Attribute {
206 64
        $attribute = new Attribute($schema, $node->getAttribute('name'));
207 64
        $attribute->setDoc($this->getDocumentation($node));
208 64
        $this->fillItem($attribute, $node);
209
210 64
        if ($node->hasAttribute('nillable')) {
211 1
            $attribute->setNil($node->getAttribute('nillable') == 'true');
212
        }
213 64
        if ($node->hasAttribute('form')) {
214 1
            $attribute->setQualified($node->getAttribute('form') == 'qualified');
215
        }
216 64
        if ($node->hasAttribute('use')) {
217 64
            $attribute->setUse($node->getAttribute('use'));
218
        }
219
220 64
        return $attribute;
221
    }
222
223 64
    private function loadAttributeOrElementDef(
224
        Schema $schema,
225
        DOMElement $node,
226
        bool $attributeDef
227
    ): Closure {
228 64
        $name = $node->getAttribute('name');
229 64
        if ($attributeDef) {
230 64
            $attribute = new AttributeDef($schema, $name);
231 64
            $schema->addAttribute($attribute);
232
        } else {
233 64
            $attribute = new ElementDef($schema, $name);
234 64
            $attribute->setDoc($this->getDocumentation($node));
235 64
            $schema->addElement($attribute);
236
        }
237
238 64
        return function () use ($attribute, $node): void {
239 64
            $this->fillItem($attribute, $node);
240 64
        };
241
    }
242
243 64
    private function loadAttributeDef(Schema $schema, DOMElement $node): Closure
244
    {
245 64
        return $this->loadAttributeOrElementDef($schema, $node, true);
246
    }
247
248 64
    private function getDocumentation(DOMElement $node): string
249
    {
250 64
        return $this->documentationReader->get($node);
251
    }
252
253
    /**
254
     * @return Closure[]
255
     */
256 64
    private function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null): array
257
    {
258 64
        $this->setSchemaThingsFromNode($schema, $node, $parent);
259 64
        $functions = array();
260
261 64
        self::againstDOMNodeList(
262 64
            $node,
263 64
            function (
264
                DOMElement $node,
265
                DOMElement $childNode
266
            ) use (
267 64
                $schema,
268 64
                &$functions
269
            ): void {
270 64
                $callback = null;
271
272 64
                switch ($childNode->localName) {
273 64
                    case 'attributeGroup':
274 64
                        $callback = $this->loadAttributeGroup($schema, $childNode);
275 64
                        break;
276 64
                    case 'include':
277 64
                    case 'import':
278 64
                        $callback = $this->loadImport($schema, $childNode);
279 64
                        break;
280 64
                    case 'redefine':
281 64
                        $callback = $this->loadRedefine($schema, $childNode);
282 64
                        break;
283 64
                    case 'element':
284 64
                        $callback = $this->loadElementDef($schema, $childNode);
285 64
                        break;
286 64
                    case 'attribute':
287 64
                        $callback = $this->loadAttributeDef($schema, $childNode);
288 64
                        break;
289 64
                    case 'group':
290 64
                        $callback = $this->loadGroup($schema, $childNode);
291 64
                        break;
292 64
                    case 'complexType':
293 64
                        $callback = $this->loadComplexType($schema, $childNode);
294 64
                        break;
295
                    case 'simpleType':
296
                        $callback = $this->loadSimpleType($schema, $childNode);
297 64
                        break;
298 64
                }
299
300 64
                if ($callback instanceof Closure) {
301
                    $functions[] = $callback;
302
                }
303 64
            }
304
        );
305
306 64
        return $functions;
307
    }
308 64
309 64
    private function loadGroupRef(Group $referenced, DOMElement $node): GroupRef
310
    {
311 64
        $ref = new GroupRef($referenced);
312 64
        $ref->setDoc($this->getDocumentation($node));
313
314 64
        self::maybeSetMax($ref, $node);
315
        self::maybeSetMin($ref, $node);
316
317 64
        return $ref;
318
    }
319 64
320 64
    private static function maybeSetMax(InterfaceSetMinMax $ref, DOMElement $node): void
321
    {
322 64
        if ($node->hasAttribute('maxOccurs')) {
323
            $ref->setMax($node->getAttribute('maxOccurs') == 'unbounded' ? -1 : (int) $node->getAttribute('maxOccurs'));
324 64
        }
325
    }
326 64
327 64
    private static function maybeSetMin(InterfaceSetMinMax $ref, DOMElement $node): void
328 64
    {
329 7
        if ($node->hasAttribute('minOccurs')) {
330
            $ref->setMin((int) $node->getAttribute('minOccurs'));
331
            if ($ref->getMin() > $ref->getMax() && $ref->getMax() !== -1) {
332 64
                $ref->setMax($ref->getMin());
333
            }
334 64
        }
335
    }
336 64
337 1
    private static function maybeSetDefault(InterfaceSetDefault $ref, DOMElement $node): void
338
    {
339 64
        if ($node->hasAttribute('default')) {
340
            $ref->setDefault($node->getAttribute('default'));
341 64
        }
342
    }
343
344
    private function loadSequence(ElementContainer $elementContainer, DOMElement $node, int $max = null): void
345 64
    {
346 64
        $max =
347 64
            (
348
                (is_int($max) && (bool) $max) ||
349 64
                $node->getAttribute('maxOccurs') == 'unbounded' ||
350 64
                $node->getAttribute('maxOccurs') > 1
351
            )
352 64
                ? 2
353 64
                : null;
354 64
355
        self::againstDOMNodeList(
356
            $node,
357
            function (
358 64
                DOMElement $node,
359 64
                DOMElement $childNode
360
            ) use (
361 64
                $elementContainer,
362 64
                $max
363 64
            ): void {
364 64
                $this->loadSequenceChildNode(
365 64
                    $elementContainer,
366
                    $node,
367 64
                    $childNode,
368
                    $max
369 64
                );
370
            }
371 64
        );
372
    }
373
374
    private function loadSequenceChildNode(
375
        ElementContainer $elementContainer,
376
        DOMElement $node,
377 64
        DOMElement $childNode,
378 64
        ? int $max
379 64
    ): void {
380 64
        switch ($childNode->localName) {
381 64
            case 'sequence':
382 64
            case 'choice':
383 64
            case 'all':
384 64
                $this->loadSequence(
385
                    $elementContainer,
386 64
                    $childNode,
387 64
                    $max
388 64
                );
389 64
                break;
390 64
            case 'element':
391 64
                $this->loadSequenceChildNodeLoadElement(
392 64
                    $elementContainer,
393
                    $node,
394 64
                    $childNode,
395 64
                    $max
396 64
                );
397 64
                break;
398 64
            case 'group':
399 64
                $this->addGroupAsElement(
400 64
                    $elementContainer->getSchema(),
401
                    $node,
402 64
                    $childNode,
403
                    $elementContainer
404 64
                );
405
                break;
406 64
        }
407
    }
408
409
    private function loadSequenceChildNodeLoadElement(
410
        ElementContainer $elementContainer,
411
        DOMElement $node,
412 64
        DOMElement $childNode,
413 64
        ? int $max
414 64
    ): void {
415
        if ($childNode->hasAttribute('ref')) {
416 64
            $element = new ElementRef(
417
                $this->findElement($elementContainer->getSchema(), $node, $childNode->getAttribute('ref'))
418 64
            );
419 64
            $element->setDoc($this->getDocumentation($childNode));
420 64
421
            self::maybeSetMax($element, $childNode);
422
            self::maybeSetMin($element, $childNode);
423 64
            if ($childNode->hasAttribute('nillable')) {
424 64
                $element->setNil($childNode->getAttribute('nillable') == 'true');
425
            }
426
            if ($childNode->hasAttribute('form')) {
427 64
                $element->setQualified($childNode->getAttribute('form') == 'qualified');
428 64
            }
429 64
        } else {
430
            $element = $this->loadElement(
431
                $elementContainer->getSchema(),
432 64
                $childNode
433
            );
434
        }
435
        if ($max > 1) {
436
            /*
437
            * although one might think the typecast is not needed with $max being `? int $max` after passing > 1,
438 64
            * phpstan@a4f89fa still thinks it's possibly null.
439
            * see https://github.com/phpstan/phpstan/issues/577 for related issue
440 64
            */
441 64
            $element->setMax((int) $max);
442
        }
443 64
        $elementContainer->addElement($element);
444
    }
445
446
    private function addGroupAsElement(
447
        Schema $schema,
448
        DOMElement $node,
449 64
        DOMElement $childNode,
450 64
        ElementContainer $elementContainer
451 64
    ): void {
452 64
        $referencedGroup = $this->findGroup(
453
            $schema,
454
            $node,
455 64
            $childNode->getAttribute('ref')
456 64
        );
457 64
458
        $group = $this->loadGroupRef($referencedGroup, $childNode);
459 64
        $elementContainer->addElement($group);
460
    }
461 64
462 64
    private function loadGroup(Schema $schema, DOMElement $node): Closure
463 64
    {
464
        $group = new Group($schema, $node->getAttribute('name'));
465 64
        $group->setDoc($this->getDocumentation($node));
466 1
        $groupOriginal = $group;
467
468 1
        if ($node->hasAttribute('maxOccurs') || $node->hasAttribute('maxOccurs')) {
469 1
            $group = new GroupRef($group);
470
471 1
            if ($node->hasAttribute('maxOccurs')) {
472 1
                self::maybeSetMax($group, $node);
473
            }
474
            if ($node->hasAttribute('minOccurs')) {
475
                self::maybeSetMin($group, $node);
476 64
            }
477
        }
478 64
479 64
        $schema->addGroup($group);
480 64
481 64
        return function () use ($groupOriginal, $node): void {
482 64
            static::againstDOMNodeList(
483 64
                $node,
484 64
                function (DOMelement $node, DOMElement $childNode) use ($groupOriginal): void {
485 64
                    switch ($childNode->localName) {
486 64
                        case 'sequence':
487 64
                        case 'choice':
488
                        case 'all':
489 64
                            $this->loadSequence($groupOriginal, $childNode);
490
                            break;
491 64
                    }
492
                }
493
            );
494 64
        };
495
    }
496
497
    private function loadComplexType(Schema $schema, DOMElement $node, Closure $callback = null): Closure
498
    {
499 64
        /**
500
         * @var bool
501 64
         */
502 64
        $isSimple = false;
503 64
504
        self::againstDOMNodeList(
505
            $node,
506
            function (
507 64
                DOMElement $node,
508
                DOMElement $childNode
509 64
            ) use (
510 1
                &$isSimple
511
            ): void {
512 64
                if ($isSimple) {
513 2
                    return;
514
                }
515 64
                if ($childNode->localName === 'simpleContent') {
516
                    $isSimple = true;
517
                }
518 64
            }
519
        );
520 64
521 64
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute('name')) : new ComplexType($schema, $node->getAttribute('name'));
522 64
523
        $type->setDoc($this->getDocumentation($node));
524
        if ($node->getAttribute('name')) {
525 64
            $schema->addType($type);
526 64
        }
527
528 64
        return function () use ($type, $node, $schema, $callback): void {
529 64
            $this->fillTypeNode($type, $node, true);
530 64
531
            self::againstDOMNodeList(
532
                $node,
533
                function (
534 64
                    DOMElement $node,
535 64
                    DOMElement $childNode
536
                ) use (
537 64
                    $schema,
538 64
                    $type
539 64
                ): void {
540 64
                    $this->loadComplexTypeFromChildNode(
541 64
                        $type,
542
                        $node,
543 64
                        $childNode,
544
                        $schema
545
                    );
546 64
                }
547 64
            );
548
549 64
            if ($callback instanceof Closure) {
550
                call_user_func($callback, $type);
551
            }
552 64
        };
553
    }
554
555
    private function loadComplexTypeFromChildNode(
556
        BaseComplexType $type,
557
        DOMElement $node,
558 64
        DOMElement $childNode,
559 64
        Schema $schema
560 64
    ): void {
561 64
        switch ($childNode->localName) {
562 64
            case 'sequence':
563 64
            case 'choice':
564 64
            case 'all':
565 64
                if ($type instanceof ElementContainer) {
566
                    $this->loadSequence(
567
                        $type,
568 64
                        $childNode
569 64
                    );
570 64
                }
571 64
                break;
572 64
            case 'attribute':
573 64
                $this->addAttributeFromAttributeOrRef(
574 64
                    $type,
575
                    $childNode,
576 64
                    $schema,
577 64
                    $node
578 2
                );
579 2
                break;
580 2
            case 'attributeGroup':
581 2
                $this->findSomethingLikeAttributeGroup(
582 2
                    $schema,
583
                    $node,
584 2
                    $childNode,
585 64
                    $type
586
                );
587 1
                break;
588
            case 'group':
589 1
                if (
590 1
                    $type instanceof ComplexType
591 1
                ) {
592 1
                    $this->addGroupAsElement(
593 1
                        $schema,
594
                        $node,
595
                        $childNode,
596 1
                        $type
597
                    );
598 64
                }
599
                break;
600 64
        }
601
    }
602 64
603 64
    private function loadSimpleType(Schema $schema, DOMElement $node, Closure $callback = null): Closure
604 64
    {
605 64
        $type = new SimpleType($schema, $node->getAttribute('name'));
606
        $type->setDoc($this->getDocumentation($node));
607
        if ($node->getAttribute('name')) {
608 64
            $schema->addType($type);
609 64
        }
610
611 64
        return function () use ($type, $node, $callback): void {
612 64
            $this->fillTypeNode($type, $node, true);
613 64
614 64
            self::againstDOMNodeList(
615 64
                $node,
616 64
                function (DOMElement $node, DOMElement $childNode) use ($type): void {
617 64
                    switch ($childNode->localName) {
618 64
                        case 'union':
619 64
                            $this->loadUnion($type, $childNode);
620 64
                            break;
621
                        case 'list':
622 64
                            $this->loadList($type, $childNode);
623
                            break;
624
                    }
625 64
                }
626 64
            );
627
628 64
            if ($callback instanceof Closure) {
629
                call_user_func($callback, $type);
630
            }
631 64
        };
632
    }
633 64
634
    private function loadList(SimpleType $type, DOMElement $node): void
635
    {
636
        if ($node->hasAttribute('itemType')) {
637 64
            /**
638 64
             * @var SimpleType
639
             */
640 64
            $listType = $this->findSomeType($type, $node, 'itemType');
641 64
            $type->setList($listType);
642 64
        } else {
643
            self::againstDOMNodeList(
644
                $node,
645
                function (
646 64
                    DOMElement $node,
647
                    DOMElement $childNode
648 64
                ) use (
649 64
                    $type
650 64
                ): void {
651 64
                    $this->loadTypeWithCallback(
652 64
                        $type->getSchema(),
653 64
                        $childNode,
654
                        function (SimpleType $list) use ($type): void {
655 64
                            $type->setList($list);
656
                        }
657
                    );
658 64
                }
659
            );
660 64
        }
661
    }
662
663
    private function findSomeType(
664
        SchemaItem $fromThis,
665 64
        DOMElement $node,
666 64
        string $attributeName
667 64
    ): SchemaItem {
668 64
        return $this->findSomeTypeFromAttribute(
669
            $fromThis,
670
            $node,
671
            $node->getAttribute($attributeName)
672 64
        );
673
    }
674
675
    private function findSomeTypeFromAttribute(
676
        SchemaItem $fromThis,
677 64
        DOMElement $node,
678 64
        string $attributeName
679 64
    ): SchemaItem {
680 64
        $out = $this->findType(
681
            $fromThis->getSchema(),
682
            $node,
683 64
            $attributeName
684
        );
685
686 64
        return $out;
687
    }
688 64
689 64
    private function loadUnion(SimpleType $type, DOMElement $node): void
690 64
    {
691
        if ($node->hasAttribute('memberTypes')) {
692
            $types = preg_split('/\s+/', $node->getAttribute('memberTypes'));
693
            foreach ($types as $typeName) {
694 64
                /**
695 64
                 * @var SimpleType
696 64
                 */
697 64
                $unionType = $this->findSomeTypeFromAttribute(
698
                    $type,
699 64
                    $node,
700
                    $typeName
701
                );
702 64
                $type->addUnion($unionType);
703 64
            }
704 64
        }
705
        self::againstDOMNodeList(
706
            $node,
707
            function (
708 64
                DOMElement $node,
709
                DOMElement $childNode
710 64
            ) use (
711 64
                $type
712 64
            ): void {
713 64
                $this->loadTypeWithCallback(
714 64
                    $type->getSchema(),
715 64
                    $childNode,
716
                    function (SimpleType $unType) use ($type): void {
717 64
                        $type->addUnion($unType);
718
                    }
719 64
                );
720
            }
721 64
        );
722
    }
723 64
724 64
    private function fillTypeNode(Type $type, DOMElement $node, bool $checkAbstract = false): void
725
    {
726
        if ($checkAbstract) {
727 64
            $type->setAbstract($node->getAttribute('abstract') === 'true' || $node->getAttribute('abstract') === '1');
728 64
        }
729 64
730 64
        self::againstDOMNodeList(
731 64
            $node,
732 64
            function (DOMElement $node, DOMElement $childNode) use ($type): void {
733 64
                switch ($childNode->localName) {
734 64
                    case 'restriction':
735 64
                        $this->loadRestriction($type, $childNode);
736 64
                        break;
737
                    case 'extension':
738 64
                        if ($type instanceof BaseComplexType) {
739 64
                            $this->loadExtension($type, $childNode);
740 64
                        }
741 64
                        break;
742 64
                    case 'simpleContent':
743
                    case 'complexContent':
744 64
                        $this->fillTypeNode($type, $childNode);
745
                        break;
746 64
                }
747
            }
748 64
        );
749
    }
750 64
751 64
    private function loadExtension(BaseComplexType $type, DOMElement $node): void
752
    {
753 64
        $extension = new Extension();
754 64
        $type->setExtension($extension);
755 64
756 64
        if ($node->hasAttribute('base')) {
757 64
            $this->findAndSetSomeBase(
758
                $type,
759
                $extension,
760 64
                $node
761 64
            );
762
        }
763 64
        $this->loadExtensionChildNodes($type, $node);
764
    }
765
766
    private function findAndSetSomeBase(
767
        Type $type,
768
        Base $setBaseOnThis,
769
        DOMElement $node
770
    ): void {
771 64
        /**
772 64
         * @var Type
773 64
         */
774
        $parent = $this->findSomeType($type, $node, 'base');
775 64
        $setBaseOnThis->setBase($parent);
776
    }
777
778
    private function loadExtensionChildNodes(
779 64
        BaseComplexType $type,
780 64
        DOMElement $node
781 64
    ): void {
782
        self::againstDOMNodeList(
783
            $node,
784
            function (
785 64
                DOMElement $node,
786
                DOMElement $childNode
787 64
            ) use (
788 64
                $type
789 64
            ): void {
790 64
                switch ($childNode->localName) {
791 64
                    case 'sequence':
792 64
                    case 'choice':
793 64
                    case 'all':
794 64
                        if ($type instanceof ElementContainer) {
795
                            $this->loadSequence(
796
                                $type,
797 64
                                $childNode
798 64
                            );
799 64
                        }
800 64
                        break;
801 64
                    case 'attribute':
802 64
                        $this->addAttributeFromAttributeOrRef(
803 64
                            $type,
804
                            $childNode,
805 64
                            $type->getSchema(),
806 64
                            $node
807 64
                        );
808 64
                        break;
809 64
                    case 'attributeGroup':
810 64
                        $this->findSomethingLikeAttributeGroup(
811 64
                            $type->getSchema(),
812
                            $node,
813 64
                            $childNode,
814
                            $type
815 64
                        );
816
                        break;
817 64
                }
818
            }
819 64
        );
820
    }
821 64
822 64
    private function loadRestriction(Type $type, DOMElement $node): void
823 64
    {
824 64
        $restriction = new Restriction();
825
        $type->setRestriction($restriction);
826 64
        if ($node->hasAttribute('base')) {
827 64
            $this->findAndSetSomeBase($type, $restriction, $node);
828 64
        } else {
829
            self::againstDOMNodeList(
830
                $node,
831
                function (
832 64
                    DOMElement $node,
833 64
                    DOMElement $childNode
834
                ) use (
835 64
                    $type,
836 64
                    $restriction
837 64
                ): void {
838 64
                    $this->loadTypeWithCallback(
839 64
                        $type->getSchema(),
840 64
                        $childNode,
841
                        function (Type $restType) use ($restriction): void {
842 64
                            $restriction->setBase($restType);
843
                        }
844
                    );
845 64
                }
846 64
            );
847 64
        }
848
        self::againstDOMNodeList(
849
            $node,
850
            function (
851 64
                DOMElement $node,
852
                DOMElement $childNode
853
            ) use (
854 64
                $restriction
855 64
            ): void {
856
                if (
857 64
                in_array(
858
                    $childNode->localName,
859
                    [
860
                        'enumeration',
861
                        'pattern',
862
                        'length',
863
                        'minLength',
864
                        'maxLength',
865
                        'minInclusive',
866
                        'maxInclusive',
867
                        'minExclusive',
868
                        'maxExclusive',
869
                        'fractionDigits',
870 64
                        'totalDigits',
871
                        'whiteSpace',
872
                    ],
873 64
                    true
874 64
                )
875
                ) {
876 64
                    $restriction->addCheck(
877 64
                        $childNode->localName,
878
                        [
879
                            'value' => $childNode->getAttribute('value'),
880
                            'doc' => $this->getDocumentation($childNode),
881 64
                        ]
882
                    );
883 64
                }
884
            }
885
        );
886
    }
887
888 64
    /**
889
     * @return mixed[]
890 64
     */
891 64
    private static function splitParts(DOMElement $node, string $typeName): array
892 64
    {
893 64
        $prefix = null;
894
        $name = $typeName;
895
        if (strpos($typeName, ':') !== false) {
896
            list($prefix, $name) = explode(':', $typeName);
897
        }
898
899 64
        /**
900
         * @psalm-suppress PossiblyNullArgument
901
         */
902 64
        $namespace = $node->lookupNamespaceUri($prefix);
903 64
904 64
        return array(
905
            $name,
906
            $namespace,
907
            $prefix,
908 64
        );
909
    }
910 64
911
    private function findAttributeItem(Schema $schema, DOMElement $node, string $typeName): AttributeItem
912
    {
913
        list($name, $namespace) = static::splitParts($node, $typeName);
914
915 64
        /**
916
         * @var string|null $namespace
917
         */
918
        $namespace = $namespace ?: $schema->getTargetNamespace();
919
920
        try {
921 64
            /**
922
             * @var AttributeItem $out
923 64
             */
924
            $out = $schema->findAttribute((string) $name, $namespace);
925
926
            return $out;
927
        } catch (TypeNotFoundException $e) {
928
            throw new TypeException(
929
                sprintf(
930
                    "Can't find %s named {%s}#%s, at line %d in %s ",
931
                    'attribute',
932
                    $namespace,
933
                    $name,
934
                    $node->getLineNo(),
935
                    $node->ownerDocument->documentURI
936
                ),
937
                0,
938
                $e
939
            );
940 64
        }
941
    }
942 64
943
    private function findAttributeGroup(Schema $schema, DOMElement $node, string $typeName): AttributeGroup
944
    {
945
        list($name, $namespace) = static::splitParts($node, $typeName);
946
947 64
        /**
948
         * @var string|null $namespace
949
         */
950
        $namespace = $namespace ?: $schema->getTargetNamespace();
951
952
        try {
953 64
            /**
954
             * @var AttributeGroup $out
955 64
             */
956
            $out = $schema->findAttributeGroup((string) $name, $namespace);
957
958
            return $out;
959
        } catch (TypeNotFoundException $e) {
960
            throw new TypeException(
961
                sprintf(
962
                    "Can't find %s named {%s}#%s, at line %d in %s ",
963
                    'attributegroup',
964
                    $namespace,
965
                    $name,
966
                    $node->getLineNo(),
967
                    $node->ownerDocument->documentURI
968
                ),
969
                0,
970
                $e
971
            );
972 64
        }
973
    }
974 64
975
    private function findElement(Schema $schema, DOMElement $node, string $typeName): ElementDef
976
    {
977
        list($name, $namespace) = static::splitParts($node, $typeName);
978
979 64
        /**
980
         * @var string|null $namespace
981
         */
982 64
        $namespace = $namespace ?: $schema->getTargetNamespace();
983
984
        try {
985
            return $schema->findElement((string) $name, $namespace);
986
        } catch (TypeNotFoundException $e) {
987
            throw new TypeException(
988
                sprintf(
989
                    "Can't find %s named {%s}#%s, at line %d in %s ",
990
                    'element',
991
                    $namespace,
992
                    $name,
993
                    $node->getLineNo(),
994
                    $node->ownerDocument->documentURI
995
                ),
996
                0,
997
                $e
998
            );
999 64
        }
1000
    }
1001 64
1002
    private function findGroup(Schema $schema, DOMElement $node, string $typeName): Group
1003
    {
1004
        list($name, $namespace) = static::splitParts($node, $typeName);
1005
1006 64
        /**
1007
         * @var string|null $namespace
1008
         */
1009
        $namespace = $namespace ?: $schema->getTargetNamespace();
1010
1011
        try {
1012 64
            /**
1013
             * @var Group $out
1014 64
             */
1015
            $out = $schema->findGroup((string) $name, $namespace);
1016
1017
            return $out;
1018
        } catch (TypeNotFoundException $e) {
1019
            throw new TypeException(
1020
                sprintf(
1021
                    "Can't find %s named {%s}#%s, at line %d in %s ",
1022
                    'group',
1023
                    $namespace,
1024
                    $name,
1025
                    $node->getLineNo(),
1026
                    $node->ownerDocument->documentURI
1027
                ),
1028
                0,
1029
                $e
1030
            );
1031 64
        }
1032
    }
1033 64
1034
    private function findType(Schema $schema, DOMElement $node, string $typeName): SchemaItem
1035
    {
1036
        list($name, $namespace) = static::splitParts($node, $typeName);
1037
1038 64
        /**
1039
         * @var string|null $namespace
1040
         */
1041
        $namespace = $namespace ?: $schema->getTargetNamespace();
1042
1043
        try {
1044 64
            /**
1045
             * @var SchemaItem $out
1046 64
             */
1047
            $out = $schema->findType((string) $name, $namespace);
1048
1049
            return $out;
1050
        } catch (TypeNotFoundException $e) {
1051
            throw new TypeException(
1052
                sprintf(
1053
                    "Can't find %s named {%s}#%s, at line %d in %s ",
1054
                    'type',
1055
                    $namespace,
1056
                    $name,
1057
                    $node->getLineNo(),
1058
                    $node->ownerDocument->documentURI
1059
                ),
1060
                0,
1061
                $e
1062
            );
1063
        }
1064
    }
1065
1066 64
    /**
1067
     * @return Closure
1068 64
     */
1069
    private function loadElementDef(Schema $schema, DOMElement $node): Closure
1070
    {
1071 64
        return $this->loadAttributeOrElementDef($schema, $node, false);
1072
    }
1073
1074
    private function fillItem(Item $element, DOMElement $node): void
1075
    {
1076 64
        /**
1077 64
         * @var bool
1078 64
         */
1079 64
        $skip = false;
1080
        self::againstDOMNodeList(
1081
            $node,
1082
            function (
1083 64
                DOMElement $node,
1084 64
                DOMElement $childNode
1085
            ) use (
1086
                $element,
1087 64
                &$skip
1088 64
            ): void {
1089 64
                if (
1090
                    !$skip &&
1091 64
                    in_array(
1092
                        $childNode->localName,
1093
                        [
1094 64
                            'complexType',
1095
                            'simpleType',
1096
                        ],
1097 64
                        true
1098 64
                    )
1099 64
                ) {
1100 64
                    $this->loadTypeWithCallback(
1101 64
                        $element->getSchema(),
1102 64
                        $childNode,
1103
                        function (Type $type) use ($element): void {
1104 64
                            $element->setType($type);
1105
                        }
1106 64
                    );
1107
                    $skip = true;
1108 64
                }
1109 64
            }
1110
        );
1111 64
        if ($skip) {
1112 64
            return;
1113
        }
1114 64
        $this->fillItemNonLocalType($element, $node);
1115
    }
1116 64
1117
    private function fillItemNonLocalType(Item $element, DOMElement $node): void
1118
    {
1119
        if ($node->getAttribute('type')) {
1120 64
            /**
1121
             * @var Type
1122
             */
1123
            $type = $this->findSomeType($element, $node, 'type');
1124
        } else {
1125 64
            /**
1126 64
             * @var Type
1127 64
             */
1128 64
            $type = $this->findSomeTypeFromAttribute(
1129
                $element,
1130
                $node,
1131
                ($node->lookupPrefix(self::XSD_NS).':anyType')
1132 64
            );
1133 64
        }
1134
1135 64
        $element->setType($type);
1136
    }
1137
1138
    private function loadImport(
1139 64
        Schema $schema,
1140 64
        DOMElement $node
1141 64
    ): Closure {
1142 1
        $namespace = $node->getAttribute('namespace');
1143
        $schemaLocation = $node->getAttribute('schemaLocation');
1144
        if (!$schemaLocation && isset($this->knownNamespaceSchemaLocations[$namespace])) {
1145
            $schemaLocation = $this->knownNamespaceSchemaLocations[$namespace];
1146 64
        }
1147 3
1148 3
        // postpone schema loading
1149 3
        if ($namespace && !$schemaLocation && !isset(self::$globalSchemaInfo[$namespace])) {
1150 3
            return function () use ($schema, $namespace): void {
1151
                if (!empty($this->loadedSchemas[$namespace])) {
1152
                    foreach ($this->loadedSchemas[$namespace] as $s) {
1153 3
                        $schema->addSchema($s, $namespace);
1154 64
                    }
1155 1
                }
1156 1
            };
1157
        } elseif ($namespace && !$schemaLocation && !empty($this->loadedSchemas[$namespace])) {
1158
            foreach ($this->loadedSchemas[$namespace] as $s) {
1159
                $schema->addSchema($s, $namespace);
1160 64
            }
1161 64
        }
1162
1163 64
        $base = urldecode($node->ownerDocument->documentURI);
1164 64
        $file = UrlUtils::resolveRelativeUrl($base, $schemaLocation);
1165
1166 64
        if (isset($this->loadedFiles[$file])) {
1167 64
            $schema->addSchema($this->loadedFiles[$file]);
1168
1169
            return function (): void {
1170 3
            };
1171
        }
1172
1173 3
        return $this->loadImportFresh($namespace, $schema, $file);
1174
    }
1175
1176
    private function loadRedefine(Schema $schema, DOMElement $node)
1177 3
    {
1178 2
        $base = urldecode($node->ownerDocument->documentURI);
1179 2
        $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute('schemaLocation'));
1180 2
1181
        if (isset($this->loadedFiles[$file])) {
1182 1
            /* @var $redefined Schema */
1183
            $redefined = clone $this->loadedFiles[$file];
1184
1185 3
            if($schema->getTargetNamespace() !== $redefined->getTargetNamespace()){
1186
                $redefined->setTargetNamespace($schema->getTargetNamespace());
1187
            }
1188
1189
            $schema->addSchema($redefined);
1190
1191
            return function () use ($redefined, $node, $schema): void {
1192
1193 3
                $callbacks = $this->schemaNode($redefined, $node, $schema);
1194 3
                foreach ($callbacks as $callback) {
1195 3
                    $callback();
1196 1
                }
1197 3
            };
1198
        }
1199
1200 3
        return $this->loadImportFresh($schema->getTargetNamespace(), $schema, $file);
0 ignored issues
show
Bug introduced by
It seems like $schema->getTargetNamespace() can also be of type null; however, parameter $namespace of GoetasWebservices\XML\XS...ader::loadImportFresh() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1200
        return $this->loadImportFresh(/** @scrutinizer ignore-type */ $schema->getTargetNamespace(), $schema, $file);
Loading history...
1201
    }
1202 3
1203
    private function createOrUseSchemaForNs(
1204 3
        Schema $schema,
1205
        string $namespace
1206 3
    ): Schema {
1207 3
        if (('' !== trim($namespace))) {
1208
            $newSchema = new Schema();
1209 3
            $newSchema->addSchema($this->getGlobalSchema());
1210
            $schema->addSchema($newSchema);
1211
        } else {
1212
            $newSchema = $schema;
1213
        }
1214
1215
        return $newSchema;
1216
    }
1217 64
1218
    private function loadImportFresh(
1219 64
        string $namespace,
1220 64
        Schema $schema,
1221 64
        string $file
1222
    ): Closure {
1223
        return function () use ($namespace, $schema, $file): void {
1224
            $dom = $this->getDOM(
1225 64
                isset($this->knownLocationSchemas[$file])
1226 64
                    ? $this->knownLocationSchemas[$file]
1227 64
                    : $file
1228 64
            );
1229
1230 64
            $schemaNew = $this->createOrUseSchemaForNs($schema, $namespace);
1231 64
1232 64
            $this->setLoadedFile($file, $schemaNew);
1233
1234 64
            $callbacks = $this->schemaNode($schemaNew, $dom->documentElement, $schema);
1235 64
1236
            foreach ($callbacks as $callback) {
1237
                $callback();
1238 64
            }
1239 64
        };
1240
    }
1241 64
1242 64
    /**
1243 64
     * @var Schema|null
1244
     */
1245 64
    protected $globalSchema;
1246 64
1247 64
    public function getGlobalSchema(): Schema
1248
    {
1249
        if (!($this->globalSchema instanceof Schema)) {
1250
            $callbacks = array();
1251
            $globalSchemas = array();
1252
            /**
1253 64
             * @var string $namespace
1254 64
             */
1255
            foreach (self::$globalSchemaInfo as $namespace => $uri) {
1256
                $this->setLoadedFile(
1257
                    $uri,
1258 64
                    $globalSchemas[$namespace] = $schema = new Schema()
1259
                );
1260
                $this->setLoadedSchema($namespace, $schema);
1261
                if ($namespace === self::XSD_NS) {
1262 64
                    $this->globalSchema = $schema;
1263
                }
1264
                $xml = $this->getDOM($this->knownLocationSchemas[$uri]);
1265
                $callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement));
1266
            }
1267
1268 1
            $globalSchemas[(string) static::XSD_NS]->addType(new SimpleType($globalSchemas[(string) static::XSD_NS], 'anySimpleType'));
1269
            $globalSchemas[(string) static::XSD_NS]->addType(new SimpleType($globalSchemas[(string) static::XSD_NS], 'anyType'));
1270 1
1271 1
            $globalSchemas[(string) static::XML_NS]->addSchema(
1272
                $globalSchemas[(string) static::XSD_NS],
1273 1
                (string) static::XSD_NS
1274 1
            );
1275
            $globalSchemas[(string) static::XSD_NS]->addSchema(
1276
                $globalSchemas[(string) static::XML_NS],
1277 1
                (string) static::XML_NS
1278 1
            );
1279 1
1280 1
            /**
1281 1
             * @var Closure
1282
             */
1283 1
            foreach ($callbacks as $callback) {
1284
                $callback();
1285 1
            }
1286
        }
1287 1
1288 1
        if (!($this->globalSchema instanceof Schema)) {
1289
            throw new TypeException('Global schema not discovered');
1290
        }
1291
1292 1
        return $this->globalSchema;
1293 1
    }
1294
1295
    /**
1296 1
     * @param DOMNode[] $nodes
1297
     */
1298
    public function readNodes(array $nodes, string $file = null): Schema
1299 63
    {
1300
        $rootSchema = new Schema();
1301 63
        $rootSchema->addSchema($this->getGlobalSchema());
1302 63
1303
        if ($file !== null) {
1304 63
            $this->setLoadedFile($file, $rootSchema);
1305 62
        }
1306
1307
        $all = array();
1308 63
        foreach ($nodes as $k => $node) {
1309
            if (($node instanceof \DOMElement) && $node->namespaceURI === self::XSD_NS && $node->localName == 'schema') {
1310 63
                $holderSchema = new Schema();
1311
                $holderSchema->addSchema($this->getGlobalSchema());
1312 63
1313 57
                $this->setLoadedSchemaFromElement($node, $holderSchema);
1314
1315
                $rootSchema->addSchema($holderSchema);
1316 63
1317
                $callbacks = $this->schemaNode($holderSchema, $node);
1318
                $all = array_merge($callbacks, $all);
1319
            }
1320
        }
1321
1322 61
        foreach ($all as $callback) {
1323
            call_user_func($callback);
1324 61
        }
1325 61
1326 61
        return $rootSchema;
1327 1
    }
1328
1329 60
    public function readNode(DOMElement $node, string $file = null): Schema
1330 60
    {
1331
        $rootSchema = new Schema();
1332 60
        $rootSchema->addSchema($this->getGlobalSchema());
1333
1334
        if ($file !== null) {
1335
            $this->setLoadedFile($file, $rootSchema);
1336
        }
1337
1338 3
        $this->setLoadedSchemaFromElement($node, $rootSchema);
1339
1340 3
        $callbacks = $this->schemaNode($rootSchema, $node);
1341
1342 2
        foreach ($callbacks as $callback) {
1343
            call_user_func($callback);
1344
        }
1345
1346
        return $rootSchema;
1347
    }
1348 65
1349
    /**
1350 65
     * @throws IOException
1351 65
     */
1352 65
    public function readString(string $content, string $file = 'schema.xsd'): Schema
1353 1
    {
1354 1
        $xml = new DOMDocument('1.0', 'UTF-8');
1355
        libxml_use_internal_errors(true);
1356 64
        if (!@$xml->loadXML($content)) {
1357
            throw new IOException("Can't load the schema", 0, $this->extractErrorMessage());
1358 64
        }
1359
        libxml_use_internal_errors(false);
1360
        $xml->documentURI = $file;
1361 64
1362
        return $this->readNode($xml->documentElement, $file);
1363
    }
1364
1365 64
    /**
1366 64
     * @throws IOException
1367
     */
1368
    public function readFile(string $file): Schema
1369
    {
1370 64
        $xml = $this->getDOM($file);
1371
1372 64
        return $this->readNode($xml->documentElement, $file);
1373 64
    }
1374 64
1375 64
    /**
1376
     * @throws IOException
1377
     */
1378
    private function getDOM(string $file): DOMDocument
1379 64
    {
1380
        $xml = new DOMDocument('1.0', 'UTF-8');
1381 64
        libxml_use_internal_errors(true);
1382
        if (!@$xml->load($file)) {
1383
            libxml_use_internal_errors(false);
1384
            throw new IOException("Can't load the file '$file'", 0, $this->extractErrorMessage());
1385
        }
1386
        libxml_use_internal_errors(false);
1387
1388
        return $xml;
1389 64
    }
1390
1391 64
    private static function againstDOMNodeList(
1392 64
        DOMElement $node,
1393 64
        Closure $againstNodeList
1394 64
    ): void {
1395 64
        $limit = $node->childNodes->length;
1396 64
        for ($i = 0; $i < $limit; ++$i) {
1397 64
            /**
1398
             * @var DOMNode
1399
             */
1400 64
            $childNode = $node->childNodes->item($i);
1401 64
1402
            if ($childNode instanceof DOMElement) {
1403 64
                $againstNodeList(
1404
                    $node,
1405 64
                    $childNode
1406
                );
1407
            }
1408
        }
1409 64
    }
1410 64
1411
    private function loadTypeWithCallback(
1412 64
        Schema $schema,
1413
        DOMElement $childNode,
1414 64
        Closure $callback
1415 64
    ): void {
1416 64
        /**
1417
         * @var Closure|null $func
1418 64
         */
1419 64
        $func = null;
1420
1421 64
        switch ($childNode->localName) {
1422 64
            case 'complexType':
1423
                $func = $this->loadComplexType($schema, $childNode, $callback);
1424
                break;
1425 64
            case 'simpleType':
1426 4
                $func = $this->loadSimpleType($schema, $childNode, $callback);
1427
                break;
1428 64
        }
1429 5
1430
        if ($func instanceof Closure) {
1431
            call_user_func($func);
1432 64
        }
1433
    }
1434
1435 64
    private function loadElement(
1436
        Schema $schema,
1437
        DOMElement $node
1438
    ): Element {
1439
        $element = new Element($schema, $node->getAttribute('name'));
1440
        $element->setDoc($this->getDocumentation($node));
1441 64
1442 64
        $this->fillItem($element, $node);
1443 64
1444 64
        self::maybeSetMax($element, $node);
1445
        self::maybeSetMin($element, $node);
1446
        self::maybeSetDefault($element, $node);
1447 64
1448 64
        $xp = new \DOMXPath($node->ownerDocument);
1449
        $xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema');
1450 64
1451
        if ($xp->query('ancestor::xs:choice', $node)->length) {
1452
            $element->setMin(0);
1453
        }
1454
1455
        if ($node->hasAttribute('nillable')) {
1456 64
            $element->setNil($node->getAttribute('nillable') == 'true');
1457 64
        }
1458 64
        if ($node->hasAttribute('form')) {
1459
            $element->setQualified($node->getAttribute('form') == 'qualified');
1460 64
        }
1461
1462 64
        return $element;
1463 64
    }
1464
1465 64
    private function addAttributeFromAttributeOrRef(
1466
        BaseComplexType $type,
1467 64
        DOMElement $childNode,
1468 64
        Schema $schema,
1469
        DOMElement $node
1470 64
    ): void {
1471
        $attribute = $this->getAttributeFromAttributeOrRef(
1472 64
            $childNode,
1473
            $schema,
1474 64
            $node
1475 64
        );
1476
1477 64
        $type->addAttribute($attribute);
1478 64
    }
1479
1480 64
    private function findSomethingLikeAttributeGroup(
1481
        Schema $schema,
1482 64
        DOMElement $node,
1483
        DOMElement $childNode,
1484
        AttributeContainer $addToThis
1485
    ): void {
1486
        $attribute = $this->findAttributeGroup($schema, $node, $childNode->getAttribute('ref'));
1487 64
        $addToThis->addAttribute($attribute);
1488
    }
1489 64
1490 64
    private function setLoadedFile(string $key, Schema $schema): void
1491
    {
1492
        $this->loadedFiles[$key] = $schema;
1493
    }
1494 64
1495 64
    private function setLoadedSchemaFromElement(DOMElement $node, Schema $schema): void
1496 64
    {
1497 64
        if ($node->hasAttribute('targetNamespace')) {
1498
            $this->setLoadedSchema($node->getAttribute('targetNamespace'), $schema);
1499
        }
1500
    }
1501
1502
    private function setLoadedSchema(string $namespace, Schema $schema): void
1503
    {
1504
        if (!isset($this->loadedSchemas[$namespace])) {
1505
            $this->loadedSchemas[$namespace] = array();
1506
        }
1507
        if (!in_array($schema, $this->loadedSchemas[$namespace], true)) {
1508
            $this->loadedSchemas[$namespace][] = $schema;
1509
        }
1510
    }
1511
1512
    private function setSchemaThingsFromNode(
1513
        Schema $schema,
1514
        DOMElement $node,
1515
        Schema $parent = null
1516
    ): void {
1517
        $schema->setDoc($this->getDocumentation($node));
1518
1519
        if ($node->hasAttribute('targetNamespace')) {
1520
            $schema->setTargetNamespace($node->getAttribute('targetNamespace'));
1521
        } elseif ($parent instanceof Schema) {
1522
            $schema->setTargetNamespace($parent->getTargetNamespace());
1523
        }
1524
        $schema->setElementsQualification($node->getAttribute('elementFormDefault') == 'qualified');
1525
        $schema->setAttributesQualification($node->getAttribute('attributeFormDefault') == 'qualified');
1526
        $schema->setDoc($this->getDocumentation($node));
1527
    }
1528
}
1529