Completed
Push — dependent-import ( 8a1663...921ddb )
by Asmir
15:54 queued 05:53
created

SchemaReader::loadImport()   D

Complexity

Conditions 10
Paths 5

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 19
nc 5
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace GoetasWebservices\XML\XSDReader;
6
7
use Closure;
8
use DOMDocument;
9
use DOMElement;
10
use DOMNode;
11
use GoetasWebservices\XML\XSDReader\Documentation\DocumentationReader;
12
use GoetasWebservices\XML\XSDReader\Documentation\StandardDocumentationReader;
13
use GoetasWebservices\XML\XSDReader\Exception\IOException;
14
use GoetasWebservices\XML\XSDReader\Exception\TypeException;
15
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Attribute;
16
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeContainer;
17
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeDef;
18
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
19
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup;
20
use GoetasWebservices\XML\XSDReader\Schema\Element\Element;
21
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer;
22
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
23
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef;
24
use GoetasWebservices\XML\XSDReader\Schema\Element\Group;
25
use GoetasWebservices\XML\XSDReader\Schema\Element\InterfaceSetMinMax;
26
use GoetasWebservices\XML\XSDReader\Schema\Element\GroupRef;
27
use GoetasWebservices\XML\XSDReader\Schema\Exception\TypeNotFoundException;
28
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Base;
29
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Extension;
30
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Restriction;
31
use GoetasWebservices\XML\XSDReader\Schema\Item;
32
use GoetasWebservices\XML\XSDReader\Schema\Schema;
33
use GoetasWebservices\XML\XSDReader\Schema\SchemaItem;
34
use GoetasWebservices\XML\XSDReader\Schema\Type\BaseComplexType;
35
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
36
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexTypeSimpleContent;
37
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType;
38
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
39
use GoetasWebservices\XML\XSDReader\Utils\UrlUtils;
40
41
class SchemaReader
42
{
43
    public const XSD_NS = 'http://www.w3.org/2001/XMLSchema';
44
45
    public const XML_NS = 'http://www.w3.org/XML/1998/namespace';
46
47
    /**
48
     * @var DocumentationReader
49
     */
50
    private $documentationReader;
51
52
    /**
53
     * @var Schema[]
54
     */
55
    private $loadedFiles = array();
56
57
    /**
58
     * @var Schema[][]
59
     */
60
    private $loadedSchemas = array();
61
62
    /**
63
     * @var string[]
64
     */
65
    protected $knownLocationSchemas = [
66
        'http://www.w3.org/2001/xml.xsd' => (
67
            __DIR__.'/Resources/xml.xsd'
68
        ),
69
        'http://www.w3.org/2001/XMLSchema.xsd' => (
70
            __DIR__.'/Resources/XMLSchema.xsd'
71
        ),
72
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' => (
73
            __DIR__.'/Resources/oasis-200401-wss-wssecurity-secext-1.0.xsd'
74
        ),
75
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' => (
76
            __DIR__.'/Resources/oasis-200401-wss-wssecurity-utility-1.0.xsd'
77
        ),
78
        'https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
79
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
80
        ),
81
        'http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd' => (
82
            __DIR__.'/Resources/xmldsig-core-schema.xsd'
83
        ),
84
    ];
85
86
    /**
87
     * @var string[]
88
     */
89
    protected static $globalSchemaInfo = array(
90
        self::XML_NS => 'http://www.w3.org/2001/xml.xsd',
91
        self::XSD_NS => 'http://www.w3.org/2001/XMLSchema.xsd',
92
    );
93
94
    public function __construct(DocumentationReader $documentationReader = null)
95
    {
96
        if (null === $documentationReader) {
97
            $documentationReader = new StandardDocumentationReader();
98
        }
99
        $this->documentationReader = $documentationReader;
100
    }
101
102
    public function addKnownSchemaLocation(string $remote, string $local): void
103
    {
104
        $this->knownLocationSchemas[$remote] = $local;
105
    }
106
107
    private function loadAttributeGroup(
108
        Schema $schema,
109
        DOMElement $node
110
    ): Closure {
111
        $attGroup = new AttributeGroup($schema, $node->getAttribute('name'));
112
        $attGroup->setDoc($this->getDocumentation($node));
113
        $schema->addAttributeGroup($attGroup);
114
115
        return function () use ($schema, $node, $attGroup) {
116
            SchemaReader::againstDOMNodeList(
117
                $node,
118
                function (
119
                    DOMElement $node,
120
                    DOMElement $childNode
121
                ) use (
122
                    $schema,
123
                    $attGroup
124
                ) {
125
                    switch ($childNode->localName) {
126
                        case 'attribute':
127
                            $attribute = $this->getAttributeFromAttributeOrRef(
128
                                $childNode,
129
                                $schema,
130
                                $node
131
                            );
132
                            $attGroup->addAttribute($attribute);
133
                            break;
134
                        case 'attributeGroup':
135
                            $this->findSomethingLikeAttributeGroup(
136
                                $schema,
137
                                $node,
138
                                $childNode,
139
                                $attGroup
140
                            );
141
                            break;
142
                    }
143
                }
144
            );
145
        };
146
    }
147
148
    private function getAttributeFromAttributeOrRef(
149
        DOMElement $childNode,
150
        Schema $schema,
151
        DOMElement $node
152
    ): AttributeItem {
153
        if ($childNode->hasAttribute('ref')) {
154
            $attribute = $this->findAttributeItem($schema, $node, $childNode->getAttribute('ref'));
155
        } else {
156
            /**
157
             * @var Attribute
158
             */
159
            $attribute = $this->loadAttribute($schema, $childNode);
160
        }
161
162
        return $attribute;
163
    }
164
165
    private function loadAttribute(
166
        Schema $schema,
167
        DOMElement $node
168
    ): Attribute {
169
        $attribute = new Attribute($schema, $node->getAttribute('name'));
170
        $attribute->setDoc($this->getDocumentation($node));
171
        $this->fillItem($attribute, $node);
172
173
        if ($node->hasAttribute('nillable')) {
174
            $attribute->setNil($node->getAttribute('nillable') == 'true');
175
        }
176
        if ($node->hasAttribute('form')) {
177
            $attribute->setQualified($node->getAttribute('form') == 'qualified');
178
        }
179
        if ($node->hasAttribute('use')) {
180
            $attribute->setUse($node->getAttribute('use'));
181
        }
182
183
        return $attribute;
184
    }
185
186
    private function loadAttributeOrElementDef(
187
        Schema $schema,
188
        DOMElement $node,
189
        bool $attributeDef
190
    ): Closure {
191
        $name = $node->getAttribute('name');
192
        if ($attributeDef) {
193
            $attribute = new AttributeDef($schema, $name);
194
            $schema->addAttribute($attribute);
195
        } else {
196
            $attribute = new ElementDef($schema, $name);
197
            $schema->addElement($attribute);
198
        }
199
200
        return function () use ($attribute, $node) {
201
            $this->fillItem($attribute, $node);
202
        };
203
    }
204
205
    private function loadAttributeDef(Schema $schema, DOMElement $node): Closure
206
    {
207
        return $this->loadAttributeOrElementDef($schema, $node, true);
208
    }
209
210
    private function getDocumentation(DOMElement $node): string
211
    {
212
        return $this->documentationReader->get($node);
213
    }
214
215
    /**
216
     * @return Closure[]
217
     */
218
    private function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null): array
219
    {
220
        $this->setSchemaThingsFromNode($schema, $node, $parent);
221
        $functions = array();
222
223
        static::againstDOMNodeList(
0 ignored issues
show
Bug introduced by
Since againstDOMNodeList() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of againstDOMNodeList() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
224
            $node,
225
            function (
226
                DOMElement $node,
227
                DOMElement $childNode
228
            ) use (
229
                $schema,
230
                &$functions
231
            ) {
232
                $callback = null;
233
234
                switch ($childNode->localName) {
235
                    case 'attributeGroup':
236
                        $callback = $this->loadAttributeGroup($schema, $childNode);
237
                        break;
238
                    case 'include':
239
                    case 'import':
240
                        $callback = $this->loadImport($schema, $childNode);
241
                        break;
242
                    case 'element':
243
                        $callback = $this->loadElementDef($schema, $childNode);
244
                        break;
245
                    case 'attribute':
246
                        $callback = $this->loadAttributeDef($schema, $childNode);
247
                        break;
248
                    case 'group':
249
                        $callback = $this->loadGroup($schema, $childNode);
250
                        break;
251
                    case 'complexType':
252
                        $callback = $this->loadComplexType($schema, $childNode);
253
                        break;
254
                    case 'simpleType':
255
                        $callback = $this->loadSimpleType($schema, $childNode);
256
                        break;
257
                }
258
259
                if ($callback instanceof Closure) {
260
                    $functions[] = $callback;
261
                }
262
            }
263
        );
264
265
        return $functions;
266
    }
267
268
    private function loadGroupRef(Group $referenced, DOMElement $node): GroupRef
269
    {
270
        $ref = new GroupRef($referenced);
271
        $ref->setDoc($this->getDocumentation($node));
272
273
        self::maybeSetMax($ref, $node);
274
        self::maybeSetMin($ref, $node);
275
276
        return $ref;
277
    }
278
279
    private static function maybeSetMax(InterfaceSetMinMax $ref, DOMElement $node): InterfaceSetMinMax
280
    {
281
        if (
282
            $node->hasAttribute('maxOccurs')
283
        ) {
284
            $ref->setMax($node->getAttribute('maxOccurs') == 'unbounded' ? -1 : (int) $node->getAttribute('maxOccurs'));
285
        }
286
287
        return $ref;
288
    }
289
290
    private static function maybeSetMin(InterfaceSetMinMax $ref, DOMElement $node): InterfaceSetMinMax
291
    {
292
        if ($node->hasAttribute('minOccurs')) {
293
            $ref->setMin((int) $node->getAttribute('minOccurs'));
294
        }
295
296
        return $ref;
297
    }
298
299
    private function loadSequence(ElementContainer $elementContainer, DOMElement $node, int $max = null): void
300
    {
301
        $max =
302
        (
303
            (is_int($max) && (bool) $max) ||
304
            $node->getAttribute('maxOccurs') == 'unbounded' ||
305
            $node->getAttribute('maxOccurs') > 1
306
        )
307
            ? 2
308
            : null;
309
310
        static::againstDOMNodeList(
0 ignored issues
show
Bug introduced by
Since againstDOMNodeList() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of againstDOMNodeList() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
311
            $node,
312
            function (
313
                DOMElement $node,
314
                DOMElement $childNode
315
            ) use (
316
                $elementContainer,
317
                $max
318
            ) {
319
                $this->loadSequenceChildNode(
320
                    $elementContainer,
321
                    $node,
322
                    $childNode,
323
                    $max
324
                );
325
            }
326
        );
327
    }
328
329
    /**
330
     * @param int|null $max
331
     */
332
    private function loadSequenceChildNode(
333
        ElementContainer $elementContainer,
334
        DOMElement $node,
335
        DOMElement $childNode,
336
        $max
337
    ): void {
338
        switch ($childNode->localName) {
339
            case 'sequence':
340
            case 'choice':
341
            case 'all':
342
                $this->loadSequence(
343
                    $elementContainer,
344
                    $childNode,
345
                    $max
346
                );
347
                break;
348
            case 'element':
349
                $this->loadSequenceChildNodeLoadElement(
350
                    $elementContainer,
351
                    $node,
352
                    $childNode,
353
                    $max
354
                );
355
                break;
356
            case 'group':
357
                $this->addGroupAsElement(
358
                    $elementContainer->getSchema(),
359
                    $node,
360
                    $childNode,
361
                    $elementContainer
362
                );
363
                break;
364
        }
365
    }
366
367
    /**
368
     * @param int|null $max
369
     */
370
    private function loadSequenceChildNodeLoadElement(
371
        ElementContainer $elementContainer,
372
        DOMElement $node,
373
        DOMElement $childNode,
374
        $max
375
    ): void {
376
        if ($childNode->hasAttribute('ref')) {
377
            $element = new ElementRef(
378
                $this->findElement($elementContainer->getSchema(), $node, $childNode->getAttribute('ref'))
379
            );
380
            $element->setDoc($this->getDocumentation($childNode));
381
382
            self::maybeSetMax($element, $childNode);
383
            self::maybeSetMin($element, $childNode);
384
            if ($childNode->hasAttribute('nillable')) {
385
                $element->setNil($childNode->getAttribute('nillable') == 'true');
386
            }
387
            if ($childNode->hasAttribute('form')) {
388
                $element->setQualified($childNode->getAttribute('form') == 'qualified');
389
            }
390
        } else {
391
            $element = $this->loadElement(
392
                $elementContainer->getSchema(),
393
                $childNode
394
            );
395
        }
396
        if ($max > 1) {
397
            /*
398
            * although one might think the typecast is not needed with $max being `? int $max` after passing > 1,
399
            * phpstan@a4f89fa still thinks it's possibly null.
400
            * see https://github.com/phpstan/phpstan/issues/577 for related issue
401
            */
402
            $element->setMax((int) $max);
403
        }
404
        $elementContainer->addElement($element);
405
    }
406
407
    private function addGroupAsElement(
408
        Schema $schema,
409
        DOMElement $node,
410
        DOMElement $childNode,
411
        ElementContainer $elementContainer
412
    ): void {
413
        $referencedGroup = $this->findGroup(
414
            $schema,
415
            $node,
416
            $childNode->getAttribute('ref')
417
        );
418
419
        $group = $this->loadGroupRef($referencedGroup, $childNode);
420
        $elementContainer->addElement($group);
421
    }
422
423
    private function loadGroup(Schema $schema, DOMElement $node): Closure
424
    {
425
        $group = new Group($schema, $node->getAttribute('name'));
426
        $group->setDoc($this->getDocumentation($node));
427
428
        if ($node->hasAttribute('maxOccurs')) {
429
            /**
430
             * @var GroupRef
431
             */
432
            $group = self::maybeSetMax(new GroupRef($group), $node);
433
        }
434
        if ($node->hasAttribute('minOccurs')) {
435
            /**
436
             * @var GroupRef
437
             */
438
            $group = self::maybeSetMin(
439
                $group instanceof GroupRef ? $group : new GroupRef($group),
0 ignored issues
show
Bug introduced by
It seems like $group defined by self::maybeSetMin($group...roupRef($group), $node) on line 438 can also be of type object<GoetasWebservices...ent\InterfaceSetMinMax>; however, GoetasWebservices\XML\XS...GroupRef::__construct() does only seem to accept object<GoetasWebservices...r\Schema\Element\Group>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
440
                $node
441
            );
442
        }
443
444
        $schema->addGroup($group);
0 ignored issues
show
Bug introduced by
It seems like $group can also be of type object<GoetasWebservices...ent\InterfaceSetMinMax>; however, GoetasWebservices\XML\XS...hema\Schema::addGroup() does only seem to accept object<GoetasWebservices...r\Schema\Element\Group>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
445
446
        return function () use ($group, $node) {
447
            static::againstDOMNodeList(
0 ignored issues
show
Bug introduced by
Since againstDOMNodeList() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of againstDOMNodeList() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
448
                $node,
449
                function (DOMelement $node, DOMElement $childNode) use ($group) {
450
                    switch ($childNode->localName) {
451
                        case 'sequence':
452
                        case 'choice':
453
                        case 'all':
454
                            $this->loadSequence($group, $childNode);
0 ignored issues
show
Bug introduced by
It seems like $group can also be of type object<GoetasWebservices...ent\InterfaceSetMinMax>; however, GoetasWebservices\XML\XS...aReader::loadSequence() does only seem to accept object<GoetasWebservices...ement\ElementContainer>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
455
                            break;
456
                    }
457
                }
458
            );
459
        };
460
    }
461
462
    private function loadComplexType(Schema $schema, DOMElement $node, Closure $callback = null): Closure
463
    {
464
        /**
465
         * @var bool
466
         */
467
        $isSimple = false;
468
469
        static::againstDOMNodeList(
0 ignored issues
show
Bug introduced by
Since againstDOMNodeList() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of againstDOMNodeList() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
470
            $node,
471
            function (
472
                DOMElement $node,
473
                DOMElement $childNode
474
            ) use (
475
                &$isSimple
476
            ) {
477
                if ($isSimple) {
478
                    return;
479
                }
480
                if ($childNode->localName === 'simpleContent') {
481
                    $isSimple = true;
482
                }
483
            }
484
        );
485
486
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute('name')) : new ComplexType($schema, $node->getAttribute('name'));
487
488
        $type->setDoc($this->getDocumentation($node));
489
        if ($node->getAttribute('name')) {
490
            $schema->addType($type);
491
        }
492
493
        return function () use ($type, $node, $schema, $callback) {
494
            $this->fillTypeNode($type, $node, true);
495
496
            static::againstDOMNodeList(
0 ignored issues
show
Bug introduced by
Since againstDOMNodeList() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of againstDOMNodeList() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
497
                $node,
498
                function (
499
                    DOMElement $node,
500
                    DOMElement $childNode
501
                ) use (
502
                    $schema,
503
                    $type
504
                ) {
505
                    $this->loadComplexTypeFromChildNode(
506
                        $type,
507
                        $node,
508
                        $childNode,
509
                        $schema
510
                    );
511
                }
512
            );
513
514
            if ($callback instanceof Closure) {
515
                call_user_func($callback, $type);
516
            }
517
        };
518
    }
519
520
    private function loadComplexTypeFromChildNode(
521
        BaseComplexType $type,
522
        DOMElement $node,
523
        DOMElement $childNode,
524
        Schema $schema
525
    ): void {
526
        switch ($childNode->localName) {
527
            case 'sequence':
528
            case 'choice':
529
            case 'all':
530
                if ($type instanceof ElementContainer) {
531
                    $this->loadSequence(
532
                        $type,
533
                        $childNode
534
                    );
535
                }
536
                break;
537
            case 'attribute':
538
                $this->addAttributeFromAttributeOrRef(
539
                    $type,
540
                    $childNode,
541
                    $schema,
542
                    $node
543
                );
544
                break;
545
            case 'attributeGroup':
546
                $this->findSomethingLikeAttributeGroup(
547
                    $schema,
548
                    $node,
549
                    $childNode,
550
                    $type
551
                );
552
                break;
553
            case 'group':
554
                if (
555
                    $type instanceof ComplexType
556
                ) {
557
                    $this->addGroupAsElement(
558
                        $schema,
559
                        $node,
560
                        $childNode,
561
                        $type
562
                    );
563
                }
564
                break;
565
        }
566
    }
567
568
    private function loadSimpleType(Schema $schema, DOMElement $node, Closure $callback = null): Closure
569
    {
570
        $type = new SimpleType($schema, $node->getAttribute('name'));
571
        $type->setDoc($this->getDocumentation($node));
572
        if ($node->getAttribute('name')) {
573
            $schema->addType($type);
574
        }
575
576
        return function () use ($type, $node, $callback) {
577
            $this->fillTypeNode($type, $node, true);
578
579
            static::againstDOMNodeList(
0 ignored issues
show
Bug introduced by
Since againstDOMNodeList() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of againstDOMNodeList() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
580
                $node,
581
                function (DOMElement $node, DOMElement $childNode) use ($type) {
582
                    switch ($childNode->localName) {
583
                        case 'union':
584
                            $this->loadUnion($type, $childNode);
585
                            break;
586
                        case 'list':
587
                            $this->loadList($type, $childNode);
588
                            break;
589
                    }
590
                }
591
            );
592
593
            if ($callback instanceof Closure) {
594
                call_user_func($callback, $type);
595
            }
596
        };
597
    }
598
599
    private function loadList(SimpleType $type, DOMElement $node): void
600
    {
601
        if ($node->hasAttribute('itemType')) {
602
            /**
603
             * @var SimpleType
604
             */
605
            $listType = $this->findSomeType($type, $node, 'itemType');
606
            $type->setList($listType);
0 ignored issues
show
Documentation introduced by
$listType is of type object<GoetasWebservices...ader\Schema\SchemaItem>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
607
        } else {
608
            self::againstDOMNodeList(
609
                $node,
610
                function (
611
                    DOMElement $node,
612
                    DOMElement $childNode
613
                ) use (
614
                    $type
615
                ) {
616
                    $this->loadTypeWithCallback(
617
                        $type->getSchema(),
618
                        $childNode,
619
                        function (SimpleType $list) use ($type) {
620
                            $type->setList($list);
0 ignored issues
show
Documentation introduced by
$list is of type object<GoetasWebservices...Schema\Type\SimpleType>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
621
                        }
622
                    );
623
                }
624
            );
625
        }
626
    }
627
628
    private function findSomeType(
629
        SchemaItem $fromThis,
630
        DOMElement $node,
631
        string $attributeName
632
    ): SchemaItem {
633
        return $this->findSomeTypeFromAttribute(
634
            $fromThis,
635
            $node,
636
            $node->getAttribute($attributeName)
637
        );
638
    }
639
640
    private function findSomeTypeFromAttribute(
641
        SchemaItem $fromThis,
642
        DOMElement $node,
643
        string $attributeName
644
    ): SchemaItem {
645
        $out = $this->findType(
646
            $fromThis->getSchema(),
647
            $node,
648
            $attributeName
649
        );
650
651
        return $out;
652
    }
653
654
    private function loadUnion(SimpleType $type, DOMElement $node): void
655
    {
656
        if ($node->hasAttribute('memberTypes')) {
657
            $types = preg_split('/\s+/', $node->getAttribute('memberTypes'));
658
            foreach ($types as $typeName) {
659
                /**
660
                 * @var SimpleType
661
                 */
662
                $unionType = $this->findSomeTypeFromAttribute(
663
                    $type,
664
                    $node,
665
                    $typeName
666
                );
667
                $type->addUnion($unionType);
0 ignored issues
show
Documentation introduced by
$unionType is of type object<GoetasWebservices...ader\Schema\SchemaItem>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
668
            }
669
        }
670
        self::againstDOMNodeList(
671
            $node,
672
            function (
673
                DOMElement $node,
674
                DOMElement $childNode
675
            ) use (
676
                $type
677
            ) {
678
                $this->loadTypeWithCallback(
679
                    $type->getSchema(),
680
                    $childNode,
681
                    function (SimpleType $unType) use ($type) {
682
                        $type->addUnion($unType);
0 ignored issues
show
Documentation introduced by
$unType is of type object<GoetasWebservices...Schema\Type\SimpleType>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
683
                    }
684
                );
685
            }
686
        );
687
    }
688
689
    private function fillTypeNode(Type $type, DOMElement $node, bool $checkAbstract = false): void
690
    {
691
        if ($checkAbstract) {
692
            $type->setAbstract($node->getAttribute('abstract') === 'true' || $node->getAttribute('abstract') === '1');
693
        }
694
695
        static::againstDOMNodeList(
0 ignored issues
show
Bug introduced by
Since againstDOMNodeList() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of againstDOMNodeList() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
696
            $node,
697
            function (DOMElement $node, DOMElement $childNode) use ($type) {
698
                switch ($childNode->localName) {
699
                    case 'restriction':
700
                        $this->loadRestriction($type, $childNode);
701
                        break;
702
                    case 'extension':
703
                        if ($type instanceof BaseComplexType) {
704
                            $this->loadExtension($type, $childNode);
705
                        }
706
                        break;
707
                    case 'simpleContent':
708
                    case 'complexContent':
709
                        $this->fillTypeNode($type, $childNode);
710
                        break;
711
                }
712
            }
713
        );
714
    }
715
716
    private function loadExtension(BaseComplexType $type, DOMElement $node): void
717
    {
718
        $extension = new Extension();
719
        $type->setExtension($extension);
720
721
        if ($node->hasAttribute('base')) {
722
            $this->findAndSetSomeBase(
723
                $type,
724
                $extension,
725
                $node
726
            );
727
        }
728
        $this->loadExtensionChildNodes($type, $node);
729
    }
730
731
    private function findAndSetSomeBase(
732
        Type $type,
733
        Base $setBaseOnThis,
734
        DOMElement $node
735
    ): void {
736
        /**
737
         * @var Type
738
         */
739
        $parent = $this->findSomeType($type, $node, 'base');
740
        $setBaseOnThis->setBase($parent);
0 ignored issues
show
Compatibility introduced by
$parent of type object<GoetasWebservices...ader\Schema\SchemaItem> is not a sub-type of object<GoetasWebservices...eader\Schema\Type\Type>. It seems like you assume a concrete implementation of the interface GoetasWebservices\XML\XSDReader\Schema\SchemaItem to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
741
    }
742
743
    private function loadExtensionChildNodes(
744
        BaseComplexType $type,
745
        DOMElement $node
746
    ) {
747
        static::againstDOMNodeList(
0 ignored issues
show
Bug introduced by
Since againstDOMNodeList() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of againstDOMNodeList() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
748
            $node,
749
            function (
750
                DOMElement $node,
751
                DOMElement $childNode
752
            ) use (
753
                $type
754
            ) {
755
                switch ($childNode->localName) {
756
                    case 'sequence':
757
                    case 'choice':
758
                    case 'all':
759
                        if ($type instanceof ElementContainer) {
760
                            $this->loadSequence(
761
                                $type,
762
                                $childNode
763
                            );
764
                        }
765
                        break;
766
                    case 'attribute':
767
                        $this->addAttributeFromAttributeOrRef(
768
                            $type,
769
                            $childNode,
770
                            $type->getSchema(),
771
                            $node
772
                        );
773
                        break;
774
                    case 'attributeGroup':
775
                        $this->findSomethingLikeAttributeGroup(
776
                            $type->getSchema(),
777
                            $node,
778
                            $childNode,
779
                            $type
780
                        );
781
                        break;
782
                }
783
            }
784
        );
785
    }
786
787
    private function loadRestriction(Type $type, DOMElement $node): void
788
    {
789
        $restriction = new Restriction();
790
        $type->setRestriction($restriction);
791
        if ($node->hasAttribute('base')) {
792
            $this->findAndSetSomeBase($type, $restriction, $node);
793
        } else {
794
            self::againstDOMNodeList(
795
                $node,
796
                function (
797
                    DOMElement $node,
798
                    DOMElement $childNode
799
                ) use (
800
                    $type,
801
                    $restriction
802
                ) {
803
                    $this->loadTypeWithCallback(
804
                        $type->getSchema(),
805
                        $childNode,
806
                        function (Type $restType) use ($restriction) {
807
                            $restriction->setBase($restType);
808
                        }
809
                    );
810
                }
811
            );
812
        }
813
        self::againstDOMNodeList(
814
            $node,
815
            function (
816
                DOMElement $node,
817
                DOMElement $childNode
818
            ) use (
819
                $restriction
820
            ) {
821
                if (
822
                    in_array(
823
                        $childNode->localName,
824
                        [
825
                            'enumeration',
826
                            'pattern',
827
                            'length',
828
                            'minLength',
829
                            'maxLength',
830
                            'minInclusive',
831
                            'maxInclusive',
832
                            'minExclusive',
833
                            'maxExclusive',
834
                            'fractionDigits',
835
                            'totalDigits',
836
                            'whiteSpace',
837
                        ],
838
                        true
839
                    )
840
                ) {
841
                    $restriction->addCheck(
842
                        $childNode->localName,
843
                        [
844
                            'value' => $childNode->getAttribute('value'),
845
                            'doc' => $this->getDocumentation($childNode),
846
                        ]
847
                    );
848
                }
849
            }
850
        );
851
    }
852
853
    /**
854
     * @return mixed[]
855
     */
856
    private static function splitParts(DOMElement $node, string $typeName): array
857
    {
858
        $prefix = null;
859
        $name = $typeName;
860
        if (strpos($typeName, ':') !== false) {
861
            list($prefix, $name) = explode(':', $typeName);
862
        }
863
864
        $namespace = $node->lookupNamespaceUri($prefix ?: '');
865
866
        return array(
867
            $name,
868
            $namespace,
869
            $prefix,
870
        );
871
    }
872
873
    private function findAttributeItem(Schema $schema, DOMElement $node, string $typeName): AttributeItem
874
    {
875
        list($name, $namespace) = static::splitParts($node, $typeName);
0 ignored issues
show
Bug introduced by
Since splitParts() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of splitParts() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
876
877
        $namespace = $namespace ?: $schema->getTargetNamespace();
878
879
        try {
880
            return $schema->findAttribute($name, $namespace);
881
        } catch (TypeNotFoundException $e) {
882
            throw new TypeException(
883
                sprintf(
884
                    "Can't find %s named {%s}#%s, at line %d in %s ",
885
                    'attribute',
886
                    $namespace,
887
                    $name,
888
                    $node->getLineNo(),
889
                    $node->ownerDocument->documentURI
890
                ),
891
                0,
892
                $e
893
            );
894
        }
895
    }
896
897
    private function findAttributeGroup(Schema $schema, DOMElement $node, string $typeName): AttributeGroup
898
    {
899
        list($name, $namespace) = static::splitParts($node, $typeName);
0 ignored issues
show
Bug introduced by
Since splitParts() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of splitParts() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
900
901
        $namespace = $namespace ?: $schema->getTargetNamespace();
902
903
        try {
904
            return $schema->findAttributeGroup($name, $namespace);
905
        } catch (TypeNotFoundException $e) {
906
            throw new TypeException(
907
                sprintf(
908
                    "Can't find %s named {%s}#%s, at line %d in %s ",
909
                    'attributegroup',
910
                    $namespace,
911
                    $name,
912
                    $node->getLineNo(),
913
                    $node->ownerDocument->documentURI
914
                ),
915
                0,
916
                $e
917
            );
918
        }
919
    }
920
921
    private function findElement(Schema $schema, DOMElement $node, string $typeName): ElementDef
922
    {
923
        list($name, $namespace) = static::splitParts($node, $typeName);
0 ignored issues
show
Bug introduced by
Since splitParts() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of splitParts() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
924
925
        $namespace = $namespace ?: $schema->getTargetNamespace();
926
927
        try {
928
            return $schema->findElement($name, $namespace);
929
        } catch (TypeNotFoundException $e) {
930
            throw new TypeException(
931
                sprintf(
932
                    "Can't find %s named {%s}#%s, at line %d in %s ",
933
                    'element',
934
                    $namespace,
935
                    $name,
936
                    $node->getLineNo(),
937
                    $node->ownerDocument->documentURI
938
                ),
939
                0,
940
                $e
941
            );
942
        }
943
    }
944
945
    private function findGroup(Schema $schema, DOMElement $node, string $typeName): Group
946
    {
947
        list($name, $namespace) = static::splitParts($node, $typeName);
0 ignored issues
show
Bug introduced by
Since splitParts() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of splitParts() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
948
949
        $namespace = $namespace ?: $schema->getTargetNamespace();
950
951
        try {
952
            return $schema->findGroup($name, $namespace);
953
        } catch (TypeNotFoundException $e) {
954
            throw new TypeException(
955
                sprintf(
956
                    "Can't find %s named {%s}#%s, at line %d in %s ",
957
                    'group',
958
                    $namespace,
959
                    $name,
960
                    $node->getLineNo(),
961
                    $node->ownerDocument->documentURI
962
                ),
963
                0,
964
                $e
965
            );
966
        }
967
    }
968
969
    private function findType(Schema $schema, DOMElement $node, string $typeName): SchemaItem
970
    {
971
        list($name, $namespace) = static::splitParts($node, $typeName);
0 ignored issues
show
Bug introduced by
Since splitParts() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of splitParts() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
972
973
        $namespace = $namespace ?: $schema->getTargetNamespace();
974
975
        try {
976
            return $schema->findType($name, $namespace);
977
        } catch (TypeNotFoundException $e) {
978
            throw new TypeException(
979
                sprintf(
980
                    "Can't find %s named {%s}#%s, at line %d in %s ",
981
                    'type',
982
                    $namespace,
983
                    $name,
984
                    $node->getLineNo(),
985
                    $node->ownerDocument->documentURI
986
                ),
987
                0,
988
                $e
989
            );
990
        }
991
    }
992
993
    /**
994
     * @return Closure
995
     */
996
    private function loadElementDef(Schema $schema, DOMElement $node): Closure
997
    {
998
        return $this->loadAttributeOrElementDef($schema, $node, false);
999
    }
1000
1001
    private function fillItem(Item $element, DOMElement $node)
1002
    {
1003
        /**
1004
         * @var bool
1005
         */
1006
        $skip = false;
1007
        static::againstDOMNodeList(
0 ignored issues
show
Bug introduced by
Since againstDOMNodeList() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of againstDOMNodeList() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
1008
            $node,
1009
            function (
1010
                DOMElement $node,
1011
                DOMElement $childNode
1012
            ) use (
1013
                $element,
1014
                &$skip
1015
            ) {
1016
                if (
1017
                    !$skip &&
1018
                    in_array(
1019
                        $childNode->localName,
1020
                        [
1021
                            'complexType',
1022
                            'simpleType',
1023
                        ],
1024
                        true
1025
                    )
1026
                ) {
1027
                    $this->loadTypeWithCallback(
1028
                        $element->getSchema(),
1029
                        $childNode,
1030
                        function (Type $type) use ($element) {
1031
                            $element->setType($type);
1032
                        }
1033
                    );
1034
                    $skip = true;
1035
                }
1036
            }
1037
        );
1038
        if ($skip) {
1039
            return;
1040
        }
1041
        $this->fillItemNonLocalType($element, $node);
1042
    }
1043
1044
    private function fillItemNonLocalType(Item $element, DOMElement $node): void
1045
    {
1046
        if ($node->getAttribute('type')) {
1047
            /**
1048
             * @var Type
1049
             */
1050
            $type = $this->findSomeType($element, $node, 'type');
1051
        } else {
1052
            /**
1053
             * @var Type
1054
             */
1055
            $type = $this->findSomeTypeFromAttribute(
1056
                $element,
1057
                $node,
1058
                ($node->lookupPrefix(self::XSD_NS).':anyType')
1059
            );
1060
        }
1061
1062
        $element->setType($type);
0 ignored issues
show
Compatibility introduced by
$type of type object<GoetasWebservices...ader\Schema\SchemaItem> is not a sub-type of object<GoetasWebservices...eader\Schema\Type\Type>. It seems like you assume a concrete implementation of the interface GoetasWebservices\XML\XSDReader\Schema\SchemaItem to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
1063
    }
1064
1065
    private function loadImport(
1066
        Schema $schema,
1067
        DOMElement $node
1068
    ): Closure {
1069
1070
        $namespace = $node->getAttribute('namespace');
1071
        $schemaLocation = $node->getAttribute('schemaLocation');
1072
1073
        // postpone schema loading
1074
        if ($namespace && !$schemaLocation && !isset(self::$globalSchemaInfo[$namespace])) {
1075
            return function () use ($schema, $namespace) {
1076
                if (!empty($this->loadedSchemas[$namespace])) {
1077
                    foreach ($this->loadedSchemas[$namespace] as $s) {
1078
                        $schema->addSchema($s, $namespace);
0 ignored issues
show
Documentation introduced by
$s is of type object<GoetasWebservices...SDReader\Schema\Schema>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1079
                    }
1080
                }
1081
            };
1082
        } elseif ($namespace && !$schemaLocation && isset($this->globalSchema[$namespace])) {
1083
            $schema->addSchema($this->globalSchema[$namespace]);
1084
        }
1085
1086
        $base = urldecode($node->ownerDocument->documentURI);
1087
        $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute('schemaLocation'));
1088
1089
        if (isset($this->loadedFiles[$file])) {
1090
            $schema->addSchema($this->loadedFiles[$file]);
0 ignored issues
show
Documentation introduced by
$this->loadedFiles[$file] is of type object<GoetasWebservices...SDReader\Schema\Schema>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1091
            return function () {
1092
            };
1093
        }
1094
1095
        return $this->loadImportFresh($namespace, $schema, $file);
1096
    }
1097
1098
    private function createOrUseSchemaForNs(
1099
        Schema $schema,
1100
        string $namespace
1101
    ): Schema {
1102
1103
        if (('' !== trim($namespace))) {
1104
            $newSchema = new Schema();
1105
            $newSchema->addSchema($this->getGlobalSchema());
0 ignored issues
show
Documentation introduced by
$this->getGlobalSchema() is of type object<GoetasWebservices...SDReader\Schema\Schema>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1106
            $schema->addSchema($newSchema);
0 ignored issues
show
Documentation introduced by
$newSchema is of type object<GoetasWebservices...SDReader\Schema\Schema>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1107
        } else {
1108
            $newSchema = $schema;
1109
        }
1110
1111
        return $newSchema;
1112
    }
1113
1114
    private function loadImportFresh(
1115
        string $namespace,
1116
        Schema $schema,
1117
        string $file
1118
    ): Closure {
1119
        return function () use ($namespace, $schema, $file) {
1120
1121
            $dom = $this->getDOM(
1122
            isset($this->knownLocationSchemas[$file])
1123
                ? $this->knownLocationSchemas[$file]
1124
                : $file
1125
            );
1126
1127
            $schemaNew = $this->createOrUseSchemaForNs($schema, $namespace);
1128
1129
            $this->setLoadedFile($file, $schemaNew);
1130
1131
            $callbacks = $this->schemaNode($schemaNew, $dom->documentElement, $schema);
1132
1133
            foreach ($callbacks as $callback) {
1134
                $callback();
1135
            }
1136
        };
1137
    }
1138
1139
    /**
1140
     * @var Schema|null
1141
     */
1142
    protected $globalSchema;
1143
1144
    /**
1145
     * @return Schema
1146
     */
1147
    private function getGlobalSchema(): Schema
1148
    {
1149
        if (!($this->globalSchema instanceof Schema)) {
1150
            $callbacks = array();
1151
            $globalSchemas = array();
1152
            /**
1153
             * @var string $namespace
1154
             */
1155
            foreach (self::$globalSchemaInfo as $namespace => $uri) {
1156
                $this->setLoadedFile(
1157
                    $uri,
1158
                    $globalSchemas[$namespace] = $schema = new Schema()
1159
                );
1160
                if ($namespace === self::XSD_NS) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $namespace (integer) and self::XSD_NS (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
1161
                    $this->globalSchema = $schema;
1162
                }
1163
                $xml = $this->getDOM($this->knownLocationSchemas[$uri]);
1164
                $callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement));
1165
            }
1166
1167
            $globalSchemas[(string) static::XSD_NS]->addType(new SimpleType($globalSchemas[(string) static::XSD_NS], 'anySimpleType'));
1168
            $globalSchemas[(string) static::XSD_NS]->addType(new SimpleType($globalSchemas[(string) static::XSD_NS], 'anyType'));
1169
1170
            $globalSchemas[(string) static::XML_NS]->addSchema(
1171
                $globalSchemas[(string) static::XSD_NS],
1172
                (string) static::XSD_NS
1173
            );
1174
            $globalSchemas[(string) static::XSD_NS]->addSchema(
1175
                $globalSchemas[(string) static::XML_NS],
1176
                (string) static::XML_NS
1177
            );
1178
1179
            /**
1180
             * @var Closure
1181
             */
1182
            foreach ($callbacks as $callback) {
1183
                $callback();
1184
            }
1185
        }
1186
1187
        /**
1188
         * @var Schema
1189
         */
1190
        $out = $this->globalSchema;
1191
1192
        if (!($out instanceof Schema)) {
1193
            throw new TypeException('Globa schema not discoverd');
1194
        }
1195
1196
        return $out;
1197
    }
1198
1199
    public function readNodes(array $nodes, string $file = null)
1200
    {
1201
        $rootSchema = new Schema();
1202
        $rootSchema->addSchema($this->getGlobalSchema());
0 ignored issues
show
Documentation introduced by
$this->getGlobalSchema() is of type object<GoetasWebservices...SDReader\Schema\Schema>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1203
1204
        if ($file !== null) {
1205
            $this->setLoadedFile($file, $rootSchema);
1206
        }
1207
1208
        $all = array();
1209
        foreach ($nodes as $k => $node) {
1210
            if (($node instanceof \DOMElement) && $node->namespaceURI === self::XSD_NS && $node->localName == 'schema') {
1211
1212
                $holderSchema = new Schema();
1213
                $holderSchema->addSchema($this->getGlobalSchema());
0 ignored issues
show
Documentation introduced by
$this->getGlobalSchema() is of type object<GoetasWebservices...SDReader\Schema\Schema>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1214
1215
                $this->setLoadedSchema($node, $holderSchema);
1216
1217
                $rootSchema->addSchema($holderSchema);
0 ignored issues
show
Documentation introduced by
$holderSchema is of type object<GoetasWebservices...SDReader\Schema\Schema>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1218
1219
                $callbacks = $this->schemaNode($holderSchema, $node);
1220
                $all = array_merge($callbacks, $all);
1221
            }
1222
        }
1223
1224
        foreach ($all as $callback) {
1225
            call_user_func($callback);
1226
        }
1227
        return $rootSchema;
1228
    }
1229
1230
    public function readNode(DOMElement $node, string $file = null): Schema
1231
    {
1232
        $rootSchema = new Schema();
1233
        $rootSchema->addSchema($this->getGlobalSchema());
0 ignored issues
show
Documentation introduced by
$this->getGlobalSchema() is of type object<GoetasWebservices...SDReader\Schema\Schema>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1234
1235
        if ($file !== null) {
1236
            $this->setLoadedFile($file, $rootSchema);
1237
        }
1238
1239
        $this->setLoadedSchema($node, $rootSchema);
1240
1241
1242
        $callbacks = $this->schemaNode($rootSchema, $node);
1243
1244
        foreach ($callbacks as $callback) {
1245
            call_user_func($callback);
1246
        }
1247
1248
        return $rootSchema;
1249
    }
1250
1251
    /**
1252
     * @throws IOException
1253
     */
1254
    public function readString(string $content, string $file = 'schema.xsd'): Schema
1255
    {
1256
        $xml = new DOMDocument('1.0', 'UTF-8');
1257
        if (!$xml->loadXML($content)) {
1258
            throw new IOException("Can't load the schema");
1259
        }
1260
        $xml->documentURI = $file;
1261
1262
        return $this->readNode($xml->documentElement, $file);
1263
    }
1264
1265
    public function readFile(string $file): Schema
1266
    {
1267
        $xml = $this->getDOM($file);
1268
1269
        return $this->readNode($xml->documentElement, $file);
1270
    }
1271
1272
    /**
1273
     * @throws IOException
1274
     */
1275
    private function getDOM(string $file): DOMDocument
1276
    {
1277
        $xml = new DOMDocument('1.0', 'UTF-8');
1278
        if (!$xml->load($file)) {
1279
            throw new IOException("Can't load the file $file");
1280
        }
1281
1282
        return $xml;
1283
    }
1284
1285
    private static function againstDOMNodeList(
1286
        DOMElement $node,
1287
        Closure $againstNodeList
1288
    ) {
1289
        $limit = $node->childNodes->length;
1290
        for ($i = 0; $i < $limit; $i += 1) {
1291
            /**
1292
             * @var DOMNode
1293
             */
1294
            $childNode = $node->childNodes->item($i);
1295
1296
            if ($childNode instanceof DOMElement) {
1297
                $againstNodeList(
1298
                    $node,
1299
                    $childNode
1300
                );
1301
            }
1302
        }
1303
    }
1304
1305
    private function loadTypeWithCallback(
1306
        Schema $schema,
1307
        DOMElement $childNode,
1308
        Closure $callback
1309
    ) {
1310
        /**
1311
         * @var Closure|null $func
1312
         */
1313
        $func = null;
1314
1315
        switch ($childNode->localName) {
1316
            case 'complexType':
1317
                $func = $this->loadComplexType($schema, $childNode, $callback);
1318
                break;
1319
            case 'simpleType':
1320
                $func = $this->loadSimpleType($schema, $childNode, $callback);
1321
                break;
1322
        }
1323
1324
        if ($func instanceof Closure) {
1325
            call_user_func($func);
1326
        }
1327
    }
1328
1329
    private function loadElement(
1330
        Schema $schema,
1331
        DOMElement $node
1332
    ): Element {
1333
        $element = new Element($schema, $node->getAttribute('name'));
1334
        $element->setDoc($this->getDocumentation($node));
1335
1336
        $this->fillItem($element, $node);
1337
1338
        self::maybeSetMax($element, $node);
1339
        self::maybeSetMin($element, $node);
1340
1341
        $xp = new \DOMXPath($node->ownerDocument);
1342
        $xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema');
1343
1344
        if ($xp->query('ancestor::xs:choice', $node)->length) {
1345
            $element->setMin(0);
1346
        }
1347
1348
        if ($node->hasAttribute('nillable')) {
1349
            $element->setNil($node->getAttribute('nillable') == 'true');
1350
        }
1351
        if ($node->hasAttribute('form')) {
1352
            $element->setQualified($node->getAttribute('form') == 'qualified');
1353
        }
1354
1355
        return $element;
1356
    }
1357
1358
    private function addAttributeFromAttributeOrRef(
1359
        BaseComplexType $type,
1360
        DOMElement $childNode,
1361
        Schema $schema,
1362
        DOMElement $node
1363
    ): void {
1364
        $attribute = $this->getAttributeFromAttributeOrRef(
1365
            $childNode,
1366
            $schema,
1367
            $node
1368
        );
1369
1370
        $type->addAttribute($attribute);
1371
    }
1372
1373
    private function findSomethingLikeAttributeGroup(
1374
        Schema $schema,
1375
        DOMElement $node,
1376
        DOMElement $childNode,
1377
        AttributeContainer $addToThis
1378
    ): void {
1379
        $attribute = $this->findAttributeGroup($schema, $node, $childNode->getAttribute('ref'));
1380
        $addToThis->addAttribute($attribute);
1381
    }
1382
1383
    private function setLoadedFile(string $key, Schema $schema): void
1384
    {
1385
        $this->loadedFiles[$key] = $schema;
1386
    }
1387
1388
    private function setLoadedSchema(DOMElement $node, Schema $schema): void
1389
    {
1390
        if ($node->hasAttribute('targetNamespace')) {
1391
            $this->loadedSchemas[$node->getAttribute('targetNamespace')][] = $schema;
1392
        }
1393
    }
1394
1395
    private function setSchemaThingsFromNode(
1396
        Schema $schema,
1397
        DOMElement $node,
1398
        Schema $parent = null
1399
    ): void {
1400
        $schema->setDoc($this->getDocumentation($node));
1401
1402
        if ($node->hasAttribute('targetNamespace')) {
1403
            $schema->setTargetNamespace($node->getAttribute('targetNamespace'));
1404
        } elseif ($parent instanceof Schema) {
1405
            $schema->setTargetNamespace($parent->getTargetNamespace());
1406
        }
1407
        $schema->setElementsQualification($node->getAttribute('elementFormDefault') == 'qualified');
1408
        $schema->setAttributesQualification($node->getAttribute('attributeFormDefault') == 'qualified');
1409
        $schema->setDoc($this->getDocumentation($node));
1410
    }
1411
}
1412