Completed
Branch psalm (3be4e8)
by SignpostMarv
10:06
created

SchemaReader::findElement()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

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

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
1176
1177
        return $this->schemaNode(
1178
            $this->loadImportFreshCallbacksNewSchema(
1179
                $namespace,
1180
                $schema,
1181
                $file
1182
            ),
1183
            $this->getDOM(
1184
                isset($this->knownLocationSchemas[$file])
1185
                    ? $this->knownLocationSchemas[$file]
1186
                    : $file
1187
            )->documentElement,
1188
            $schema
1189
        );
1190
    }
1191
1192
    private function loadImportFresh(
1193
        string $namespace,
1194
        Schema $schema,
1195
        string $file
1196
    ): Closure {
1197
        return function () use ($namespace, $schema, $file): void {
1198
            foreach (
1199
                $this->loadImportFreshCallbacks(
1200
                    $namespace,
1201
                    $schema,
1202
                    $file
1203
                ) as $callback
1204
            ) {
1205
                $callback();
1206
            }
1207
        };
1208
    }
1209
1210
    /**
1211
     * @var Schema|null
1212
     */
1213
    protected $globalSchema;
1214
1215
    /**
1216
     * @return string[]
1217
     */
1218
    public function getGlobalSchemaInfo(): array
1219
    {
1220
        return self::$globalSchemaInfo;
1221
    }
1222
1223
    /**
1224
     * @return Schema
1225
     */
1226
    public function getGlobalSchema(): Schema
1227
    {
1228
        if (!($this->globalSchema instanceof Schema)) {
1229
            $callbacks = array();
1230
            $globalSchemas = array();
1231
            /**
1232
             * @var string $namespace
1233
             */
1234
            foreach (self::$globalSchemaInfo as $namespace => $uri) {
1235
                $this->setLoadedFile(
1236
                    $uri,
1237
                    $globalSchemas[$namespace] = $schema = new Schema()
1238
                );
1239
                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...
1240
                    $this->globalSchema = $schema;
1241
                }
1242
                $xml = $this->getDOM($this->knownLocationSchemas[$uri]);
1243
                $callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement));
1244
            }
1245
1246
            $globalSchemas[(string) static::XSD_NS]->addType(new SimpleType($globalSchemas[(string) static::XSD_NS], 'anySimpleType'));
1247
            $globalSchemas[(string) static::XSD_NS]->addType(new SimpleType($globalSchemas[(string) static::XSD_NS], 'anyType'));
1248
1249
            $globalSchemas[(string) static::XML_NS]->addSchema(
1250
                $globalSchemas[(string) static::XSD_NS],
1251
                (string) static::XSD_NS
1252
            );
1253
            $globalSchemas[(string) static::XSD_NS]->addSchema(
1254
                $globalSchemas[(string) static::XML_NS],
1255
                (string) static::XML_NS
1256
            );
1257
1258
            /**
1259
             * @var Closure
1260
             */
1261
            foreach ($callbacks as $callback) {
1262
                $callback();
1263
            }
1264
        }
1265
1266
        /**
1267
         * @var Schema
1268
         */
1269
        $out = $this->globalSchema;
1270
1271
        if (!($out instanceof Schema)) {
1272
            throw new TypeException('Globa schema not discoverd');
1273
        }
1274
1275
        return $out;
1276
    }
1277
1278
    private function readNode(DOMElement $node, string $file = 'schema.xsd'): Schema
1279
    {
1280
        $fileKey = $node->hasAttribute('targetNamespace') ? $this->getNamespaceSpecificFileIndex($file, $node->getAttribute('targetNamespace')) : $file;
1281
        $this->setLoadedFile($fileKey, $rootSchema = new Schema());
1282
1283
        $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...
1284
        $callbacks = $this->schemaNode($rootSchema, $node);
1285
1286
        foreach ($callbacks as $callback) {
1287
            call_user_func($callback);
1288
        }
1289
1290
        return $rootSchema;
1291
    }
1292
1293
    /**
1294
     * It is possible that a single file contains multiple <xsd:schema/> nodes, for instance in a WSDL file.
1295
     *
1296
     * Each of these  <xsd:schema/> nodes typically target a specific namespace. Append the target namespace to the
1297
     * file to distinguish between multiple schemas in a single file.
1298
     */
1299
    private function getNamespaceSpecificFileIndex(string $file, string $targetNamespace): string
1300
    {
1301
        return $file.'#'.$targetNamespace;
1302
    }
1303
1304
    /**
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
        if (!$xml->loadXML($content)) {
1311
            throw new IOException("Can't load the schema");
1312
        }
1313
        $xml->documentURI = $file;
1314
1315
        return $this->readNode($xml->documentElement, $file);
1316
    }
1317
1318
    public function readFile(string $file): Schema
1319
    {
1320
        $xml = $this->getDOM($file);
1321
1322
        return $this->readNode($xml->documentElement, $file);
1323
    }
1324
1325
    /**
1326
     * @throws IOException
1327
     */
1328
    private function getDOM(string $file): DOMDocument
1329
    {
1330
        $xml = new DOMDocument('1.0', 'UTF-8');
1331
        if (!$xml->load($file)) {
1332
            throw new IOException("Can't load the file $file");
1333
        }
1334
1335
        return $xml;
1336
    }
1337
1338
    private static function againstDOMNodeList(
1339
        DOMElement $node,
1340
        Closure $againstNodeList
1341
    ) : void {
1342
        $limit = $node->childNodes->length;
1343
        for ($i = 0; $i < $limit; $i += 1) {
1344
            /**
1345
             * @var DOMNode
1346
             */
1347
            $childNode = $node->childNodes->item($i);
1348
1349
            if ($childNode instanceof DOMElement) {
1350
                $againstNodeList(
1351
                    $node,
1352
                    $childNode
1353
                );
1354
            }
1355
        }
1356
    }
1357
1358
    private function loadTypeWithCallback(
1359
        Schema $schema,
1360
        DOMElement $childNode,
1361
        Closure $callback
1362
    ) : void {
1363
        /**
1364
         * @var Closure|null $func
1365
         */
1366
        $func = null;
1367
1368
        switch ($childNode->localName) {
1369
            case 'complexType':
1370
                $func = $this->loadComplexType($schema, $childNode, $callback);
1371
                break;
1372
            case 'simpleType':
1373
                $func = $this->loadSimpleType($schema, $childNode, $callback);
1374
                break;
1375
        }
1376
1377
        if ($func instanceof Closure) {
1378
            call_user_func($func);
1379
        }
1380
    }
1381
1382
    private function loadElement(
1383
        Schema $schema,
1384
        DOMElement $node
1385
    ): Element {
1386
        $element = new Element($schema, $node->getAttribute('name'));
1387
        $element->setDoc($this->getDocumentation($node));
1388
1389
        $this->fillItem($element, $node);
1390
1391
        self::maybeSetMax($element, $node);
1392
        self::maybeSetMin($element, $node);
1393
1394
        $xp = new \DOMXPath($node->ownerDocument);
1395
        $xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema');
1396
1397
        if ($xp->query('ancestor::xs:choice', $node)->length) {
1398
            $element->setMin(0);
1399
        }
1400
1401
        if ($node->hasAttribute('nillable')) {
1402
            $element->setNil($node->getAttribute('nillable') == 'true');
1403
        }
1404
        if ($node->hasAttribute('form')) {
1405
            $element->setQualified($node->getAttribute('form') == 'qualified');
1406
        }
1407
1408
        return $element;
1409
    }
1410
1411
    private function addAttributeFromAttributeOrRef(
1412
        BaseComplexType $type,
1413
        DOMElement $childNode,
1414
        Schema $schema,
1415
        DOMElement $node
1416
    ): void {
1417
        $attribute = $this->getAttributeFromAttributeOrRef(
1418
            $childNode,
1419
            $schema,
1420
            $node
1421
        );
1422
1423
        $type->addAttribute($attribute);
1424
    }
1425
1426
    private function findSomethingLikeAttributeGroup(
1427
        Schema $schema,
1428
        DOMElement $node,
1429
        DOMElement $childNode,
1430
        AttributeContainer $addToThis
1431
    ): void {
1432
        $attribute = $this->findAttributeGroup($schema, $node, $childNode->getAttribute('ref'));
1433
        $addToThis->addAttribute($attribute);
1434
    }
1435
1436
    private function setLoadedFile(string $key, Schema $schema): Schema
1437
    {
1438
        $this->loadedFiles[$key] = $schema;
1439
1440
        return $schema;
1441
    }
1442
1443
    private function setSchemaThingsFromNode(
1444
        Schema $schema,
1445
        DOMElement $node,
1446
        Schema $parent = null
1447
    ): void {
1448
        $schema->setDoc($this->getDocumentation($node));
1449
1450
        if ($node->hasAttribute('targetNamespace')) {
1451
            $schema->setTargetNamespace($node->getAttribute('targetNamespace'));
1452
        } elseif ($parent instanceof Schema) {
1453
            $schema->setTargetNamespace($parent->getTargetNamespace());
1454
        }
1455
        $schema->setElementsQualification($node->getAttribute('elementFormDefault') == 'qualified');
1456
        $schema->setAttributesQualification($node->getAttribute('attributeFormDefault') == 'qualified');
1457
        $schema->setDoc($this->getDocumentation($node));
1458
    }
1459
}
1460