Passed
Push — master ( abfc58...408208 )
by Asmir
04:49 queued 02:26
created

SchemaReader::loadSimpleType()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 18
c 1
b 0
f 0
dl 0
loc 27
ccs 18
cts 18
cp 1
rs 9.3554
cc 5
nc 2
nop 3
crap 5
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\InterfaceSetMinMax;
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 = [];
57
58
    /**
59
     * @var Schema[][]
60
     */
61
    private $loadedSchemas = [];
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 = [
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 = [];
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 85
    public function __construct(DocumentationReader $documentationReader = null)
114
    {
115 85
        if (null === $documentationReader) {
116 85
            $documentationReader = new StandardDocumentationReader();
117
        }
118 85
        $this->documentationReader = $documentationReader;
119 85
    }
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 74
    private function loadAttributeGroup(
145
        Schema $schema,
146
        DOMElement $node
147
    ): Closure {
148 74
        $attGroup = new AttributeGroup($schema, $node->getAttribute('name'));
149 74
        $attGroup->setDoc($this->getDocumentation($node));
150 74
        $schema->addAttributeGroup($attGroup);
151
152
        return function () use ($schema, $node, $attGroup): void {
153 74
            SchemaReader::againstDOMNodeList(
154 74
                $node,
155
                function (
156
                    DOMElement $node,
157
                    DOMElement $childNode
158
                ) use (
159 74
                    $schema,
160 74
                    $attGroup
161
                ): void {
162 74
                    switch ($childNode->localName) {
163 74
                        case 'attribute':
164 74
                            $attribute = $this->getAttributeFromAttributeOrRef(
165 74
                                $childNode,
166 74
                                $schema,
167 74
                                $node
168
                            );
169 74
                            $attGroup->addAttribute($attribute);
170 74
                            break;
171 74
                        case 'attributeGroup':
172 1
                            $this->findSomethingLikeAttributeGroup(
173 1
                                $schema,
174 1
                                $node,
175 1
                                $childNode,
176 1
                                $attGroup
177
                            );
178 1
                            break;
179
                    }
180 74
                }
181
            );
182 74
        };
183
    }
184
185 74
    private function getAttributeFromAttributeOrRef(
186
        DOMElement $childNode,
187
        Schema $schema,
188
        DOMElement $node
189
    ): AttributeItem {
190 74
        if ($childNode->hasAttribute('ref')) {
191 74
            $attribute = $this->findAttributeItem($schema, $node, $childNode->getAttribute('ref'));
192
        } else {
193
            /**
194
             * @var Attribute
195
             */
196 74
            $attribute = $this->loadAttribute($schema, $childNode);
197
        }
198
199 74
        return $attribute;
200
    }
201
202 74
    private function loadAttribute(
203
        Schema $schema,
204
        DOMElement $node
205
    ): Attribute {
206 74
        $attribute = new Attribute($schema, $node->getAttribute('name'));
207 74
        $attribute->setDoc($this->getDocumentation($node));
208 74
        $this->fillItem($attribute, $node);
209
210 74
        if ($node->hasAttribute('nillable')) {
211 1
            $attribute->setNil($node->getAttribute('nillable') == 'true');
212
        }
213 74
        if ($node->hasAttribute('form')) {
214 1
            $attribute->setQualified($node->getAttribute('form') == 'qualified');
215
        }
216 74
        if ($node->hasAttribute('use')) {
217 74
            $attribute->setUse($node->getAttribute('use'));
218
        }
219
220 74
        return $attribute;
221
    }
222
223 74
    private function loadAttributeOrElementDef(
224
        Schema $schema,
225
        DOMElement $node,
226
        bool $attributeDef
227
    ): Closure {
228 74
        $name = $node->getAttribute('name');
229 74
        if ($attributeDef) {
230 74
            $attribute = new AttributeDef($schema, $name);
231 74
            $schema->addAttribute($attribute);
232
        } else {
233 74
            $attribute = new ElementDef($schema, $name);
234 74
            $attribute->setDoc($this->getDocumentation($node));
235 74
            $schema->addElement($attribute);
236
        }
237
238
        return function () use ($attribute, $node): void {
239 74
            $this->fillItem($attribute, $node);
240 74
        };
241
    }
242
243 74
    private function loadAttributeDef(Schema $schema, DOMElement $node): Closure
244
    {
245 74
        return $this->loadAttributeOrElementDef($schema, $node, true);
246
    }
247
248 74
    private function getDocumentation(DOMElement $node): string
249
    {
250 74
        return $this->documentationReader->get($node);
251
    }
252
253
    /**
254
     * @return Closure[]
255
     */
256 74
    private function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null): array
257
    {
258 74
        $this->setSchemaThingsFromNode($schema, $node, $parent);
259 74
        $functions = [];
260
261 74
        self::againstDOMNodeList(
262 74
            $node,
263
            function (
264
                DOMElement $node,
265
                DOMElement $childNode
266
            ) use (
267 74
                $schema,
268 74
                &$functions
269
            ): void {
270 74
                $callback = null;
271
272 74
                switch ($childNode->localName) {
273 74
                    case 'attributeGroup':
274 74
                        $callback = $this->loadAttributeGroup($schema, $childNode);
275 74
                        break;
276 74
                    case 'include':
277 74
                    case 'import':
278 74
                        $callback = $this->loadImport($schema, $childNode);
279 74
                        break;
280 74
                    case 'element':
281 74
                        $callback = $this->loadElementDef($schema, $childNode);
282 74
                        break;
283 74
                    case 'attribute':
284 74
                        $callback = $this->loadAttributeDef($schema, $childNode);
285 74
                        break;
286 74
                    case 'group':
287 74
                        $callback = $this->loadGroup($schema, $childNode);
288 74
                        break;
289 74
                    case 'complexType':
290 74
                        $callback = $this->loadComplexType($schema, $childNode);
291 74
                        break;
292 74
                    case 'simpleType':
293 74
                        $callback = $this->loadSimpleType($schema, $childNode);
294 74
                        break;
295
                }
296
297 74
                if ($callback instanceof Closure) {
298 74
                    $functions[] = $callback;
299
                }
300 74
            }
301
        );
302
303 74
        return $functions;
304
    }
305
306 74
    private function loadGroupRef(Group $referenced, DOMElement $node): GroupRef
307
    {
308 74
        $ref = new GroupRef($referenced);
309 74
        $ref->setDoc($this->getDocumentation($node));
310
311 74
        self::maybeSetMax($ref, $node);
312 74
        self::maybeSetMin($ref, $node);
313
314 74
        return $ref;
315
    }
316
317 74
    private static function maybeSetMax(InterfaceSetMinMax $ref, DOMElement $node): void
318
    {
319 74
        if ($node->hasAttribute('maxOccurs')) {
320 74
            $ref->setMax($node->getAttribute('maxOccurs') == 'unbounded' ? -1 : (int) $node->getAttribute('maxOccurs'));
321
        }
322 74
    }
323
324 74
    private static function maybeSetMin(InterfaceSetMinMax $ref, DOMElement $node): void
325
    {
326 74
        if ($node->hasAttribute('minOccurs')) {
327 74
            $ref->setMin((int) $node->getAttribute('minOccurs'));
328 74
            if ($ref->getMin() > $ref->getMax() && $ref->getMax() !== -1) {
329 7
                $ref->setMax($ref->getMin());
330
            }
331
        }
332 74
    }
333
334 74
    private static function maybeSetDefault(InterfaceSetDefault $ref, DOMElement $node): void
335
    {
336 74
        if ($node->hasAttribute('default')) {
337 1
            $ref->setDefault($node->getAttribute('default'));
338
        }
339 74
    }
340
341 74
    private function loadSequence(ElementContainer $elementContainer, DOMElement $node, int $max = null, int $min = null): void
342
    {
343
        $max =
344
            (
345 74
                (is_int($max) && (bool) $max) ||
346 74
                $node->getAttribute('maxOccurs') == 'unbounded' ||
347 74
                $node->getAttribute('maxOccurs') > 1
348
            )
349 74
                ? 2
350 74
                : null;
351
        $min =
352
            (
353 74
                $min === null &&
354 74
                !$node->hasAttribute('minOccurs')
355
            )
356 74
                ? null
357 74
                : (int) max((int) $min, $node->getAttribute('minOccurs'));
358
359 74
        self::againstDOMNodeList(
360 74
            $node,
361
            function (
362
                DOMElement $node,
363
                DOMElement $childNode
364
            ) use (
365 74
                $elementContainer,
366 74
                $max,
367 74
                $min
368
            ): void {
369 74
                $this->loadSequenceChildNode(
370 74
                    $elementContainer,
371 74
                    $node,
372 74
                    $childNode,
373 74
                    $max,
374 74
                    $min
375
                );
376 74
            }
377
        );
378 74
    }
379
380 74
    private function loadSequenceChildNode(
381
        ElementContainer $elementContainer,
382
        DOMElement $node,
383
        DOMElement $childNode,
384
        ?int $max,
385
        ?int $min = null
386
    ): void {
387 74
        switch ($childNode->localName) {
388 74
            case 'sequence':
389 74
            case 'choice':
390 74
            case 'all':
391 74
                $this->loadSequence(
392 74
                    $elementContainer,
393 74
                    $childNode,
394 74
                    $max,
395 74
                    $min
396
                );
397 74
                break;
398 74
            case 'element':
399 74
                $this->loadSequenceChildNodeLoadElement(
400 74
                    $elementContainer,
401 74
                    $node,
402 74
                    $childNode,
403 74
                    $max,
404 74
                    $min
405
                );
406 74
                break;
407 74
            case 'group':
408 74
                $this->addGroupAsElement(
409 74
                    $elementContainer->getSchema(),
410 74
                    $node,
411 74
                    $childNode,
412 74
                    $elementContainer
413
                );
414 74
                break;
415
        }
416 74
    }
417
418 74
    private function loadSequenceChildNodeLoadElement(
419
        ElementContainer $elementContainer,
420
        DOMElement $node,
421
        DOMElement $childNode,
422
        ?int $max,
423
        ?int $min
424
    ): void {
425 74
        if ($childNode->hasAttribute('ref')) {
426 74
            $elementDef = $this->findElement($elementContainer->getSchema(), $node, $childNode->getAttribute('ref'));
427 74
            $element = new ElementRef($elementDef);
428 74
            $element->setDoc($this->getDocumentation($childNode));
429
430 74
            self::maybeSetMax($element, $childNode);
431 74
            self::maybeSetMin($element, $childNode);
432
433 74
            $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

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

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

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