Completed
Push — master ( 719fae...7f0a0e )
by Asmir
16s queued 14s
created

SchemaReader::findGroup()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 18
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

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

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

1393
        $xp = new \DOMXPath(/** @scrutinizer ignore-type */ $node->ownerDocument);
Loading history...
1394 74
        $xp->registerNamespace('xs', self::XSD_NS);
1395
1396
        if ($xp->query('ancestor::xs:choice', $node)->length) {
1397 74
            $element->setMin(0);
1398 4
        }
1399
1400 74
        if ($node->hasAttribute('nillable')) {
1401 5
            $element->setNil($node->getAttribute('nillable') == 'true');
1402
        }
1403
        if ($node->hasAttribute('form')) {
1404 74
            $element->setQualified($node->getAttribute('form') == 'qualified');
1405
        }
1406 74
1407 74
        $parentNode = $node->parentNode;
1408
        if ($parentNode->localName != 'schema' || $parentNode->namespaceURI != self::XSD_NS) {
1409
            $element->setLocal(true);
1410 74
        }
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