Passed
Pull Request — master (#70)
by Axel
11:23 queued 01:22
created

SchemaReader::setLoadedSchemaFromElement()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 2
nc 2
nop 2
crap 2
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
        'http://www.w3.org/2000/09/xmldsig#' => (
94
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
95
        ),
96
    ];
97
98
    /**
99
     * @var string[]
100 2
     */
101
    protected static $globalSchemaInfo = [
102 2
        self::XML_NS => 'http://www.w3.org/2001/xml.xsd',
103
        self::XSD_NS => 'http://www.w3.org/2001/XMLSchema.xsd',
104 2
    ];
105 1
106
    private function extractErrorMessage(): \Exception
107 2
    {
108 2
        $errors = [];
109
110 2
        foreach (libxml_get_errors() as $error) {
111
            $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);
112
        }
113 85
        $e = new \Exception(implode('; ', $errors));
114
        libxml_use_internal_errors(false);
115 85
116 85
        return $e;
117
    }
118 85
119 85
    public function __construct(DocumentationReader $documentationReader = null)
120
    {
121
        if (null === $documentationReader) {
122
            $documentationReader = new StandardDocumentationReader();
123
        }
124
        $this->documentationReader = $documentationReader;
125
    }
126
127 1
    /**
128
     * Override remote location with a local file.
129 1
     *
130 1
     * @param string $remote remote schema URL
131
     * @param string $local  local file path
132
     */
133
    public function addKnownSchemaLocation(string $remote, string $local): void
134
    {
135
        $this->knownLocationSchemas[$remote] = $local;
136
    }
137
138
    /**
139 1
     * Specify schema location by namespace.
140
     * This can be used for schemas which import namespaces but do not specify schemaLocation attributes.
141 1
     *
142 1
     * @param string $namespace namespace
143
     * @param string $location  schema URL
144 74
     */
145
    public function addKnownNamespaceSchemaLocation(string $namespace, string $location): void
146
    {
147
        $this->knownNamespaceSchemaLocations[$namespace] = $location;
148 74
    }
149 74
150 74
    private function loadAttributeGroup(
151
        Schema $schema,
152
        DOMElement $node
153 74
    ): Closure {
154 74
        $attGroup = new AttributeGroup($schema, $node->getAttribute('name'));
155
        $attGroup->setDoc($this->getDocumentation($node));
156
        $schema->addAttributeGroup($attGroup);
157
158
        return function () use ($schema, $node, $attGroup): void {
159 74
            SchemaReader::againstDOMNodeList(
160 74
                $node,
161
                function (
162 74
                    DOMElement $node,
163 74
                    DOMElement $childNode
164 74
                ) use (
165 74
                    $schema,
166 74
                    $attGroup
167 74
                ): void {
168
                    switch ($childNode->localName) {
169 74
                        case 'attribute':
170 74
                            $attribute = $this->getAttributeFromAttributeOrRef(
171 74
                                $childNode,
172 1
                                $schema,
173 1
                                $node
174 1
                            );
175 1
                            $attGroup->addAttribute($attribute);
176 1
                            break;
177
                        case 'attributeGroup':
178 1
                            $this->findSomethingLikeAttributeGroup(
179
                                $schema,
180 74
                                $node,
181
                                $childNode,
182 74
                                $attGroup
183
                            );
184
                            break;
185 74
                    }
186
                }
187
            );
188
        };
189
    }
190 74
191 74
    private function getAttributeFromAttributeOrRef(
192
        DOMElement $childNode,
193
        Schema $schema,
194
        DOMElement $node
195
    ): AttributeItem {
196 74
        if ($childNode->hasAttribute('ref')) {
197
            $attribute = $this->findAttributeItem($schema, $node, $childNode->getAttribute('ref'));
198
        } else {
199 74
            /**
200
             * @var Attribute
201
             */
202 74
            $attribute = $this->loadAttribute($schema, $childNode);
203
        }
204
205
        return $attribute;
206 74
    }
207 74
208 74
    private function loadAttribute(Schema $schema, DOMElement $node): Attribute
209
    {
210 74
        $attribute = new Attribute($schema, $node->getAttribute('name'));
211 1
        $attribute->setDoc($this->getDocumentation($node));
212
        $this->fillItem($attribute, $node);
213 74
        $this->fillAttribute($attribute, $node);
214 1
215
        return $attribute;
216 74
    }
217 74
218
    private function fillAttribute(Attribute $attribute, DOMElement $node): void
219
    {
220 74
        if ($node->hasAttribute('fixed')) {
221
            $attribute->setFixed($node->getAttribute('fixed'));
222
        }
223 74
        if ($node->hasAttribute('default')) {
224
            $attribute->setDefault($node->getAttribute('default'));
225
        }
226
        if ($node->hasAttribute('nillable')) {
227
            $attribute->setNil($node->getAttribute('nillable') == 'true');
228 74
        }
229 74
        if ($node->hasAttribute('form')) {
230 74
            $attribute->setQualified($node->getAttribute('form') == 'qualified');
231 74
        }
232
        if ($node->hasAttribute('use')) {
233 74
            $attribute->setUse($node->getAttribute('use'));
234 74
        }
235 74
    }
236
237
    private function loadAttributeOrElementDef(
238
        Schema $schema,
239 74
        DOMElement $node,
240 74
        bool $isAttribute
241
    ): Closure {
242
        $name = $node->getAttribute('name');
243 74
        if ($isAttribute) {
244
            $attribute = new AttributeDef($schema, $name);
245 74
            $attribute->setDoc($this->getDocumentation($node));
246
            $this->fillAttribute($attribute, $node);
0 ignored issues
show
Bug introduced by
$attribute of type GoetasWebservices\XML\XS...\Attribute\AttributeDef is incompatible with the type GoetasWebservices\XML\XS...ema\Attribute\Attribute expected by parameter $attribute of GoetasWebservices\XML\XS...Reader::fillAttribute(). ( Ignorable by Annotation )

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

246
            $this->fillAttribute(/** @scrutinizer ignore-type */ $attribute, $node);
Loading history...
247
            $schema->addAttribute($attribute);
248 74
        } else {
249
            $attribute = new ElementDef($schema, $name);
250 74
            $attribute->setDoc($this->getDocumentation($node));
251
            $this->fillElement($attribute, $node);
252
            $schema->addElement($attribute);
253
        }
254
255
        return function () use ($attribute, $node): void {
256 74
            $this->fillItem($attribute, $node);
257
        };
258 74
    }
259 74
260
    private function loadElementDef(Schema $schema, DOMElement $node): Closure
261 74
    {
262 74
        return $this->loadAttributeOrElementDef($schema, $node, false);
263
    }
264
265
    private function loadAttributeDef(Schema $schema, DOMElement $node): Closure
266
    {
267 74
        return $this->loadAttributeOrElementDef($schema, $node, true);
268 74
    }
269
270 74
    private function getDocumentation(DOMElement $node): string
271
    {
272 74
        return $this->documentationReader->get($node);
273 74
    }
274 74
275 74
    /**
276 74
     * @return Closure[]
277 74
     */
278 74
    private function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null): array
279 74
    {
280 74
        $this->setSchemaThingsFromNode($schema, $node, $parent);
281 74
        $functions = [];
282 74
283 74
        self::againstDOMNodeList(
284 74
            $node,
285 74
            function (
286 74
                DOMElement $node,
287 74
                DOMElement $childNode
288 74
            ) use (
289 74
                $schema,
290 74
                &$functions
291 74
            ): void {
292 74
                $callback = null;
293 74
294 74
                switch ($childNode->localName) {
295
                    case 'attributeGroup':
296
                        $callback = $this->loadAttributeGroup($schema, $childNode);
297 74
                        break;
298 74
                    case 'include':
299
                    case 'import':
300 74
                        $callback = $this->loadImport($schema, $childNode);
301
                        break;
302
                    case 'element':
303 74
                        $callback = $this->loadElementDef($schema, $childNode);
304
                        break;
305
                    case 'attribute':
306 74
                        $callback = $this->loadAttributeDef($schema, $childNode);
307
                        break;
308 74
                    case 'group':
309 74
                        $callback = $this->loadGroup($schema, $childNode);
310
                        break;
311 74
                    case 'complexType':
312 74
                        $callback = $this->loadComplexType($schema, $childNode);
313
                        break;
314 74
                    case 'simpleType':
315
                        $callback = $this->loadSimpleType($schema, $childNode);
316
                        break;
317 74
                }
318
319 74
                if ($callback instanceof Closure) {
320 74
                    $functions[] = $callback;
321
                }
322 74
            }
323
        );
324 74
325
        return $functions;
326 74
    }
327 74
328 74
    private function loadGroupRef(Group $referenced, DOMElement $node): GroupRef
329 7
    {
330
        $ref = new GroupRef($referenced);
331
        $ref->setDoc($this->getDocumentation($node));
332 74
333
        self::maybeSetMax($ref, $node);
334 74
        self::maybeSetMin($ref, $node);
335
336 74
        return $ref;
337 1
    }
338
339 74
    private static function maybeSetMax(InterfaceSetMinMax $ref, DOMElement $node): void
340
    {
341 74
        if ($node->hasAttribute('maxOccurs')) {
342
            $ref->setMax($node->getAttribute('maxOccurs') == 'unbounded' ? -1 : (int) $node->getAttribute('maxOccurs'));
343
        }
344
    }
345 74
346 74
    private static function maybeSetMin(InterfaceSetMinMax $ref, DOMElement $node): void
347 74
    {
348
        if ($node->hasAttribute('minOccurs')) {
349 74
            $ref->setMin((int) $node->getAttribute('minOccurs'));
350 74
            if ($ref->getMin() > $ref->getMax() && $ref->getMax() !== -1) {
351
                $ref->setMax($ref->getMin());
352
            }
353 74
        }
354 74
    }
355
356 74
    private static function maybeSetFixed(InterfaceSetFixed $ref, DOMElement $node): void
357 74
    {
358
        if ($node->hasAttribute('fixed')) {
359 74
            $ref->setFixed($node->getAttribute('fixed'));
360 74
        }
361
    }
362
363
    private static function maybeSetDefault(InterfaceSetDefault $ref, DOMElement $node): void
364
    {
365 74
        if ($node->hasAttribute('default')) {
366 74
            $ref->setDefault($node->getAttribute('default'));
367 74
        }
368
    }
369 74
370 74
    private function loadSequence(ElementContainer $elementContainer, DOMElement $node, int $max = null, int $min = null): void
371 74
    {
372 74
        $max =
373 74
            (
374 74
                (is_int($max) && (bool) $max) ||
375
                $node->getAttribute('maxOccurs') == 'unbounded' ||
376 74
                $node->getAttribute('maxOccurs') > 1
377
            )
378 74
                ? 2
379
                : null;
380 74
        $min =
381
            (
382
                $min === null &&
383
                !$node->hasAttribute('minOccurs')
384
            )
385
                ? null
386
                : (int) max((int) $min, $node->getAttribute('minOccurs'));
387 74
388 74
        self::againstDOMNodeList(
389 74
            $node,
390 74
            function (
391 74
                DOMElement $node,
392 74
                DOMElement $childNode
393 74
            ) use (
394 74
                $elementContainer,
395 74
                $max,
396
                $min
397 74
            ): void {
398 74
                $this->loadSequenceChildNode(
399 74
                    $elementContainer,
400 74
                    $node,
401 74
                    $childNode,
402 74
                    $max,
403 74
                    $min
404 74
                );
405
            }
406 74
        );
407 74
    }
408 74
409 74
    private function loadSequenceChildNode(
410 74
        ElementContainer $elementContainer,
411 74
        DOMElement $node,
412 74
        DOMElement $childNode,
413
        ?int $max,
414 74
        ?int $min = null
415
    ): void {
416 74
        switch ($childNode->localName) {
417
            case 'sequence':
418 74
            case 'choice':
419
            case 'all':
420
                $this->loadSequence(
421
                    $elementContainer,
422
                    $childNode,
423
                    $max,
424
                    $min
425 74
                );
426 74
                break;
427 74
            case 'element':
428 74
                $this->loadSequenceChildNodeLoadElement(
429
                    $elementContainer,
430 74
                    $node,
431 74
                    $childNode,
432
                    $max,
433 74
                    $min
434 74
                );
435
                break;
436 74
            case 'group':
437 74
                $this->addGroupAsElement(
438
                    $elementContainer->getSchema(),
439
                    $node,
440 74
                    $childNode,
441
                    $elementContainer
442
                );
443 74
                break;
444 74
        }
445
    }
446
447 74
    private function loadSequenceChildNodeLoadElement(
448 74
        ElementContainer $elementContainer,
449 74
        DOMElement $node,
450
        DOMElement $childNode,
451
        ?int $max,
452
        ?int $min
453 74
    ): void {
454 74
        if ($childNode->hasAttribute('ref')) {
455
            $elementDef = $this->findElement($elementContainer->getSchema(), $node, $childNode->getAttribute('ref'));
456
            $element = new ElementRef($elementDef);
457 74
            $element->setDoc($this->getDocumentation($childNode));
458
            $this->fillElement($element, $childNode);
459
        } else {
460
            $element = $this->loadElement($elementContainer->getSchema(), $childNode);
461
        }
462
463 74
        if ($min !== null) {
464
            $element->setMin($min);
465 74
        }
466 74
467
        if ($max > 1) {
468 74
            /*
469
            * although one might think the typecast is not needed with $max being `? int $max` after passing > 1,
470
            * phpstan@a4f89fa still thinks it's possibly null.
471
            * see https://github.com/phpstan/phpstan/issues/577 for related issue
472
            */
473
            $element->setMax((int) $max);
474 74
        }
475 74
        $elementContainer->addElement($element);
476 74
    }
477 74
478
    private function addGroupAsElement(
479
        Schema $schema,
480 74
        DOMElement $node,
481 74
        DOMElement $childNode,
482 74
        ElementContainer $elementContainer
483
    ): void {
484 74
        $referencedGroup = $this->findGroup(
485
            $schema,
486 74
            $node,
487 74
            $childNode->getAttribute('ref')
488 74
        );
489
490 74
        $group = $this->loadGroupRef($referencedGroup, $childNode);
491 1
        $elementContainer->addElement($group);
492
    }
493 1
494 1
    private function loadGroup(Schema $schema, DOMElement $node): Closure
495
    {
496 1
        $group = new Group($schema, $node->getAttribute('name'));
497 1
        $group->setDoc($this->getDocumentation($node));
498
        $groupOriginal = $group;
499
500
        if ($node->hasAttribute('maxOccurs') || $node->hasAttribute('minOccurs')) {
501 74
            $group = $this->loadGroupRef($group, $node);
502
        }
503
504 74
        $schema->addGroup($group);
505 74
506
        return function () use ($groupOriginal, $node): void {
507 74
            static::againstDOMNodeList(
508 74
                $node,
509 74
                function (DOMelement $node, DOMElement $childNode) use ($groupOriginal): void {
510 74
                    switch ($childNode->localName) {
511 74
                        case 'sequence':
512 74
                        case 'choice':
513
                        case 'all':
514 74
                            $this->loadSequence($groupOriginal, $childNode);
515
                            break;
516 74
                    }
517
                }
518
            );
519 74
        };
520
    }
521
522
    private function loadComplexType(Schema $schema, DOMElement $node, Closure $callback = null): Closure
523
    {
524 74
        /**
525
         * @var bool
526 74
         */
527 74
        $isSimple = false;
528
529
        self::againstDOMNodeList(
530
            $node,
531
            function (
532 74
                DOMElement $node,
533
                DOMElement $childNode
534 74
            ) use (
535 1
                &$isSimple
536
            ): void {
537 74
                if ($isSimple) {
538 2
                    return;
539
                }
540 74
                if ($childNode->localName === 'simpleContent') {
541
                    $isSimple = true;
542
                }
543 74
            }
544
        );
545 74
546 74
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute('name')) : new ComplexType($schema, $node->getAttribute('name'));
547 74
548
        $type->setDoc($this->getDocumentation($node));
549
        if ($node->getAttribute('name')) {
550
            $schema->addType($type);
551 74
        }
552
553 74
        return function () use ($type, $node, $schema, $callback): void {
554 74
            $this->fillTypeNode($type, $node, true);
555
556
            self::againstDOMNodeList(
557
                $node,
558
                function (
559 74
                    DOMElement $node,
560 74
                    DOMElement $childNode
561
                ) use (
562 74
                    $schema,
563 74
                    $type
564 74
                ): void {
565 74
                    $this->loadComplexTypeFromChildNode(
566 74
                        $type,
567
                        $node,
568 74
                        $childNode,
569
                        $schema
570
                    );
571 74
                }
572 74
            );
573
574 74
            if ($callback instanceof Closure) {
575
                call_user_func($callback, $type);
576
            }
577 74
        };
578
    }
579
580
    private function loadComplexTypeFromChildNode(
581
        BaseComplexType $type,
582
        DOMElement $node,
583 74
        DOMElement $childNode,
584 74
        Schema $schema
585 74
    ): void {
586 74
        switch ($childNode->localName) {
587 74
            case 'sequence':
588 74
            case 'choice':
589 74
            case 'all':
590 74
                if ($type instanceof ElementContainer) {
591
                    $this->loadSequence(
592
                        $type,
593 74
                        $childNode
594 74
                    );
595 74
                }
596 74
                break;
597 74
            case 'attribute':
598 74
                $this->addAttributeFromAttributeOrRef(
599 74
                    $type,
600
                    $childNode,
601 74
                    $schema,
602 74
                    $node
603 2
                );
604 2
                break;
605 2
            case 'attributeGroup':
606 2
                $this->findSomethingLikeAttributeGroup(
607 2
                    $schema,
608
                    $node,
609 2
                    $childNode,
610 74
                    $type
611
                );
612 1
                break;
613
            case 'group':
614 1
                if (
615 1
                    $type instanceof ComplexType
616 1
                ) {
617 1
                    $this->addGroupAsElement(
618 1
                        $schema,
619
                        $node,
620
                        $childNode,
621 1
                        $type
622
                    );
623 74
                }
624
                break;
625 74
        }
626
    }
627 74
628 74
    private function loadSimpleType(Schema $schema, DOMElement $node, Closure $callback = null): Closure
629 74
    {
630 74
        $type = new SimpleType($schema, $node->getAttribute('name'));
631
        $type->setDoc($this->getDocumentation($node));
632
        if ($node->getAttribute('name')) {
633
            $schema->addType($type);
634 74
        }
635
636 74
        return function () use ($type, $node, $callback): void {
637 74
            $this->fillTypeNode($type, $node, true);
638
639 74
            self::againstDOMNodeList(
640 74
                $node,
641 74
                function (DOMElement $node, DOMElement $childNode) use ($type): void {
642 74
                    switch ($childNode->localName) {
643 74
                        case 'union':
644 74
                            $this->loadUnion($type, $childNode);
645 74
                            break;
646
                        case 'list':
647 74
                            $this->loadList($type, $childNode);
648
                            break;
649
                    }
650 74
                }
651 74
            );
652
653 74
            if ($callback instanceof Closure) {
654
                call_user_func($callback, $type);
655
            }
656 74
        };
657
    }
658 74
659
    private function loadList(SimpleType $type, DOMElement $node): void
660
    {
661
        if ($node->hasAttribute('itemType')) {
662 74
            /**
663 74
             * @var SimpleType
664
             */
665 74
            $listType = $this->findSomeType($type, $node, 'itemType');
666 74
            $type->setList($listType);
667
        } else {
668
            self::againstDOMNodeList(
669
                $node,
670
                function (
671 74
                    DOMElement $node,
672
                    DOMElement $childNode
673 74
                ) use (
674 74
                    $type
675 74
                ): void {
676
                    $this->loadTypeWithCallback(
677 74
                        $type->getSchema(),
678 74
                        $childNode,
679
                        function (SimpleType $list) use ($type): void {
680 74
                            $type->setList($list);
681
                        }
682
                    );
683 74
                }
684
            );
685 74
        }
686
    }
687
688
    private function findSomeType(
689
        SchemaItem $fromThis,
690 74
        DOMElement $node,
691 74
        string $attributeName
692 74
    ): SchemaItem {
693 74
        return $this->findSomeTypeFromAttribute(
694
            $fromThis,
695
            $node,
696
            $node->getAttribute($attributeName)
697 74
        );
698
    }
699
700
    private function findSomeTypeFromAttribute(
701
        SchemaItem $fromThis,
702 74
        DOMElement $node,
703 74
        string $attributeName
704 74
    ): SchemaItem {
705 74
        $out = $this->findType(
706
            $fromThis->getSchema(),
707
            $node,
708 74
            $attributeName
709
        );
710
711 74
        return $out;
712
    }
713 74
714 74
    private function loadUnion(SimpleType $type, DOMElement $node): void
715 74
    {
716
        if ($node->hasAttribute('memberTypes')) {
717
            $types = preg_split('/\s+/', $node->getAttribute('memberTypes'));
718
            foreach ($types as $typeName) {
719 74
                /**
720 74
                 * @var SimpleType
721 74
                 */
722 74
                $unionType = $this->findSomeTypeFromAttribute(
723
                    $type,
724 74
                    $node,
725
                    $typeName
726
                );
727 74
                $type->addUnion($unionType);
728 74
            }
729
        }
730
        self::againstDOMNodeList(
731
            $node,
732
            function (
733 74
                DOMElement $node,
734
                DOMElement $childNode
735 74
            ) use (
736 74
                $type
737 74
            ): void {
738
                $this->loadTypeWithCallback(
739 74
                    $type->getSchema(),
740 74
                    $childNode,
741
                    function (SimpleType $unType) use ($type): void {
742 74
                        $type->addUnion($unType);
743
                    }
744 74
                );
745
            }
746 74
        );
747
    }
748 74
749 74
    private function fillTypeNode(Type $type, DOMElement $node, bool $checkAbstract = false): void
750
    {
751
        if ($checkAbstract) {
752 74
            $type->setAbstract($node->getAttribute('abstract') === 'true' || $node->getAttribute('abstract') === '1');
753 74
        }
754
755 74
        self::againstDOMNodeList(
756 74
            $node,
757 74
            function (DOMElement $node, DOMElement $childNode) use ($type): void {
758 74
                switch ($childNode->localName) {
759 74
                    case 'restriction':
760 74
                        $this->loadRestriction($type, $childNode);
761 74
                        break;
762
                    case 'extension':
763 74
                        if ($type instanceof BaseComplexType) {
764 74
                            $this->loadExtension($type, $childNode);
765 74
                        }
766 74
                        break;
767 74
                    case 'simpleContent':
768
                    case 'complexContent':
769 74
                        $this->fillTypeNode($type, $childNode);
770
                        break;
771 74
                }
772
            }
773 74
        );
774
    }
775 74
776 74
    private function loadExtension(BaseComplexType $type, DOMElement $node): void
777
    {
778 74
        $extension = new Extension();
779 74
        $type->setExtension($extension);
780 74
781 74
        if ($node->hasAttribute('base')) {
782 74
            $this->findAndSetSomeBase($type, $extension, $node);
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($type, $childNode);
817 74
                        }
818 74
                        break;
819 74
                }
820
                $this->loadChildAttributesAndAttributeGroups($type, $node, $childNode);
821
            }
822 74
        );
823 74
    }
824 74
825 74
    private function loadChildAttributesAndAttributeGroups(
826 74
        BaseComplexType $type,
827 74
        DOMElement $node,
828 74
        DOMElement $childNode
829
    ): void {
830 74
        switch ($childNode->localName) {
831 74
            case 'attribute':
832 74
                $this->addAttributeFromAttributeOrRef(
833 74
                    $type,
834 74
                    $childNode,
835 74
                    $type->getSchema(),
836 74
                    $node
837
                );
838 74
                break;
839
            case 'attributeGroup':
840 74
                $this->findSomethingLikeAttributeGroup(
841
                    $type->getSchema(),
842 74
                    $node,
843
                    $childNode,
844 74
                    $type
845
                );
846 74
                break;
847 74
        }
848 74
    }
849 74
850
    private function loadRestriction(Type $type, DOMElement $node): void
851 74
    {
852 74
        $restriction = new Restriction();
853
        $type->setRestriction($restriction);
854
        if ($node->hasAttribute('base')) {
855
            $this->findAndSetSomeBase($type, $restriction, $node);
856
        } else {
857 74
            self::againstDOMNodeList(
858 74
                $node,
859
                function (
860 74
                    DOMElement $node,
861 74
                    DOMElement $childNode
862 74
                ) use (
863
                    $type,
864 74
                    $restriction
865 74
                ): void {
866
                    $this->loadTypeWithCallback(
867 74
                        $type->getSchema(),
868
                        $childNode,
869
                        function (Type $restType) use ($restriction): void {
870 74
                            $restriction->setBase($restType);
871 74
                        }
872
                    );
873
                }
874
            );
875
        }
876 74
        $this->loadRestrictionChildNodes($type, $restriction, $node);
877
    }
878
879 74
    private function loadRestrictionChildNodes(
880 74
        Type $type,
881
        Restriction $restriction,
882 74
        DOMElement $node
883
    ): void {
884
        self::againstDOMNodeList(
885
            $node,
886
            function (
887
                DOMElement $node,
888
                DOMElement $childNode
889
            ) use (
890
                $type,
891
                $restriction
892
            ): void {
893
                if ($type instanceof BaseComplexType) {
894
                    $this->loadChildAttributesAndAttributeGroups($type, $node, $childNode);
895 74
                }
896
                if (
897
                in_array(
898 74
                    $childNode->localName,
899 74
                    [
900
                        'enumeration',
901 74
                        'pattern',
902 74
                        'length',
903
                        'minLength',
904
                        'maxLength',
905
                        'minInclusive',
906 74
                        'maxInclusive',
907
                        'minExclusive',
908 74
                        'maxExclusive',
909
                        'fractionDigits',
910
                        'totalDigits',
911
                        'whiteSpace',
912
                    ],
913 74
                    true
914
                )
915 74
                ) {
916 74
                    $restriction->addCheck(
917 74
                        $childNode->localName,
918 74
                        [
919
                            'value' => $childNode->getAttribute('value'),
920
                            'doc' => $this->getDocumentation($childNode),
921
                        ]
922
                    );
923
                }
924 74
            }
925
        );
926
    }
927 74
928 74
    /**
929 74
     * @return mixed[]
930
     */
931
    private static function splitParts(DOMElement $node, string $typeName): array
932
    {
933 74
        $prefix = null;
934
        $name = $typeName;
935 74
        if (strpos($typeName, ':') !== false) {
936
            [$prefix, $name] = explode(':', $typeName);
937
        }
938
939
        /**
940 74
         * @psalm-suppress PossiblyNullArgument
941
         */
942
        $namespace = $node->lookupNamespaceUri($prefix);
943
944
        return [
945
            $name,
946 74
            $namespace,
947
            $prefix,
948 74
        ];
949
    }
950
951
    private function findAttributeItem(Schema $schema, DOMElement $node, string $typeName): AttributeItem
952
    {
953
        [$name, $namespace] = static::splitParts($node, $typeName);
954 74
955
        /**
956 74
         * @var string|null $namespace
957
         */
958
        $namespace = $namespace ?: $schema->getTargetNamespace();
959
960
        try {
961 74
            /**
962
             * @var AttributeItem $out
963
             */
964
            $out = $schema->findAttribute((string) $name, $namespace);
965
966
            return $out;
967 74
        } catch (TypeNotFoundException $e) {
968
            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);
969 74
        }
970
    }
971
972
    private function findAttributeGroup(Schema $schema, DOMElement $node, string $typeName): AttributeGroup
973
    {
974
        [$name, $namespace] = static::splitParts($node, $typeName);
975 74
976
        /**
977 74
         * @var string|null $namespace
978
         */
979
        $namespace = $namespace ?: $schema->getTargetNamespace();
980
981
        try {
982 74
            /**
983
             * @var AttributeGroup $out
984
             */
985 74
            $out = $schema->findAttributeGroup((string) $name, $namespace);
986
987
            return $out;
988
        } catch (TypeNotFoundException $e) {
989
            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);
990
        }
991 74
    }
992
993 74
    private function findElement(Schema $schema, DOMElement $node, string $typeName): ElementDef
994
    {
995
        [$name, $namespace] = static::splitParts($node, $typeName);
996
997
        /**
998 74
         * @var string|null $namespace
999
         */
1000
        $namespace = $namespace ?: $schema->getTargetNamespace();
1001
1002
        try {
1003
            return $schema->findElement((string) $name, $namespace);
1004 74
        } catch (TypeNotFoundException $e) {
1005
            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);
1006 74
        }
1007
    }
1008
1009
    private function findGroup(Schema $schema, DOMElement $node, string $typeName): Group
1010
    {
1011
        [$name, $namespace] = static::splitParts($node, $typeName);
1012 74
1013
        /**
1014 74
         * @var string|null $namespace
1015
         */
1016
        $namespace = $namespace ?: $schema->getTargetNamespace();
1017
1018
        try {
1019 74
            /**
1020
             * @var Group $out
1021
             */
1022
            $out = $schema->findGroup((string) $name, $namespace);
1023 74
1024 1
            return $out;
1025 1
        } catch (TypeNotFoundException $e) {
1026
            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);
1027 74
        }
1028
    }
1029 74
1030 74
    private function findType(Schema $schema, DOMElement $node, string $typeName): SchemaItem
1031 74
    {
1032 74
        [$name, $namespace] = static::splitParts($node, $typeName);
1033
1034
        /**
1035
         * @var string|null $namespace
1036
         */
1037
        $namespace = $namespace ?: $schema->getTargetNamespace();
1038
1039 74
        $tryFindType = static function (Schema $schema, string $name, ?string $namespace): ?SchemaItem {
1040
            try {
1041 74
                return $schema->findType($name, $namespace);
1042
            } catch (TypeNotFoundException $e) {
1043
                return null;
1044 74
            }
1045
        };
1046
1047
        $interestingSchemas = array_merge([$schema], $this->loadedSchemas[$namespace] ?? []);
1048
        foreach ($interestingSchemas as $interestingSchema) {
1049 74
            if ($result = $tryFindType($interestingSchema, $name, $namespace)) {
1050 74
                return $result;
1051 74
            }
1052
        }
1053
1054
        throw new TypeException(sprintf("Can't find %s named {%s}#%s, at line %d in %s ", 'type', $namespace, $name, $node->getLineNo(), $node->ownerDocument->documentURI));
1055
    }
1056 74
1057 74
    private function fillItem(Item $element, DOMElement $node): void
1058
    {
1059
        /**
1060 74
         * @var bool
1061 74
         */
1062 74
        $skip = false;
1063
        self::againstDOMNodeList(
1064 74
            $node,
1065
            function (
1066
                DOMElement $node,
1067 74
                DOMElement $childNode
1068
            ) use (
1069
                $element,
1070 74
                &$skip
1071 74
            ): void {
1072 74
                if (
1073
                    !$skip &&
1074 74
                    in_array(
1075 74
                        $childNode->localName,
1076
                        [
1077 74
                            'complexType',
1078
                            'simpleType',
1079 74
                        ],
1080
                        true
1081 74
                    )
1082 74
                ) {
1083
                    $this->loadTypeWithCallback(
1084 74
                        $element->getSchema(),
1085 74
                        $childNode,
1086
                        function (Type $type) use ($element): void {
1087 74
                            $element->setType($type);
1088
                        }
1089 74
                    );
1090
                    $skip = true;
1091
                }
1092
            }
1093 74
        );
1094
        if ($skip) {
1095
            return;
1096
        }
1097
        $this->fillItemNonLocalType($element, $node);
1098 74
    }
1099 74
1100 74
    private function fillItemNonLocalType(Item $element, DOMElement $node): void
1101 74
    {
1102
        if ($node->getAttribute('type')) {
1103
            /**
1104
             * @var Type
1105 74
             */
1106 74
            $type = $this->findSomeType($element, $node, 'type');
1107
        } else {
1108 74
            /**
1109
             * @var Type
1110
             */
1111
            $type = $this->findSomeTypeFromAttribute(
1112 74
                $element,
1113 74
                $node,
1114 74
                ($node->lookupPrefix(self::XSD_NS).':anyType')
1115 1
            );
1116
        }
1117
1118
        $element->setType($type);
1119 74
    }
1120
1121 3
    private function loadImport(
1122 3
        Schema $schema,
1123 3
        DOMElement $node
1124
    ): Closure {
1125
        $namespace = $node->getAttribute('namespace');
1126 3
        $schemaLocation = $node->getAttribute('schemaLocation');
1127 74
        if (!$schemaLocation && isset($this->knownNamespaceSchemaLocations[$namespace])) {
1128 1
            $schemaLocation = $this->knownNamespaceSchemaLocations[$namespace];
1129 1
        }
1130
1131
        // postpone schema loading
1132
        if ($namespace && !$schemaLocation && !isset(self::$globalSchemaInfo[$namespace])) {
1133 74
            return function () use ($schema, $namespace): void {
1134 74
                if (!empty($this->loadedSchemas[$namespace])) {
1135
                    foreach ($this->loadedSchemas[$namespace] as $s) {
1136 74
                        $schema->addSchema($s, $namespace);
1137 74
                    }
1138
                }
1139
            };
1140 74
        } elseif ($namespace && !$schemaLocation && !empty($this->loadedSchemas[$namespace])) {
1141
            foreach ($this->loadedSchemas[$namespace] as $s) {
1142
                $schema->addSchema($s, $namespace);
1143 3
            }
1144
        }
1145
1146 3
        $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

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

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