Completed
Push — master ( 408208...60b26b )
by Asmir
22s queued 13s
created

SchemaReader::getDocumentation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 1
cts 1
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
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\InterfaceSetDefault;
27
use GoetasWebservices\XML\XSDReader\Schema\Element\InterfaceSetFixed;
28
use GoetasWebservices\XML\XSDReader\Schema\Element\InterfaceSetMinMax;
29
use GoetasWebservices\XML\XSDReader\Schema\Exception\TypeNotFoundException;
30
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Base;
31
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Extension;
32
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Restriction;
33
use GoetasWebservices\XML\XSDReader\Schema\Item;
34
use GoetasWebservices\XML\XSDReader\Schema\Schema;
35
use GoetasWebservices\XML\XSDReader\Schema\SchemaItem;
36
use GoetasWebservices\XML\XSDReader\Schema\Type\BaseComplexType;
37
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
38
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexTypeSimpleContent;
39
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType;
40
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
41
use GoetasWebservices\XML\XSDReader\Utils\UrlUtils;
42
43
class SchemaReader
44
{
45
    public const XSD_NS = 'http://www.w3.org/2001/XMLSchema';
46
47
    public const XML_NS = 'http://www.w3.org/XML/1998/namespace';
48
49
    /**
50
     * @var DocumentationReader
51
     */
52
    private $documentationReader;
53
54
    /**
55
     * @var Schema[]
56
     */
57
    private $loadedFiles = [];
58
59
    /**
60
     * @var Schema[][]
61
     */
62
    private $loadedSchemas = [];
63
64
    /**
65
     * @var string[]
66
     */
67
    protected $knownLocationSchemas = [
68
        'http://www.w3.org/2001/xml.xsd' => (
69
            __DIR__.'/Resources/xml.xsd'
70
        ),
71
        'http://www.w3.org/2001/XMLSchema.xsd' => (
72
            __DIR__.'/Resources/XMLSchema.xsd'
73
        ),
74
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' => (
75
            __DIR__.'/Resources/oasis-200401-wss-wssecurity-secext-1.0.xsd'
76
        ),
77
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' => (
78
            __DIR__.'/Resources/oasis-200401-wss-wssecurity-utility-1.0.xsd'
79
        ),
80
        'https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
81
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
82
        ),
83
        'http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
84
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
85
        ),
86
    ];
87
88
    /**
89
     * @var string[]
90
     */
91
    protected $knownNamespaceSchemaLocations = [];
92
93
    /**
94
     * @var string[]
95
     */
96
    protected static $globalSchemaInfo = [
97
        self::XML_NS => 'http://www.w3.org/2001/xml.xsd',
98
        self::XSD_NS => 'http://www.w3.org/2001/XMLSchema.xsd',
99
    ];
100 2
101
    private function extractErrorMessage(): \Exception
102 2
    {
103
        $errors = [];
104 2
105 1
        foreach (libxml_get_errors() as $error) {
106
            $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);
107 2
        }
108 2
        $e = new \Exception(implode('; ', $errors));
109
        libxml_use_internal_errors(false);
110 2
111
        return $e;
112
    }
113 85
114
    public function __construct(DocumentationReader $documentationReader = null)
115 85
    {
116 85
        if (null === $documentationReader) {
117
            $documentationReader = new StandardDocumentationReader();
118 85
        }
119 85
        $this->documentationReader = $documentationReader;
120
    }
121
122
    /**
123
     * Override remote location with a local file.
124
     *
125
     * @param string $remote remote schema URL
126
     * @param string $local  local file path
127 1
     */
128
    public function addKnownSchemaLocation(string $remote, string $local): void
129 1
    {
130 1
        $this->knownLocationSchemas[$remote] = $local;
131
    }
132
133
    /**
134
     * Specify schema location by namespace.
135
     * This can be used for schemas which import namespaces but do not specify schemaLocation attributes.
136
     *
137
     * @param string $namespace namespace
138
     * @param string $location  schema URL
139 1
     */
140
    public function addKnownNamespaceSchemaLocation(string $namespace, string $location): void
141 1
    {
142 1
        $this->knownNamespaceSchemaLocations[$namespace] = $location;
143
    }
144 74
145
    private function loadAttributeGroup(
146
        Schema $schema,
147
        DOMElement $node
148 74
    ): Closure {
149 74
        $attGroup = new AttributeGroup($schema, $node->getAttribute('name'));
150 74
        $attGroup->setDoc($this->getDocumentation($node));
151
        $schema->addAttributeGroup($attGroup);
152
153 74
        return function () use ($schema, $node, $attGroup): void {
154 74
            SchemaReader::againstDOMNodeList(
155
                $node,
156
                function (
157
                    DOMElement $node,
158
                    DOMElement $childNode
159 74
                ) use (
160 74
                    $schema,
161
                    $attGroup
162 74
                ): void {
163 74
                    switch ($childNode->localName) {
164 74
                        case 'attribute':
165 74
                            $attribute = $this->getAttributeFromAttributeOrRef(
166 74
                                $childNode,
167 74
                                $schema,
168
                                $node
169 74
                            );
170 74
                            $attGroup->addAttribute($attribute);
171 74
                            break;
172 1
                        case 'attributeGroup':
173 1
                            $this->findSomethingLikeAttributeGroup(
174 1
                                $schema,
175 1
                                $node,
176 1
                                $childNode,
177
                                $attGroup
178 1
                            );
179
                            break;
180 74
                    }
181
                }
182 74
            );
183
        };
184
    }
185 74
186
    private function getAttributeFromAttributeOrRef(
187
        DOMElement $childNode,
188
        Schema $schema,
189
        DOMElement $node
190 74
    ): AttributeItem {
191 74
        if ($childNode->hasAttribute('ref')) {
192
            $attribute = $this->findAttributeItem($schema, $node, $childNode->getAttribute('ref'));
193
        } else {
194
            /**
195
             * @var Attribute
196 74
             */
197
            $attribute = $this->loadAttribute($schema, $childNode);
198
        }
199 74
200
        return $attribute;
201
    }
202 74
203
    private function loadAttribute(
204
        Schema $schema,
205
        DOMElement $node
206 74
    ): Attribute {
207 74
        $attribute = new Attribute($schema, $node->getAttribute('name'));
208 74
        $attribute->setDoc($this->getDocumentation($node));
209
        $this->fillItem($attribute, $node);
210 74
211 1
        if ($node->hasAttribute('nillable')) {
212
            $attribute->setNil($node->getAttribute('nillable') == 'true');
213 74
        }
214 1
        if ($node->hasAttribute('form')) {
215
            $attribute->setQualified($node->getAttribute('form') == 'qualified');
216 74
        }
217 74
        if ($node->hasAttribute('use')) {
218
            $attribute->setUse($node->getAttribute('use'));
219
        }
220 74
221
        return $attribute;
222
    }
223 74
224
    private function loadAttributeOrElementDef(
225
        Schema $schema,
226
        DOMElement $node,
227
        bool $attributeDef
228 74
    ): Closure {
229 74
        $name = $node->getAttribute('name');
230 74
        if ($attributeDef) {
231 74
            $attribute = new AttributeDef($schema, $name);
232
            $schema->addAttribute($attribute);
233 74
        } else {
234 74
            $attribute = new ElementDef($schema, $name);
235 74
            $attribute->setDoc($this->getDocumentation($node));
236
            $schema->addElement($attribute);
237
        }
238
239 74
        return function () use ($attribute, $node): void {
240 74
            $this->fillItem($attribute, $node);
241
        };
242
    }
243 74
244
    private function loadAttributeDef(Schema $schema, DOMElement $node): Closure
245 74
    {
246
        return $this->loadAttributeOrElementDef($schema, $node, true);
247
    }
248 74
249
    private function getDocumentation(DOMElement $node): string
250 74
    {
251
        return $this->documentationReader->get($node);
252
    }
253
254
    /**
255
     * @return Closure[]
256 74
     */
257
    private function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null): array
258 74
    {
259 74
        $this->setSchemaThingsFromNode($schema, $node, $parent);
260
        $functions = [];
261 74
262 74
        self::againstDOMNodeList(
263
            $node,
264
            function (
265
                DOMElement $node,
266
                DOMElement $childNode
267 74
            ) use (
268 74
                $schema,
269
                &$functions
270 74
            ): void {
271
                $callback = null;
272 74
273 74
                switch ($childNode->localName) {
274 74
                    case 'attributeGroup':
275 74
                        $callback = $this->loadAttributeGroup($schema, $childNode);
276 74
                        break;
277 74
                    case 'include':
278 74
                    case 'import':
279 74
                        $callback = $this->loadImport($schema, $childNode);
280 74
                        break;
281 74
                    case 'element':
282 74
                        $callback = $this->loadElementDef($schema, $childNode);
283 74
                        break;
284 74
                    case 'attribute':
285 74
                        $callback = $this->loadAttributeDef($schema, $childNode);
286 74
                        break;
287 74
                    case 'group':
288 74
                        $callback = $this->loadGroup($schema, $childNode);
289 74
                        break;
290 74
                    case 'complexType':
291 74
                        $callback = $this->loadComplexType($schema, $childNode);
292 74
                        break;
293 74
                    case 'simpleType':
294 74
                        $callback = $this->loadSimpleType($schema, $childNode);
295
                        break;
296
                }
297 74
298 74
                if ($callback instanceof Closure) {
299
                    $functions[] = $callback;
300 74
                }
301
            }
302
        );
303 74
304
        return $functions;
305
    }
306 74
307
    private function loadGroupRef(Group $referenced, DOMElement $node): GroupRef
308 74
    {
309 74
        $ref = new GroupRef($referenced);
310
        $ref->setDoc($this->getDocumentation($node));
311 74
312 74
        self::maybeSetMax($ref, $node);
313
        self::maybeSetMin($ref, $node);
314 74
315
        return $ref;
316
    }
317 74
318
    private static function maybeSetMax(InterfaceSetMinMax $ref, DOMElement $node): void
319 74
    {
320 74
        if ($node->hasAttribute('maxOccurs')) {
321
            $ref->setMax($node->getAttribute('maxOccurs') == 'unbounded' ? -1 : (int) $node->getAttribute('maxOccurs'));
322 74
        }
323
    }
324 74
325
    private static function maybeSetMin(InterfaceSetMinMax $ref, DOMElement $node): void
326 74
    {
327 74
        if ($node->hasAttribute('minOccurs')) {
328 74
            $ref->setMin((int) $node->getAttribute('minOccurs'));
329 7
            if ($ref->getMin() > $ref->getMax() && $ref->getMax() !== -1) {
330
                $ref->setMax($ref->getMin());
331
            }
332 74
        }
333
    }
334 74
335
    private static function maybeSetFixed(InterfaceSetFixed $ref, DOMElement $node): void
336 74
    {
337 1
        if ($node->hasAttribute('fixed')) {
338
            $ref->setFixed($node->getAttribute('fixed'));
339 74
        }
340
    }
341 74
342
    private static function maybeSetDefault(InterfaceSetDefault $ref, DOMElement $node): void
343
    {
344
        if ($node->hasAttribute('default')) {
345 74
            $ref->setDefault($node->getAttribute('default'));
346 74
        }
347 74
    }
348
349 74
    private function loadSequence(ElementContainer $elementContainer, DOMElement $node, int $max = null, int $min = null): void
350 74
    {
351
        $max =
352
            (
353 74
                (is_int($max) && (bool) $max) ||
354 74
                $node->getAttribute('maxOccurs') == 'unbounded' ||
355
                $node->getAttribute('maxOccurs') > 1
356 74
            )
357 74
                ? 2
358
                : null;
359 74
        $min =
360 74
            (
361
                $min === null &&
362
                !$node->hasAttribute('minOccurs')
363
            )
364
                ? null
365 74
                : (int) max((int) $min, $node->getAttribute('minOccurs'));
366 74
367 74
        self::againstDOMNodeList(
368
            $node,
369 74
            function (
370 74
                DOMElement $node,
371 74
                DOMElement $childNode
372 74
            ) use (
373 74
                $elementContainer,
374 74
                $max,
375
                $min
376 74
            ): void {
377
                $this->loadSequenceChildNode(
378 74
                    $elementContainer,
379
                    $node,
380 74
                    $childNode,
381
                    $max,
382
                    $min
383
                );
384
            }
385
        );
386
    }
387 74
388 74
    private function loadSequenceChildNode(
389 74
        ElementContainer $elementContainer,
390 74
        DOMElement $node,
391 74
        DOMElement $childNode,
392 74
        ?int $max,
393 74
        ?int $min = null
394 74
    ): void {
395 74
        switch ($childNode->localName) {
396
            case 'sequence':
397 74
            case 'choice':
398 74
            case 'all':
399 74
                $this->loadSequence(
400 74
                    $elementContainer,
401 74
                    $childNode,
402 74
                    $max,
403 74
                    $min
404 74
                );
405
                break;
406 74
            case 'element':
407 74
                $this->loadSequenceChildNodeLoadElement(
408 74
                    $elementContainer,
409 74
                    $node,
410 74
                    $childNode,
411 74
                    $max,
412 74
                    $min
413
                );
414 74
                break;
415
            case 'group':
416 74
                $this->addGroupAsElement(
417
                    $elementContainer->getSchema(),
418 74
                    $node,
419
                    $childNode,
420
                    $elementContainer
421
                );
422
                break;
423
        }
424
    }
425 74
426 74
    private function loadSequenceChildNodeLoadElement(
427 74
        ElementContainer $elementContainer,
428 74
        DOMElement $node,
429
        DOMElement $childNode,
430 74
        ?int $max,
431 74
        ?int $min
432
    ): void {
433 74
        if ($childNode->hasAttribute('ref')) {
434 74
            $elementDef = $this->findElement($elementContainer->getSchema(), $node, $childNode->getAttribute('ref'));
435
            $element = new ElementRef($elementDef);
436 74
            $element->setDoc($this->getDocumentation($childNode));
437 74
438
            self::maybeSetMax($element, $childNode);
439
            self::maybeSetMin($element, $childNode);
440 74
441
            $xp = new \DOMXPath($node->ownerDocument);
0 ignored issues
show
Bug introduced by
It seems like $node->ownerDocument can also be of type null; however, parameter $document of DOMXPath::__construct() does only seem to accept DOMDocument, 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

441
            $xp = new \DOMXPath(/** @scrutinizer ignore-type */ $node->ownerDocument);
Loading history...
442
            $xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema');
443 74
444 74
            if ($xp->query('ancestor::xs:choice', $childNode)->length) {
445
                $element->setMin(0);
446
            }
447 74
448 74
            if ($childNode->hasAttribute('nillable')) {
449 74
                $element->setNil($childNode->getAttribute('nillable') == 'true');
450
            }
451
            if ($childNode->hasAttribute('form')) {
452
                $element->setQualified($childNode->getAttribute('form') == 'qualified');
453 74
            }
454 74
        } else {
455
            $element = $this->loadElement(
456
                $elementContainer->getSchema(),
457 74
                $childNode
458
            );
459
        }
460
461
        if ($min !== null) {
462
            $element->setMin($min);
463 74
        }
464
465 74
        if ($max > 1) {
466 74
            /*
467
            * although one might think the typecast is not needed with $max being `? int $max` after passing > 1,
468 74
            * phpstan@a4f89fa still thinks it's possibly null.
469
            * see https://github.com/phpstan/phpstan/issues/577 for related issue
470
            */
471
            $element->setMax((int) $max);
472
        }
473
        $elementContainer->addElement($element);
474 74
    }
475 74
476 74
    private function addGroupAsElement(
477 74
        Schema $schema,
478
        DOMElement $node,
479
        DOMElement $childNode,
480 74
        ElementContainer $elementContainer
481 74
    ): void {
482 74
        $referencedGroup = $this->findGroup(
483
            $schema,
484 74
            $node,
485
            $childNode->getAttribute('ref')
486 74
        );
487 74
488 74
        $group = $this->loadGroupRef($referencedGroup, $childNode);
489
        $elementContainer->addElement($group);
490 74
    }
491 1
492
    private function loadGroup(Schema $schema, DOMElement $node): Closure
493 1
    {
494 1
        $group = new Group($schema, $node->getAttribute('name'));
495
        $group->setDoc($this->getDocumentation($node));
496 1
        $groupOriginal = $group;
497 1
498
        if ($node->hasAttribute('maxOccurs') || $node->hasAttribute('maxOccurs')) {
499
            $group = new GroupRef($group);
500
501 74
            if ($node->hasAttribute('maxOccurs')) {
502
                self::maybeSetMax($group, $node);
503
            }
504 74
            if ($node->hasAttribute('minOccurs')) {
505 74
                self::maybeSetMin($group, $node);
506
            }
507 74
        }
508 74
509 74
        $schema->addGroup($group);
510 74
511 74
        return function () use ($groupOriginal, $node): void {
512 74
            static::againstDOMNodeList(
513
                $node,
514 74
                function (DOMelement $node, DOMElement $childNode) use ($groupOriginal): void {
515
                    switch ($childNode->localName) {
516 74
                        case 'sequence':
517
                        case 'choice':
518
                        case 'all':
519 74
                            $this->loadSequence($groupOriginal, $childNode);
520
                            break;
521
                    }
522
                }
523
            );
524 74
        };
525
    }
526 74
527 74
    private function loadComplexType(Schema $schema, DOMElement $node, Closure $callback = null): Closure
528
    {
529
        /**
530
         * @var bool
531
         */
532 74
        $isSimple = false;
533
534 74
        self::againstDOMNodeList(
535 1
            $node,
536
            function (
537 74
                DOMElement $node,
538 2
                DOMElement $childNode
539
            ) use (
540 74
                &$isSimple
541
            ): void {
542
                if ($isSimple) {
543 74
                    return;
544
                }
545 74
                if ($childNode->localName === 'simpleContent') {
546 74
                    $isSimple = true;
547 74
                }
548
            }
549
        );
550
551 74
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute('name')) : new ComplexType($schema, $node->getAttribute('name'));
552
553 74
        $type->setDoc($this->getDocumentation($node));
554 74
        if ($node->getAttribute('name')) {
555
            $schema->addType($type);
556
        }
557
558
        return function () use ($type, $node, $schema, $callback): void {
559 74
            $this->fillTypeNode($type, $node, true);
560 74
561
            self::againstDOMNodeList(
562 74
                $node,
563 74
                function (
564 74
                    DOMElement $node,
565 74
                    DOMElement $childNode
566 74
                ) use (
567
                    $schema,
568 74
                    $type
569
                ): void {
570
                    $this->loadComplexTypeFromChildNode(
571 74
                        $type,
572 74
                        $node,
573
                        $childNode,
574 74
                        $schema
575
                    );
576
                }
577 74
            );
578
579
            if ($callback instanceof Closure) {
580
                call_user_func($callback, $type);
581
            }
582
        };
583 74
    }
584 74
585 74
    private function loadComplexTypeFromChildNode(
586 74
        BaseComplexType $type,
587 74
        DOMElement $node,
588 74
        DOMElement $childNode,
589 74
        Schema $schema
590 74
    ): void {
591
        switch ($childNode->localName) {
592
            case 'sequence':
593 74
            case 'choice':
594 74
            case 'all':
595 74
                if ($type instanceof ElementContainer) {
596 74
                    $this->loadSequence(
597 74
                        $type,
598 74
                        $childNode
599 74
                    );
600
                }
601 74
                break;
602 74
            case 'attribute':
603 2
                $this->addAttributeFromAttributeOrRef(
604 2
                    $type,
605 2
                    $childNode,
606 2
                    $schema,
607 2
                    $node
608
                );
609 2
                break;
610 74
            case 'attributeGroup':
611
                $this->findSomethingLikeAttributeGroup(
612 1
                    $schema,
613
                    $node,
614 1
                    $childNode,
615 1
                    $type
616 1
                );
617 1
                break;
618 1
            case 'group':
619
                if (
620
                    $type instanceof ComplexType
621 1
                ) {
622
                    $this->addGroupAsElement(
623 74
                        $schema,
624
                        $node,
625 74
                        $childNode,
626
                        $type
627 74
                    );
628 74
                }
629 74
                break;
630 74
        }
631
    }
632
633
    private function loadSimpleType(Schema $schema, DOMElement $node, Closure $callback = null): Closure
634 74
    {
635
        $type = new SimpleType($schema, $node->getAttribute('name'));
636 74
        $type->setDoc($this->getDocumentation($node));
637 74
        if ($node->getAttribute('name')) {
638
            $schema->addType($type);
639 74
        }
640 74
641 74
        return function () use ($type, $node, $callback): void {
642 74
            $this->fillTypeNode($type, $node, true);
643 74
644 74
            self::againstDOMNodeList(
645 74
                $node,
646
                function (DOMElement $node, DOMElement $childNode) use ($type): void {
647 74
                    switch ($childNode->localName) {
648
                        case 'union':
649
                            $this->loadUnion($type, $childNode);
650 74
                            break;
651 74
                        case 'list':
652
                            $this->loadList($type, $childNode);
653 74
                            break;
654
                    }
655
                }
656 74
            );
657
658 74
            if ($callback instanceof Closure) {
659
                call_user_func($callback, $type);
660
            }
661
        };
662 74
    }
663 74
664
    private function loadList(SimpleType $type, DOMElement $node): void
665 74
    {
666 74
        if ($node->hasAttribute('itemType')) {
667
            /**
668
             * @var SimpleType
669
             */
670
            $listType = $this->findSomeType($type, $node, 'itemType');
671 74
            $type->setList($listType);
672
        } else {
673 74
            self::againstDOMNodeList(
674 74
                $node,
675 74
                function (
676
                    DOMElement $node,
677 74
                    DOMElement $childNode
678 74
                ) use (
679
                    $type
680 74
                ): void {
681
                    $this->loadTypeWithCallback(
682
                        $type->getSchema(),
683 74
                        $childNode,
684
                        function (SimpleType $list) use ($type): void {
685 74
                            $type->setList($list);
686
                        }
687
                    );
688
                }
689
            );
690 74
        }
691 74
    }
692 74
693 74
    private function findSomeType(
694
        SchemaItem $fromThis,
695
        DOMElement $node,
696
        string $attributeName
697 74
    ): SchemaItem {
698
        return $this->findSomeTypeFromAttribute(
699
            $fromThis,
700
            $node,
701
            $node->getAttribute($attributeName)
702 74
        );
703 74
    }
704 74
705 74
    private function findSomeTypeFromAttribute(
706
        SchemaItem $fromThis,
707
        DOMElement $node,
708 74
        string $attributeName
709
    ): SchemaItem {
710
        $out = $this->findType(
711 74
            $fromThis->getSchema(),
712
            $node,
713 74
            $attributeName
714 74
        );
715 74
716
        return $out;
717
    }
718
719 74
    private function loadUnion(SimpleType $type, DOMElement $node): void
720 74
    {
721 74
        if ($node->hasAttribute('memberTypes')) {
722 74
            $types = preg_split('/\s+/', $node->getAttribute('memberTypes'));
723
            foreach ($types as $typeName) {
724 74
                /**
725
                 * @var SimpleType
726
                 */
727 74
                $unionType = $this->findSomeTypeFromAttribute(
728 74
                    $type,
729
                    $node,
730
                    $typeName
731
                );
732
                $type->addUnion($unionType);
733 74
            }
734
        }
735 74
        self::againstDOMNodeList(
736 74
            $node,
737 74
            function (
738
                DOMElement $node,
739 74
                DOMElement $childNode
740 74
            ) use (
741
                $type
742 74
            ): void {
743
                $this->loadTypeWithCallback(
744 74
                    $type->getSchema(),
745
                    $childNode,
746 74
                    function (SimpleType $unType) use ($type): void {
747
                        $type->addUnion($unType);
748 74
                    }
749 74
                );
750
            }
751
        );
752 74
    }
753 74
754
    private function fillTypeNode(Type $type, DOMElement $node, bool $checkAbstract = false): void
755 74
    {
756 74
        if ($checkAbstract) {
757 74
            $type->setAbstract($node->getAttribute('abstract') === 'true' || $node->getAttribute('abstract') === '1');
758 74
        }
759 74
760 74
        self::againstDOMNodeList(
761 74
            $node,
762
            function (DOMElement $node, DOMElement $childNode) use ($type): void {
763 74
                switch ($childNode->localName) {
764 74
                    case 'restriction':
765 74
                        $this->loadRestriction($type, $childNode);
766 74
                        break;
767 74
                    case 'extension':
768
                        if ($type instanceof BaseComplexType) {
769 74
                            $this->loadExtension($type, $childNode);
770
                        }
771 74
                        break;
772
                    case 'simpleContent':
773 74
                    case 'complexContent':
774
                        $this->fillTypeNode($type, $childNode);
775 74
                        break;
776 74
                }
777
            }
778 74
        );
779 74
    }
780 74
781 74
    private function loadExtension(BaseComplexType $type, DOMElement $node): void
782 74
    {
783
        $extension = new Extension();
784
        $type->setExtension($extension);
785 74
786 74
        if ($node->hasAttribute('base')) {
787
            $this->findAndSetSomeBase(
788 74
                $type,
789
                $extension,
790
                $node
791
            );
792
        }
793
        $this->loadExtensionChildNodes($type, $node);
794
    }
795
796 74
    private function findAndSetSomeBase(
797 74
        Type $type,
798 74
        Base $setBaseOnThis,
799
        DOMElement $node
800 74
    ): void {
801
        /**
802
         * @var Type
803
         */
804 74
        $parent = $this->findSomeType($type, $node, 'base');
805 74
        $setBaseOnThis->setBase($parent);
806
    }
807
808
    private function loadExtensionChildNodes(
809
        BaseComplexType $type,
810 74
        DOMElement $node
811
    ): void {
812 74
        self::againstDOMNodeList(
813 74
            $node,
814 74
            function (
815 74
                DOMElement $node,
816 74
                DOMElement $childNode
817 74
            ) use (
818 74
                $type
819 74
            ): void {
820
                switch ($childNode->localName) {
821
                    case 'sequence':
822 74
                    case 'choice':
823 74
                    case 'all':
824 74
                        if ($type instanceof ElementContainer) {
825 74
                            $this->loadSequence(
826 74
                                $type,
827 74
                                $childNode
828 74
                            );
829
                        }
830 74
                        break;
831 74
                    case 'attribute':
832 74
                        $this->addAttributeFromAttributeOrRef(
833 74
                            $type,
834 74
                            $childNode,
835 74
                            $type->getSchema(),
836 74
                            $node
837
                        );
838 74
                        break;
839
                    case 'attributeGroup':
840 74
                        $this->findSomethingLikeAttributeGroup(
841
                            $type->getSchema(),
842 74
                            $node,
843
                            $childNode,
844 74
                            $type
845
                        );
846 74
                        break;
847 74
                }
848 74
            }
849 74
        );
850
    }
851 74
852 74
    private function loadRestriction(Type $type, DOMElement $node): void
853
    {
854
        $restriction = new Restriction();
855
        $type->setRestriction($restriction);
856
        if ($node->hasAttribute('base')) {
857 74
            $this->findAndSetSomeBase($type, $restriction, $node);
858 74
        } else {
859
            self::againstDOMNodeList(
860 74
                $node,
861 74
                function (
862 74
                    DOMElement $node,
863
                    DOMElement $childNode
864 74
                ) use (
865 74
                    $type,
866
                    $restriction
867 74
                ): void {
868
                    $this->loadTypeWithCallback(
869
                        $type->getSchema(),
870 74
                        $childNode,
871 74
                        function (Type $restType) use ($restriction): void {
872
                            $restriction->setBase($restType);
873
                        }
874
                    );
875
                }
876 74
            );
877
        }
878
        self::againstDOMNodeList(
879 74
            $node,
880 74
            function (
881
                DOMElement $node,
882 74
                DOMElement $childNode
883
            ) use (
884
                $restriction
885
            ): void {
886
                if (
887
                in_array(
888
                    $childNode->localName,
889
                    [
890
                        'enumeration',
891
                        'pattern',
892
                        'length',
893
                        'minLength',
894
                        'maxLength',
895 74
                        'minInclusive',
896
                        'maxInclusive',
897
                        'minExclusive',
898 74
                        'maxExclusive',
899 74
                        'fractionDigits',
900
                        'totalDigits',
901 74
                        'whiteSpace',
902 74
                    ],
903
                    true
904
                )
905
                ) {
906 74
                    $restriction->addCheck(
907
                        $childNode->localName,
908 74
                        [
909
                            'value' => $childNode->getAttribute('value'),
910
                            'doc' => $this->getDocumentation($childNode),
911
                        ]
912
                    );
913 74
                }
914
            }
915 74
        );
916 74
    }
917 74
918 74
    /**
919
     * @return mixed[]
920
     */
921
    private static function splitParts(DOMElement $node, string $typeName): array
922
    {
923
        $prefix = null;
924 74
        $name = $typeName;
925
        if (strpos($typeName, ':') !== false) {
926
            [$prefix, $name] = explode(':', $typeName);
927 74
        }
928 74
929 74
        /**
930
         * @psalm-suppress PossiblyNullArgument
931
         */
932
        $namespace = $node->lookupNamespaceUri($prefix);
933 74
934
        return [
935 74
            $name,
936
            $namespace,
937
            $prefix,
938
        ];
939
    }
940 74
941
    private function findAttributeItem(Schema $schema, DOMElement $node, string $typeName): AttributeItem
942
    {
943
        [$name, $namespace] = static::splitParts($node, $typeName);
944
945
        /**
946 74
         * @var string|null $namespace
947
         */
948 74
        $namespace = $namespace ?: $schema->getTargetNamespace();
949
950
        try {
951
            /**
952
             * @var AttributeItem $out
953
             */
954 74
            $out = $schema->findAttribute((string) $name, $namespace);
955
956 74
            return $out;
957
        } catch (TypeNotFoundException $e) {
958
            throw new TypeException(sprintf("Can't find %s named {%s}#%s, at line %d in %s ", 'attribute', $namespace, $name, $node->getLineNo(), $node->ownerDocument->documentURI), 0, $e);
959
        }
960
    }
961 74
962
    private function findAttributeGroup(Schema $schema, DOMElement $node, string $typeName): AttributeGroup
963
    {
964
        [$name, $namespace] = static::splitParts($node, $typeName);
965
966
        /**
967 74
         * @var string|null $namespace
968
         */
969 74
        $namespace = $namespace ?: $schema->getTargetNamespace();
970
971
        try {
972
            /**
973
             * @var AttributeGroup $out
974
             */
975 74
            $out = $schema->findAttributeGroup((string) $name, $namespace);
976
977 74
            return $out;
978
        } catch (TypeNotFoundException $e) {
979
            throw new TypeException(sprintf("Can't find %s named {%s}#%s, at line %d in %s ", 'attributegroup', $namespace, $name, $node->getLineNo(), $node->ownerDocument->documentURI), 0, $e);
980
        }
981
    }
982 74
983
    private function findElement(Schema $schema, DOMElement $node, string $typeName): ElementDef
984
    {
985 74
        [$name, $namespace] = static::splitParts($node, $typeName);
986
987
        /**
988
         * @var string|null $namespace
989
         */
990
        $namespace = $namespace ?: $schema->getTargetNamespace();
991 74
992
        try {
993 74
            return $schema->findElement((string) $name, $namespace);
994
        } catch (TypeNotFoundException $e) {
995
            throw new TypeException(sprintf("Can't find %s named {%s}#%s, at line %d in %s ", 'element', $namespace, $name, $node->getLineNo(), $node->ownerDocument->documentURI), 0, $e);
996
        }
997
    }
998 74
999
    private function findGroup(Schema $schema, DOMElement $node, string $typeName): Group
1000
    {
1001
        [$name, $namespace] = static::splitParts($node, $typeName);
1002
1003
        /**
1004 74
         * @var string|null $namespace
1005
         */
1006 74
        $namespace = $namespace ?: $schema->getTargetNamespace();
1007
1008
        try {
1009
            /**
1010
             * @var Group $out
1011
             */
1012 74
            $out = $schema->findGroup((string) $name, $namespace);
1013
1014 74
            return $out;
1015
        } catch (TypeNotFoundException $e) {
1016
            throw new TypeException(sprintf("Can't find %s named {%s}#%s, at line %d in %s ", 'group', $namespace, $name, $node->getLineNo(), $node->ownerDocument->documentURI), 0, $e);
1017
        }
1018
    }
1019 74
1020
    private function findType(Schema $schema, DOMElement $node, string $typeName): SchemaItem
1021
    {
1022
        [$name, $namespace] = static::splitParts($node, $typeName);
1023 74
1024 1
        /**
1025 1
         * @var string|null $namespace
1026
         */
1027 74
        $namespace = $namespace ?: $schema->getTargetNamespace();
1028
1029 74
        $tryFindType = static function (Schema $schema, string $name, ?string $namespace): ?SchemaItem {
1030 74
            try {
1031 74
                return $schema->findType($name, $namespace);
1032 74
            } catch (TypeNotFoundException $e) {
1033
                return null;
1034
            }
1035
        };
1036
1037
        $interestingSchemas = array_merge([$schema], $this->loadedSchemas[$namespace] ?? []);
1038
        foreach ($interestingSchemas as $interestingSchema) {
1039 74
            if ($result = $tryFindType($interestingSchema, $name, $namespace)) {
1040
                return $result;
1041 74
            }
1042
        }
1043
1044 74
        throw new TypeException(sprintf("Can't find %s named {%s}#%s, at line %d in %s ", 'type', $namespace, $name, $node->getLineNo(), $node->ownerDocument->documentURI));
1045
    }
1046
1047
    private function loadElementDef(Schema $schema, DOMElement $node): Closure
1048
    {
1049 74
        return $this->loadAttributeOrElementDef($schema, $node, false);
1050 74
    }
1051 74
1052
    private function fillItem(Item $element, DOMElement $node): void
1053
    {
1054
        /**
1055
         * @var bool
1056 74
         */
1057 74
        $skip = false;
1058
        self::againstDOMNodeList(
1059
            $node,
1060 74
            function (
1061 74
                DOMElement $node,
1062 74
                DOMElement $childNode
1063
            ) use (
1064 74
                $element,
1065
                &$skip
1066
            ): void {
1067 74
                if (
1068
                    !$skip &&
1069
                    in_array(
1070 74
                        $childNode->localName,
1071 74
                        [
1072 74
                            'complexType',
1073
                            'simpleType',
1074 74
                        ],
1075 74
                        true
1076
                    )
1077 74
                ) {
1078
                    $this->loadTypeWithCallback(
1079 74
                        $element->getSchema(),
1080
                        $childNode,
1081 74
                        function (Type $type) use ($element): void {
1082 74
                            $element->setType($type);
1083
                        }
1084 74
                    );
1085 74
                    $skip = true;
1086
                }
1087 74
            }
1088
        );
1089 74
        if ($skip) {
1090
            return;
1091
        }
1092
        $this->fillItemNonLocalType($element, $node);
1093 74
    }
1094
1095
    private function fillItemNonLocalType(Item $element, DOMElement $node): void
1096
    {
1097
        if ($node->getAttribute('type')) {
1098 74
            /**
1099 74
             * @var Type
1100 74
             */
1101 74
            $type = $this->findSomeType($element, $node, 'type');
1102
        } else {
1103
            /**
1104
             * @var Type
1105 74
             */
1106 74
            $type = $this->findSomeTypeFromAttribute(
1107
                $element,
1108 74
                $node,
1109
                ($node->lookupPrefix(self::XSD_NS).':anyType')
1110
            );
1111
        }
1112 74
1113 74
        $element->setType($type);
1114 74
    }
1115 1
1116
    private function loadImport(
1117
        Schema $schema,
1118
        DOMElement $node
1119 74
    ): Closure {
1120
        $namespace = $node->getAttribute('namespace');
1121 3
        $schemaLocation = $node->getAttribute('schemaLocation');
1122 3
        if (!$schemaLocation && isset($this->knownNamespaceSchemaLocations[$namespace])) {
1123 3
            $schemaLocation = $this->knownNamespaceSchemaLocations[$namespace];
1124
        }
1125
1126 3
        // postpone schema loading
1127 74
        if ($namespace && !$schemaLocation && !isset(self::$globalSchemaInfo[$namespace])) {
1128 1
            return function () use ($schema, $namespace): void {
1129 1
                if (!empty($this->loadedSchemas[$namespace])) {
1130
                    foreach ($this->loadedSchemas[$namespace] as $s) {
1131
                        $schema->addSchema($s, $namespace);
1132
                    }
1133 74
                }
1134 74
            };
1135
        } elseif ($namespace && !$schemaLocation && !empty($this->loadedSchemas[$namespace])) {
1136 74
            foreach ($this->loadedSchemas[$namespace] as $s) {
1137 74
                $schema->addSchema($s, $namespace);
1138
            }
1139
        }
1140 74
1141
        $base = urldecode($node->ownerDocument->documentURI);
0 ignored issues
show
Bug introduced by
It seems like $node->ownerDocument->documentURI can also be of type null; however, parameter $string of urldecode() 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

1141
        $base = urldecode(/** @scrutinizer ignore-type */ $node->ownerDocument->documentURI);
Loading history...
1142
        $file = UrlUtils::resolveRelativeUrl($base, $schemaLocation);
1143 3
1144
        if (isset($this->loadedFiles[$file])) {
1145
            $schema->addSchema($this->loadedFiles[$file]);
1146 3
1147
            return function (): void {
1148
            };
1149
        }
1150 3
1151 2
        return $this->loadImportFresh($namespace, $schema, $file);
1152 2
    }
1153 2
1154
    private function createOrUseSchemaForNs(
1155 1
        Schema $schema,
1156
        string $namespace
1157
    ): Schema {
1158 3
        if (('' !== trim($namespace))) {
1159
            $newSchema = new Schema();
1160
            $newSchema->addSchema($this->getGlobalSchema());
1161 3
            $schema->addSchema($newSchema);
1162
        } else {
1163
            $newSchema = $schema;
1164
        }
1165
1166
        return $newSchema;
1167 3
    }
1168 3
1169 3
    private function loadImportFresh(
1170
        string $namespace,
1171
        Schema $schema,
1172 3
        string $file
1173
    ): Closure {
1174 3
        return function () use ($namespace, $schema, $file): void {
1175
            $dom = $this->getDOM(
1176 3
                $this->knownLocationSchemas[$file]
1177
                    ?? $file
1178 3
            );
1179 3
1180
            $schemaNew = $this->createOrUseSchemaForNs($schema, $namespace);
1181 3
1182
            $this->setLoadedFile($file, $schemaNew);
1183
1184
            $callbacks = $this->schemaNode($schemaNew, $dom->documentElement, $schema);
1185
1186
            foreach ($callbacks as $callback) {
1187
                $callback();
1188
            }
1189 74
        };
1190
    }
1191 74
1192 74
    /**
1193 74
     * @var Schema|null
1194
     */
1195
    protected $globalSchema;
1196
1197 74
    public function getGlobalSchema(): Schema
1198 74
    {
1199 74
        if (!($this->globalSchema instanceof Schema)) {
1200 74
            $callbacks = [];
1201
            $globalSchemas = [];
1202 74
            /**
1203 74
             * @var string $namespace
1204 74
             */
1205
            foreach (self::$globalSchemaInfo as $namespace => $uri) {
1206 74
                $this->setLoadedFile(
1207 74
                    $uri,
1208
                    $globalSchemas[$namespace] = $schema = new Schema()
1209
                );
1210 74
                $this->setLoadedSchema($namespace, $schema);
1211 74
                if ($namespace === self::XSD_NS) {
1212
                    $this->globalSchema = $schema;
1213 74
                }
1214 74
                $xml = $this->getDOM($this->knownLocationSchemas[$uri]);
1215 74
                $callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement));
1216
            }
1217 74
1218 74
            $globalSchemas[(string) static::XSD_NS]->addType(new SimpleType($globalSchemas[(string) static::XSD_NS], 'anySimpleType'));
1219 74
            $globalSchemas[(string) static::XSD_NS]->addType(new SimpleType($globalSchemas[(string) static::XSD_NS], 'anyType'));
1220
1221
            $globalSchemas[(string) static::XML_NS]->addSchema(
1222
                $globalSchemas[(string) static::XSD_NS],
1223
                (string) static::XSD_NS
1224
            );
1225 74
            $globalSchemas[(string) static::XSD_NS]->addSchema(
1226 74
                $globalSchemas[(string) static::XML_NS],
1227
                (string) static::XML_NS
1228
            );
1229
1230 74
            /**
1231
             * @var Closure $callback
1232
             */
1233
            foreach ($callbacks as $callback) {
1234 74
                $callback();
1235
            }
1236
        }
1237
1238
        if (!($this->globalSchema instanceof Schema)) {
1239
            throw new TypeException('Global schema not discovered');
1240 2
        }
1241
1242 2
        return $this->globalSchema;
1243 2
    }
1244
1245 2
    /**
1246 2
     * @param DOMNode[] $nodes
1247
     */
1248
    public function readNodes(array $nodes, string $file = null): Schema
1249 2
    {
1250 2
        $rootSchema = new Schema();
1251 2
        $rootSchema->addSchema($this->getGlobalSchema());
1252 2
1253 2
        if ($file !== null) {
1254
            $this->setLoadedFile($file, $rootSchema);
1255 2
        }
1256
1257 2
        $all = [];
1258
        foreach ($nodes as $k => $node) {
1259 2
            if (($node instanceof \DOMElement) && $node->namespaceURI === self::XSD_NS && $node->localName == 'schema') {
1260 2
                $holderSchema = new Schema();
1261
                $holderSchema->addSchema($this->getGlobalSchema());
1262
1263
                $this->setLoadedSchemaFromElement($node, $holderSchema);
1264 2
1265 2
                $rootSchema->addSchema($holderSchema);
1266
1267
                $callbacks = $this->schemaNode($holderSchema, $node);
1268 2
                $all = array_merge($callbacks, $all);
1269
            }
1270
        }
1271 72
1272
        foreach ($all as $callback) {
1273 72
            call_user_func($callback);
1274 72
        }
1275
1276 72
        return $rootSchema;
1277 71
    }
1278
1279
    public function readNode(DOMElement $node, string $file = null): Schema
1280 72
    {
1281
        $rootSchema = new Schema();
1282 72
        $rootSchema->addSchema($this->getGlobalSchema());
1283
1284 72
        if ($file !== null) {
1285 66
            $this->setLoadedFile($file, $rootSchema);
1286
        }
1287
1288 72
        $this->setLoadedSchemaFromElement($node, $rootSchema);
1289
1290
        $callbacks = $this->schemaNode($rootSchema, $node);
1291
1292
        foreach ($callbacks as $callback) {
1293
            call_user_func($callback);
1294 68
        }
1295
1296 68
        return $rootSchema;
1297 68
    }
1298 68
1299 1
    /**
1300
     * @throws IOException
1301 67
     */
1302 67
    public function readString(string $content, string $file = 'schema.xsd'): Schema
1303
    {
1304 67
        $xml = new DOMDocument('1.0', 'UTF-8');
1305
        libxml_use_internal_errors(true);
1306
        if (!@$xml->loadXML($content)) {
1307
            throw new IOException("Can't load the schema", 0, $this->extractErrorMessage());
1308
        }
1309
        libxml_use_internal_errors(false);
1310 5
        $xml->documentURI = $file;
1311
1312 5
        return $this->readNode($xml->documentElement, $file);
1313
    }
1314 4
1315
    /**
1316
     * @throws IOException
1317
     */
1318
    public function readFile(string $file): Schema
1319
    {
1320 75
        $xml = $this->getDOM($file);
1321
1322 75
        return $this->readNode($xml->documentElement, $file);
1323 75
    }
1324 75
1325 1
    /**
1326 1
     * @throws IOException
1327
     */
1328 74
    private function getDOM(string $file): DOMDocument
1329
    {
1330 74
        $xml = new DOMDocument('1.0', 'UTF-8');
1331
        libxml_use_internal_errors(true);
1332
        if (!@$xml->load($file)) {
1333 74
            libxml_use_internal_errors(false);
1334
            throw new IOException("Can't load the file '$file'", 0, $this->extractErrorMessage());
1335
        }
1336
        libxml_use_internal_errors(false);
1337 74
1338 74
        return $xml;
1339
    }
1340
1341
    private static function againstDOMNodeList(
1342 74
        DOMElement $node,
1343
        Closure $againstNodeList
1344 74
    ): void {
1345 74
        $limit = $node->childNodes->length;
1346 74
        for ($i = 0; $i < $limit; ++$i) {
1347 74
            /**
1348
             * @var DOMNode
1349
             */
1350
            $childNode = $node->childNodes->item($i);
1351 74
1352
            if ($childNode instanceof DOMElement) {
1353 74
                $againstNodeList(
1354
                    $node,
1355
                    $childNode
1356
                );
1357
            }
1358
        }
1359
    }
1360
1361 74
    private function loadTypeWithCallback(
1362
        Schema $schema,
1363 74
        DOMElement $childNode,
1364 74
        Closure $callback
1365 74
    ): void {
1366 74
        /**
1367 74
         * @var Closure|null $func
1368 74
         */
1369 74
        $func = null;
1370
1371
        switch ($childNode->localName) {
1372 74
            case 'complexType':
1373 74
                $func = $this->loadComplexType($schema, $childNode, $callback);
1374
                break;
1375 74
            case 'simpleType':
1376
                $func = $this->loadSimpleType($schema, $childNode, $callback);
1377 74
                break;
1378
        }
1379
1380
        if ($func instanceof Closure) {
1381 74
            call_user_func($func);
1382 74
        }
1383
    }
1384 74
1385
    private function loadElement(
1386 74
        Schema $schema,
1387 74
        DOMElement $node
1388 74
    ): Element {
1389
        $element = new Element($schema, $node->getAttribute('name'));
1390 74
        $element->setDoc($this->getDocumentation($node));
1391 74
1392
        $this->fillItem($element, $node);
1393 74
1394 74
        self::maybeSetMax($element, $node);
1395
        self::maybeSetMin($element, $node);
1396
        self::maybeSetFixed($element, $node);
1397 74
        self::maybeSetDefault($element, $node);
1398 4
1399
        $xp = new \DOMXPath($node->ownerDocument);
0 ignored issues
show
Bug introduced by
It seems like $node->ownerDocument can also be of type null; however, parameter $document of DOMXPath::__construct() does only seem to accept DOMDocument, 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

1399
        $xp = new \DOMXPath(/** @scrutinizer ignore-type */ $node->ownerDocument);
Loading history...
1400 74
        $xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema');
1401 5
1402
        if ($xp->query('ancestor::xs:choice', $node)->length) {
1403
            $element->setMin(0);
1404 74
        }
1405
1406 74
        if ($node->hasAttribute('nillable')) {
1407 74
            $element->setNil($node->getAttribute('nillable') == 'true');
1408
        }
1409
        if ($node->hasAttribute('form')) {
1410 74
            $element->setQualified($node->getAttribute('form') == 'qualified');
1411
        }
1412
1413 74
        $parentNode = $node->parentNode;
1414
1415
        if ($parentNode->localName != 'schema' || $parentNode->namespaceURI != 'http://www.w3.org/2001/XMLSchema') {
1416
            $element->setLocal(true);
1417
        }
1418
1419 74
        return $element;
1420 74
    }
1421 74
1422 74
    private function addAttributeFromAttributeOrRef(
1423
        BaseComplexType $type,
1424
        DOMElement $childNode,
1425 74
        Schema $schema,
1426 74
        DOMElement $node
1427
    ): void {
1428 74
        $attribute = $this->getAttributeFromAttributeOrRef(
1429
            $childNode,
1430
            $schema,
1431
            $node
1432
        );
1433
1434 74
        $type->addAttribute($attribute);
1435 74
    }
1436 74
1437
    private function findSomethingLikeAttributeGroup(
1438 74
        Schema $schema,
1439
        DOMElement $node,
1440 74
        DOMElement $childNode,
1441 74
        AttributeContainer $addToThis
1442
    ): void {
1443 74
        $attribute = $this->findAttributeGroup($schema, $node, $childNode->getAttribute('ref'));
1444
        $addToThis->addAttribute($attribute);
1445 74
    }
1446 74
1447
    private function setLoadedFile(string $key, Schema $schema): void
1448 74
    {
1449
        $this->loadedFiles[$key] = $schema;
1450 74
    }
1451
1452 74
    private function setLoadedSchemaFromElement(DOMElement $node, Schema $schema): void
1453 74
    {
1454
        if ($node->hasAttribute('targetNamespace')) {
1455 74
            $this->setLoadedSchema($node->getAttribute('targetNamespace'), $schema);
1456 74
        }
1457
    }
1458 74
1459
    private function setLoadedSchema(string $namespace, Schema $schema): void
1460 74
    {
1461
        if (!isset($this->loadedSchemas[$namespace])) {
1462
            $this->loadedSchemas[$namespace] = [];
1463
        }
1464
        if (!in_array($schema, $this->loadedSchemas[$namespace], true)) {
1465 74
            $this->loadedSchemas[$namespace][] = $schema;
1466
        }
1467 74
    }
1468 74
1469
    private function setSchemaThingsFromNode(
1470
        Schema $schema,
1471
        DOMElement $node,
1472 74
        Schema $parent = null
1473 74
    ): void {
1474 74
        $schema->setDoc($this->getDocumentation($node));
1475 74
1476
        if ($node->hasAttribute('targetNamespace')) {
1477
            $schema->setTargetNamespace($node->getAttribute('targetNamespace'));
1478
        } elseif ($parent instanceof Schema) {
1479
            $schema->setTargetNamespace($parent->getTargetNamespace());
1480
        }
1481
        $schema->setElementsQualification($node->getAttribute('elementFormDefault') == 'qualified');
1482
        $schema->setAttributesQualification($node->getAttribute('attributeFormDefault') == 'qualified');
1483
        $schema->setDoc($this->getDocumentation($node));
1484
    }
1485
}
1486