Completed
Push — master ( 86e435...55881e )
by Asmir
02:25
created

SchemaReader::loadComplexTypeFromChildNode()   C

Complexity

Conditions 9
Paths 11

Size

Total Lines 47
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 47
ccs 33
cts 33
cp 1
rs 5.2941
c 0
b 0
f 0
cc 9
eloc 38
nc 11
nop 4
crap 9
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 56
    public function __construct(DocumentationReader $documentationReader = null)
90
    {
91 56
        if (null === $documentationReader) {
92 56
            $documentationReader = new StandardDocumentationReader();
93
        }
94 56
        $this->documentationReader = $documentationReader;
95 56
    }
96
97
    public function addKnownSchemaLocation(string $remote, string $local): void
98
    {
99
        $this->knownLocationSchemas[$remote] = $local;
100
    }
101
102 47
    private function loadAttributeGroup(
103
        Schema $schema,
104
        DOMElement $node
105
    ): Closure {
106 47
        $attGroup = new AttributeGroup($schema, $node->getAttribute('name'));
107 47
        $attGroup->setDoc($this->getDocumentation($node));
108 47
        $schema->addAttributeGroup($attGroup);
109
110 47
        return function () use ($schema, $node, $attGroup) {
111 47
            SchemaReader::againstDOMNodeList(
112 47
                $node,
113 47
                function (
114
                    DOMElement $node,
115
                    DOMElement $childNode
116
                ) use (
117 47
                    $schema,
118 47
                    $attGroup
119
                ) {
120 47
                    switch ($childNode->localName) {
121 47
                        case 'attribute':
122 47
                            $attribute = $this->getAttributeFromAttributeOrRef(
123 47
                                $childNode,
124 47
                                $schema,
125 47
                                $node
126
                            );
127 47
                            $attGroup->addAttribute($attribute);
128 47
                            break;
129 47
                        case 'attributeGroup':
130 1
                            $this->findSomethingLikeAttributeGroup(
131 1
                                $schema,
132 1
                                $node,
133 1
                                $childNode,
134 1
                                $attGroup
135
                            );
136 1
                            break;
137
                    }
138 47
                }
139
            );
140 47
        };
141
    }
142
143 47
    private function getAttributeFromAttributeOrRef(
144
        DOMElement $childNode,
145
        Schema $schema,
146
        DOMElement $node
147
    ): AttributeItem {
148 47
        if ($childNode->hasAttribute('ref')) {
149 47
            $attribute = $this->findAttributeItem($schema, $node, $childNode->getAttribute('ref'));
150
        } else {
151
            /**
152
             * @var Attribute
153
             */
154 47
            $attribute = $this->loadAttribute($schema, $childNode);
155
        }
156
157 47
        return $attribute;
158
    }
159
160 47
    private function loadAttribute(
161
        Schema $schema,
162
        DOMElement $node
163
    ): Attribute {
164 47
        $attribute = new Attribute($schema, $node->getAttribute('name'));
165 47
        $attribute->setDoc($this->getDocumentation($node));
166 47
        $this->fillItem($attribute, $node);
167
168 47
        if ($node->hasAttribute('nillable')) {
169 1
            $attribute->setNil($node->getAttribute('nillable') == 'true');
170
        }
171 47
        if ($node->hasAttribute('form')) {
172 1
            $attribute->setQualified($node->getAttribute('form') == 'qualified');
173
        }
174 47
        if ($node->hasAttribute('use')) {
175 47
            $attribute->setUse($node->getAttribute('use'));
176
        }
177
178 47
        return $attribute;
179
    }
180
181 47
    private function loadAttributeOrElementDef(
182
        Schema $schema,
183
        DOMElement $node,
184
        bool $attributeDef
185
    ): Closure {
186 47
        $name = $node->getAttribute('name');
187 47
        if ($attributeDef) {
188 47
            $attribute = new AttributeDef($schema, $name);
189 47
            $schema->addAttribute($attribute);
190
        } else {
191 47
            $attribute = new ElementDef($schema, $name);
192 47
            $schema->addElement($attribute);
193
        }
194
195 47
        return function () use ($attribute, $node) {
196 47
            $this->fillItem($attribute, $node);
197 47
        };
198
    }
199
200 47
    private function loadAttributeDef(Schema $schema, DOMElement $node): Closure
201
    {
202 47
        return $this->loadAttributeOrElementDef($schema, $node, true);
203
    }
204
205 47
    private function getDocumentation(DOMElement $node): string
206
    {
207 47
        return $this->documentationReader->get($node);
208
    }
209
210
    /**
211
     * @return Closure[]
212
     */
213 47
    private function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null): array
214
    {
215 47
        $this->setSchemaThingsFromNode($schema, $node, $parent);
216 47
        $functions = array();
217
218 47
        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 47
            $node,
220 47
            function (
221
                DOMElement $node,
222
                DOMElement $childNode
223
            ) use (
224 47
                $schema,
225 47
                &$functions
226
            ) {
227 47
                $callback = null;
228
229 47
                switch ($childNode->localName) {
230 47
                    case 'attributeGroup':
231 47
                        $callback = $this->loadAttributeGroup($schema, $childNode);
232 47
                        break;
233 47
                    case 'include':
234 47
                    case 'import':
235 47
                        $callback = $this->loadImport($schema, $childNode);
236 47
                        break;
237 47
                    case 'element':
238 47
                        $callback = $this->loadElementDef($schema, $childNode);
239 47
                        break;
240 47
                    case 'attribute':
241 47
                        $callback = $this->loadAttributeDef($schema, $childNode);
242 47
                        break;
243 47
                    case 'group':
244 47
                        $callback = $this->loadGroup($schema, $childNode);
245 47
                        break;
246 47
                    case 'complexType':
247 47
                        $callback = $this->loadComplexType($schema, $childNode);
248 47
                        break;
249 47
                    case 'simpleType':
250 47
                        $callback = $this->loadSimpleType($schema, $childNode);
251 47
                        break;
252
                }
253
254 47
                if ($callback instanceof Closure) {
255 47
                    $functions[] = $callback;
256
                }
257 47
            }
258
        );
259
260 47
        return $functions;
261
    }
262
263 47
    private function loadGroupRef(Group $referenced, DOMElement $node): GroupRef
264
    {
265 47
        $ref = new GroupRef($referenced);
266 47
        $ref->setDoc($this->getDocumentation($node));
267
268 47
        self::maybeSetMax($ref, $node);
269 47
        self::maybeSetMin($ref, $node);
270
271 47
        return $ref;
272
    }
273
274 47
    private static function maybeSetMax(InterfaceSetMinMax $ref, DOMElement $node): InterfaceSetMinMax
275
    {
276
        if (
277 47
            $node->hasAttribute('maxOccurs')
278
        ) {
279 47
            $ref->setMax($node->getAttribute('maxOccurs') == 'unbounded' ? -1 : (int) $node->getAttribute('maxOccurs'));
280
        }
281
282 47
        return $ref;
283
    }
284
285 47
    private static function maybeSetMin(InterfaceSetMinMax $ref, DOMElement $node): InterfaceSetMinMax
286
    {
287 47
        if ($node->hasAttribute('minOccurs')) {
288 47
            $ref->setMin((int) $node->getAttribute('minOccurs'));
289
        }
290
291 47
        return $ref;
292
    }
293
294 47
    private function loadSequence(ElementContainer $elementContainer, DOMElement $node, int $max = null): void
295
    {
296
        $max =
297
        (
298 47
            (is_int($max) && (bool) $max) ||
299 47
            $node->getAttribute('maxOccurs') == 'unbounded' ||
300 47
            $node->getAttribute('maxOccurs') > 1
301
        )
302 47
            ? 2
303 47
            : null;
304
305 47
        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 47
            $node,
307 47
            function (
308
                DOMElement $node,
309
                DOMElement $childNode
310
            ) use (
311 47
                $elementContainer,
312 47
                $max
313
            ) {
314 47
                $this->loadSequenceChildNode(
315 47
                    $elementContainer,
316 47
                    $node,
317 47
                    $childNode,
318 47
                    $max
319
                );
320 47
            }
321
        );
322 47
    }
323
324
    /**
325
     * @param int|null $max
326
     */
327 47
    private function loadSequenceChildNode(
328
        ElementContainer $elementContainer,
329
        DOMElement $node,
330
        DOMElement $childNode,
331
        $max
332
    ): void {
333 47
        switch ($childNode->localName) {
334 47
            case 'sequence':
335 47
            case 'choice':
336 47
            case 'all':
337 47
                $this->loadSequence(
338 47
                    $elementContainer,
339 47
                    $childNode,
340 47
                    $max
341
                );
342 47
                break;
343 47
            case 'element':
344 47
                $this->loadSequenceChildNodeLoadElement(
345 47
                    $elementContainer,
346 47
                    $node,
347 47
                    $childNode,
348 47
                    $max
349
                );
350 47
                break;
351 47
            case 'group':
352 47
                $this->addGroupAsElement(
353 47
                    $elementContainer->getSchema(),
354 47
                    $node,
355 47
                    $childNode,
356 47
                    $elementContainer
357
                );
358 47
                break;
359
        }
360 47
    }
361
362
    /**
363
     * @param int|null $max
364
     */
365 47
    private function loadSequenceChildNodeLoadElement(
366
        ElementContainer $elementContainer,
367
        DOMElement $node,
368
        DOMElement $childNode,
369
        $max
370
    ): void {
371 47
        if ($childNode->hasAttribute('ref')) {
372 47
            $element = new ElementRef(
373 47
                $this->findElement($elementContainer->getSchema(), $node, $childNode->getAttribute('ref'))
374
            );
375 47
            $element->setDoc($this->getDocumentation($childNode));
376
377 47
            self::maybeSetMax($element, $childNode);
378 47
            self::maybeSetMin($element, $childNode);
379 47
            if ($childNode->hasAttribute('nillable')) {
380
                $element->setNil($childNode->getAttribute('nillable') == 'true');
381
            }
382 47
            if ($childNode->hasAttribute('form')) {
383 47
                $element->setQualified($childNode->getAttribute('form') == 'qualified');
384
            }
385
        } else {
386 47
            $element = $this->loadElement(
387 47
                $elementContainer->getSchema(),
388 47
                $childNode
389
            );
390
        }
391 47
        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 47
            $element->setMax((int) $max);
398
        }
399 47
        $elementContainer->addElement($element);
400 47
    }
401
402 47
    private function addGroupAsElement(
403
        Schema $schema,
404
        DOMElement $node,
405
        DOMElement $childNode,
406
        ElementContainer $elementContainer
407
    ): void {
408 47
        $referencedGroup = $this->findGroup(
409 47
            $schema,
410 47
            $node,
411 47
            $childNode->getAttribute('ref')
412
        );
413
414 47
        $group = $this->loadGroupRef($referencedGroup, $childNode);
415 47
        $elementContainer->addElement($group);
416 47
    }
417
418 47
    private function loadGroup(Schema $schema, DOMElement $node): Closure
419
    {
420 47
        $group = new Group($schema, $node->getAttribute('name'));
421 47
        $group->setDoc($this->getDocumentation($node));
422
423 47
        if ($node->hasAttribute('maxOccurs')) {
424
            /**
425
             * @var GroupRef
426
             */
427
            $group = self::maybeSetMax(new GroupRef($group), $node);
428
        }
429 47
        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 47
        $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 47
        return function () use ($group, $node) {
442 47
            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 47
                $node,
444 47
                function (DOMelement $node, DOMElement $childNode) use ($group) {
445 47
                    switch ($childNode->localName) {
446 47
                        case 'sequence':
447 47
                        case 'choice':
448 47
                        case 'all':
449 47
                            $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 47
                            break;
451
                    }
452 47
                }
453
            );
454 47
        };
455
    }
456
457 47
    private function loadComplexType(Schema $schema, DOMElement $node, Closure $callback = null): Closure
458
    {
459
        /**
460
         * @var bool
461
         */
462 47
        $isSimple = false;
463
464 47
        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 47
            $node,
466 47
            function (
467
                DOMElement $node,
468
                DOMElement $childNode
469
            ) use (
470 47
                &$isSimple
471
            ) {
472 47
                if ($isSimple) {
473 1
                    return;
474
                }
475 47
                if ($childNode->localName === 'simpleContent') {
476 2
                    $isSimple = true;
477
                }
478 47
            }
479
        );
480
481 47
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute('name')) : new ComplexType($schema, $node->getAttribute('name'));
482
483 47
        $type->setDoc($this->getDocumentation($node));
484 47
        if ($node->getAttribute('name')) {
485 47
            $schema->addType($type);
486
        }
487
488 47
        return function () use ($type, $node, $schema, $callback) {
489 47
            $this->fillTypeNode($type, $node, true);
490
491 47
            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 47
                $node,
493 47
                function (
494
                    DOMElement $node,
495
                    DOMElement $childNode
496
                ) use (
497 47
                    $schema,
498 47
                    $type
499
                ) {
500 47
                    $this->loadComplexTypeFromChildNode(
501 47
                        $type,
502 47
                        $node,
503 47
                        $childNode,
504 47
                        $schema
505
                    );
506 47
                }
507
            );
508
509 47
            if ($callback instanceof Closure) {
510 47
                call_user_func($callback, $type);
511
            }
512 47
        };
513
    }
514
515 47
    private function loadComplexTypeFromChildNode(
516
        BaseComplexType $type,
517
        DOMElement $node,
518
        DOMElement $childNode,
519
        Schema $schema
520
    ): void {
521 47
        switch ($childNode->localName) {
522 47
            case 'sequence':
523 47
            case 'choice':
524 47
            case 'all':
525 47
                if ($type instanceof ElementContainer) {
526 47
                    $this->loadSequence(
527 47
                        $type,
528 47
                        $childNode
529
                    );
530
                }
531 47
                break;
532 47
            case 'attribute':
533 47
                $this->addAttributeFromAttributeOrRef(
534 47
                    $type,
535 47
                    $childNode,
536 47
                    $schema,
537 47
                    $node
538
                );
539 47
                break;
540 47
            case 'attributeGroup':
541 2
                $this->findSomethingLikeAttributeGroup(
542 2
                    $schema,
543 2
                    $node,
544 2
                    $childNode,
545 2
                    $type
546
                );
547 2
                break;
548 47
            case 'group':
549
                if (
550 1
                    $type instanceof ComplexType
551
                ) {
552 1
                    $this->addGroupAsElement(
553 1
                        $schema,
554 1
                        $node,
555 1
                        $childNode,
556 1
                        $type
557
                    );
558
                }
559 1
                break;
560
        }
561 47
    }
562
563 47
    private function loadSimpleType(Schema $schema, DOMElement $node, Closure $callback = null): Closure
564
    {
565 47
        $type = new SimpleType($schema, $node->getAttribute('name'));
566 47
        $type->setDoc($this->getDocumentation($node));
567 47
        if ($node->getAttribute('name')) {
568 47
            $schema->addType($type);
569
        }
570
571 47
        return function () use ($type, $node, $callback) {
572 47
            $this->fillTypeNode($type, $node, true);
573
574 47
            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 47
                $node,
576 47
                function (DOMElement $node, DOMElement $childNode) use ($type) {
577 47
                    switch ($childNode->localName) {
578 47
                        case 'union':
579 47
                            $this->loadUnion($type, $childNode);
580 47
                            break;
581 47
                        case 'list':
582 47
                            $this->loadList($type, $childNode);
583 47
                            break;
584
                    }
585 47
                }
586
            );
587
588 47
            if ($callback instanceof Closure) {
589 47
                call_user_func($callback, $type);
590
            }
591 47
        };
592
    }
593
594 47
    private function loadList(SimpleType $type, DOMElement $node): void
595
    {
596 47
        if ($node->hasAttribute('itemType')) {
597
            /**
598
             * @var SimpleType
599
             */
600 47
            $listType = $this->findSomeType($type, $node, 'itemType');
601 47
            $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 47
            self::againstDOMNodeList(
604 47
                $node,
605 47
                function (
606
                    DOMElement $node,
607
                    DOMElement $childNode
608
                ) use (
609 47
                    $type
610
                ) {
611 47
                    $this->loadTypeWithCallback(
612 47
                        $type->getSchema(),
613 47
                        $childNode,
614 47
                        function (SimpleType $list) use ($type) {
615 47
                            $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 47
                        }
617
                    );
618 47
                }
619
            );
620
        }
621 47
    }
622
623 47
    private function findSomeType(
624
        SchemaItem $fromThis,
625
        DOMElement $node,
626
        string $attributeName
627
    ): SchemaItem {
628 47
        return $this->findSomeTypeFromAttribute(
629 47
            $fromThis,
630 47
            $node,
631 47
            $node->getAttribute($attributeName)
632
        );
633
    }
634
635 47
    private function findSomeTypeFromAttribute(
636
        SchemaItem $fromThis,
637
        DOMElement $node,
638
        string $attributeName
639
    ): SchemaItem {
640 47
        $out = $this->findType(
641 47
            $fromThis->getSchema(),
642 47
            $node,
643 47
            $attributeName
644
        );
645
646 47
        return $out;
647
    }
648
649 47
    private function loadUnion(SimpleType $type, DOMElement $node): void
650
    {
651 47
        if ($node->hasAttribute('memberTypes')) {
652 47
            $types = preg_split('/\s+/', $node->getAttribute('memberTypes'));
653 47
            foreach ($types as $typeName) {
654
                /**
655
                 * @var SimpleType
656
                 */
657 47
                $unionType = $this->findSomeTypeFromAttribute(
658 47
                    $type,
659 47
                    $node,
660 47
                    $typeName
661
                );
662 47
                $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 47
        self::againstDOMNodeList(
666 47
            $node,
667 47
            function (
668
                DOMElement $node,
669
                DOMElement $childNode
670
            ) use (
671 47
                $type
672
            ) {
673 47
                $this->loadTypeWithCallback(
674 47
                    $type->getSchema(),
675 47
                    $childNode,
676 47
                    function (SimpleType $unType) use ($type) {
677 47
                        $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 47
                    }
679
                );
680 47
            }
681
        );
682 47
    }
683
684 47
    private function fillTypeNode(Type $type, DOMElement $node, bool $checkAbstract = false): void
685
    {
686 47
        if ($checkAbstract) {
687 47
            $type->setAbstract($node->getAttribute('abstract') === 'true' || $node->getAttribute('abstract') === '1');
688
        }
689
690 47
        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 47
            $node,
692 47
            function (DOMElement $node, DOMElement $childNode) use ($type) {
693 47
                switch ($childNode->localName) {
694 47
                    case 'restriction':
695 47
                        $this->loadRestriction($type, $childNode);
696 47
                        break;
697 47
                    case 'extension':
698 47
                        if ($type instanceof BaseComplexType) {
699 47
                            $this->loadExtension($type, $childNode);
700
                        }
701 47
                        break;
702 47
                    case 'simpleContent':
703 47
                    case 'complexContent':
704 47
                        $this->fillTypeNode($type, $childNode);
705 47
                        break;
706
                }
707 47
            }
708
        );
709 47
    }
710
711 47
    private function loadExtension(BaseComplexType $type, DOMElement $node): void
712
    {
713 47
        $extension = new Extension();
714 47
        $type->setExtension($extension);
715
716 47
        if ($node->hasAttribute('base')) {
717 47
            $this->findAndSetSomeBase(
718 47
                $type,
719 47
                $extension,
720 47
                $node
721
            );
722
        }
723 47
        $this->loadExtensionChildNodes($type, $node);
724 47
    }
725
726 47
    private function findAndSetSomeBase(
727
        Type $type,
728
        Base $setBaseOnThis,
729
        DOMElement $node
730
    ): void {
731
        /**
732
         * @var Type
733
         */
734 47
        $parent = $this->findSomeType($type, $node, 'base');
735 47
        $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 47
    }
737
738 47
    private function loadExtensionChildNodes(
739
        BaseComplexType $type,
740
        DOMElement $node
741
    ) {
742 47
        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 47
            $node,
744 47
            function (
745
                DOMElement $node,
746
                DOMElement $childNode
747
            ) use (
748 47
                $type
749
            ) {
750 47
                switch ($childNode->localName) {
751 47
                    case 'sequence':
752 47
                    case 'choice':
753 47
                    case 'all':
754 47
                        if ($type instanceof ElementContainer) {
755 47
                            $this->loadSequence(
756 47
                                $type,
757 47
                                $childNode
758
                            );
759
                        }
760 47
                        break;
761 47
                    case 'attribute':
762 47
                        $this->addAttributeFromAttributeOrRef(
763 47
                            $type,
764 47
                            $childNode,
765 47
                            $type->getSchema(),
766 47
                            $node
767
                        );
768 47
                        break;
769 47
                    case 'attributeGroup':
770 47
                        $this->findSomethingLikeAttributeGroup(
771 47
                            $type->getSchema(),
772 47
                            $node,
773 47
                            $childNode,
774 47
                            $type
775
                        );
776 47
                        break;
777
                }
778 47
            }
779
        );
780 47
    }
781
782 47
    private function loadRestriction(Type $type, DOMElement $node): void
783
    {
784 47
        $restriction = new Restriction();
785 47
        $type->setRestriction($restriction);
786 47
        if ($node->hasAttribute('base')) {
787 47
            $this->findAndSetSomeBase($type, $restriction, $node);
788
        } else {
789 47
            self::againstDOMNodeList(
790 47
                $node,
791 47
                function (
792
                    DOMElement $node,
793
                    DOMElement $childNode
794
                ) use (
795 47
                    $type,
796 47
                    $restriction
797
                ) {
798 47
                    $this->loadTypeWithCallback(
799 47
                        $type->getSchema(),
800 47
                        $childNode,
801 47
                        function (Type $restType) use ($restriction) {
802 47
                            $restriction->setBase($restType);
803 47
                        }
804
                    );
805 47
                }
806
            );
807
        }
808 47
        self::againstDOMNodeList(
809 47
            $node,
810 47
            function (
811
                DOMElement $node,
812
                DOMElement $childNode
813
            ) use (
814 47
                $restriction
815
            ) {
816
                if (
817 47
                    in_array(
818 47
                        $childNode->localName,
819
                        [
820 47
                            '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 47
                        true
834
                    )
835
                ) {
836 47
                    $restriction->addCheck(
837 47
                        $childNode->localName,
838
                        [
839 47
                            'value' => $childNode->getAttribute('value'),
840 47
                            'doc' => $this->getDocumentation($childNode),
841
                        ]
842
                    );
843
                }
844 47
            }
845
        );
846 47
    }
847
848
    /**
849
     * @return mixed[]
850
     */
851 47
    private static function splitParts(DOMElement $node, string $typeName): array
852
    {
853 47
        $prefix = null;
854 47
        $name = $typeName;
855 47
        if (strpos($typeName, ':') !== false) {
856 47
            list($prefix, $name) = explode(':', $typeName);
857
        }
858
859 47
        $namespace = $node->lookupNamespaceUri($prefix ?: '');
860
861
        return array(
862 47
            $name,
863 47
            $namespace,
864 47
            $prefix,
865
        );
866
    }
867
868 47
    private function findAttributeItem(Schema $schema, DOMElement $node, string $typeName): AttributeItem
869
    {
870 47
        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 47
        $namespace = $namespace ?: $schema->getTargetNamespace();
873
874
        try {
875 47
            return $schema->findAttribute($name, $namespace);
876
        } catch (TypeNotFoundException $e) {
877
            throw new TypeException(
878
                sprintf(
879
                    "Can't find %s named {%s}#%s, at line %d in %s ",
880
                    'attribute',
881
                    $namespace,
882
                    $name,
883
                    $node->getLineNo(),
884
                    $node->ownerDocument->documentURI
885
                ),
886
                0,
887
                $e
888
            );
889
        }
890
    }
891
892 47
    private function findAttributeGroup(Schema $schema, DOMElement $node, string $typeName): AttributeGroup
893
    {
894 47
        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...
895
896 47
        $namespace = $namespace ?: $schema->getTargetNamespace();
897
898
        try {
899 47
            return $schema->findAttributeGroup($name, $namespace);
900
        } catch (TypeNotFoundException $e) {
901
            throw new TypeException(
902
                sprintf(
903
                    "Can't find %s named {%s}#%s, at line %d in %s ",
904
                    'attributegroup',
905
                    $namespace,
906
                    $name,
907
                    $node->getLineNo(),
908
                    $node->ownerDocument->documentURI
909
                ),
910
                0,
911
                $e
912
            );
913
        }
914
    }
915
916 47
    private function findElement(Schema $schema, DOMElement $node, string $typeName): ElementDef
917
    {
918 47
        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...
919
920 47
        $namespace = $namespace ?: $schema->getTargetNamespace();
921
922
        try {
923 47
            return $schema->findElement($name, $namespace);
924
        } catch (TypeNotFoundException $e) {
925
            throw new TypeException(
926
                sprintf(
927
                    "Can't find %s named {%s}#%s, at line %d in %s ",
928
                    'element',
929
                    $namespace,
930
                    $name,
931
                    $node->getLineNo(),
932
                    $node->ownerDocument->documentURI
933
                ),
934
                0,
935
                $e
936
            );
937
        }
938
    }
939
940 47
    private function findGroup(Schema $schema, DOMElement $node, string $typeName): Group
941
    {
942 47
        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...
943
944 47
        $namespace = $namespace ?: $schema->getTargetNamespace();
945
946
        try {
947 47
            return $schema->findGroup($name, $namespace);
948
        } catch (TypeNotFoundException $e) {
949
            throw new TypeException(
950
                sprintf(
951
                    "Can't find %s named {%s}#%s, at line %d in %s ",
952
                    'group',
953
                    $namespace,
954
                    $name,
955
                    $node->getLineNo(),
956
                    $node->ownerDocument->documentURI
957
                ),
958
                0,
959
                $e
960
            );
961
        }
962
    }
963
964 47
    private function findType(Schema $schema, DOMElement $node, string $typeName): SchemaItem
965
    {
966 47
        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...
967
968 47
        $namespace = $namespace ?: $schema->getTargetNamespace();
969
970
        try {
971 47
            return $schema->findType($name, $namespace);
972
        } catch (TypeNotFoundException $e) {
973
            throw new TypeException(
974
                sprintf(
975
                    "Can't find %s named {%s}#%s, at line %d in %s ",
976
                    'type',
977
                    $namespace,
978
                    $name,
979
                    $node->getLineNo(),
980
                    $node->ownerDocument->documentURI
981
                ),
982
                0,
983
                $e
984
            );
985
        }
986
    }
987
988
    /**
989
     * @return Closure
990
     */
991 47
    private function loadElementDef(Schema $schema, DOMElement $node): Closure
992
    {
993 47
        return $this->loadAttributeOrElementDef($schema, $node, false);
994
    }
995
996 47
    private function fillItem(Item $element, DOMElement $node)
997
    {
998
        /**
999
         * @var bool
1000
         */
1001 47
        $skip = false;
1002 47
        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...
1003 47
            $node,
1004 47
            function (
1005
                DOMElement $node,
1006
                DOMElement $childNode
1007
            ) use (
1008 47
                $element,
1009 47
                &$skip
1010
            ) {
1011
                if (
1012 47
                    !$skip &&
1013 47
                    in_array(
1014 47
                        $childNode->localName,
1015
                        [
1016 47
                            'complexType',
1017
                            'simpleType',
1018
                        ],
1019 47
                        true
1020
                    )
1021
                ) {
1022 47
                    $this->loadTypeWithCallback(
1023 47
                        $element->getSchema(),
1024 47
                        $childNode,
1025 47
                        function (Type $type) use ($element) {
1026 47
                            $element->setType($type);
1027 47
                        }
1028
                    );
1029 47
                    $skip = true;
1030
                }
1031 47
            }
1032
        );
1033 47
        if ($skip) {
1034 47
            return;
1035
        }
1036 47
        $this->fillItemNonLocalType($element, $node);
1037 47
    }
1038
1039 47
    private function fillItemNonLocalType(Item $element, DOMElement $node): void
1040
    {
1041 47
        if ($node->getAttribute('type')) {
1042
            /**
1043
             * @var Type
1044
             */
1045 47
            $type = $this->findSomeType($element, $node, 'type');
1046
        } else {
1047
            /**
1048
             * @var Type
1049
             */
1050 47
            $type = $this->findSomeTypeFromAttribute(
1051 47
                $element,
1052 47
                $node,
1053 47
                ($node->lookupPrefix(self::XSD_NS).':anyType')
1054
            );
1055
        }
1056
1057 47
        $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...
1058 47
    }
1059
1060 47
    private function loadImport(
1061
        Schema $schema,
1062
        DOMElement $node
1063
    ): Closure {
1064 47
        $base = urldecode($node->ownerDocument->documentURI);
1065 47
        $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute('schemaLocation'));
1066
1067 47
        $namespace = $node->getAttribute('namespace');
1068
1069 47
        $keys = $this->loadImportFreshKeys($namespace, $file);
1070
1071 47
        foreach ($keys as $key) {
1072 47
            if (isset($this->loadedFiles[$key])) {
1073 47
                $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...
1074
1075 47
                return function () {
1076 47
                };
1077
            }
1078
        }
1079
1080 1
        return $this->loadImportFresh($namespace, $schema, $file);
1081
    }
1082
1083
    /**
1084
     * @return string[]
1085
     */
1086 47
    private function loadImportFreshKeys(
1087
        string $namespace,
1088
        string $file
1089
    ): array {
1090 47
        $globalSchemaInfo = $this->getGlobalSchemaInfo();
1091
1092 47
        $keys = [];
1093
1094 47
        if (isset($globalSchemaInfo[$namespace])) {
1095 47
            $keys[] = $globalSchemaInfo[$namespace];
1096
        }
1097
1098 47
        $keys[] = $this->getNamespaceSpecificFileIndex(
1099 47
            $file,
1100 47
            $namespace
1101
        );
1102
1103 47
        $keys[] = $file;
1104
1105 47
        return $keys;
1106
    }
1107
1108 1
    private function loadImportFreshCallbacksNewSchema(
1109
        string $namespace,
1110
        Schema $schema,
1111
        string $file
1112
    ): Schema {
1113
        /**
1114
         * @var Schema $newSchema
1115
         */
1116 1
        $newSchema = $this->setLoadedFile(
1117 1
            $file,
1118 1
            (('' !== trim($namespace)) ? new Schema() : $schema)
1119
        );
1120
1121 1
        if ('' !== trim($namespace)) {
1122
            $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...
1123
            $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...
1124
        }
1125
1126 1
        return $newSchema;
1127
    }
1128
1129
    /**
1130
     * @return Closure[]
1131
     */
1132 1
    private function loadImportFreshCallbacks(
1133
        string $namespace,
1134
        Schema $schema,
1135
        string $file
1136
    ): array {
1137
        /**
1138
         * @var string
1139
         */
1140 1
        $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...
1141
1142 1
        return $this->schemaNode(
1143 1
            $this->loadImportFreshCallbacksNewSchema(
1144 1
                $namespace,
1145 1
                $schema,
1146 1
                $file
1147
            ),
1148 1
            $this->getDOM(
1149 1
                isset($this->knownLocationSchemas[$file])
1150
                    ? $this->knownLocationSchemas[$file]
1151 1
                    : $file
1152 1
            )->documentElement,
1153 1
            $schema
1154
        );
1155
    }
1156
1157
    private function loadImportFresh(
1158
        string $namespace,
1159
        Schema $schema,
1160
        string $file
1161
    ): Closure {
1162 1
        return function () use ($namespace, $schema, $file) {
1163
            foreach (
1164 1
                $this->loadImportFreshCallbacks(
1165 1
                    $namespace,
1166 1
                    $schema,
1167 1
                    $file
1168
                ) as $callback
1169
            ) {
1170 1
                $callback();
1171
            }
1172 1
        };
1173
    }
1174
1175
    /**
1176
     * @var Schema|null
1177
     */
1178
    protected $globalSchema;
1179
1180
    /**
1181
     * @return string[]
1182
     */
1183 47
    public function getGlobalSchemaInfo(): array
1184
    {
1185 47
        return self::$globalSchemaInfo;
1186
    }
1187
1188
    /**
1189
     * @return Schema
1190
     */
1191 47
    public function getGlobalSchema(): Schema
1192
    {
1193 47
        if (!($this->globalSchema instanceof Schema)) {
1194 47
            $callbacks = array();
1195 47
            $globalSchemas = array();
1196
            /**
1197
             * @var string $namespace
1198
             */
1199 47
            foreach (self::$globalSchemaInfo as $namespace => $uri) {
1200 47
                $this->setLoadedFile(
1201 47
                    $uri,
1202 47
                    $globalSchemas[$namespace] = $schema = new Schema()
1203
                );
1204 47
                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...
1205 47
                    $this->globalSchema = $schema;
1206
                }
1207 47
                $xml = $this->getDOM($this->knownLocationSchemas[$uri]);
1208 47
                $callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement));
1209
            }
1210
1211 47
            $globalSchemas[(string) static::XSD_NS]->addType(new SimpleType($globalSchemas[(string) static::XSD_NS], 'anySimpleType'));
1212 47
            $globalSchemas[(string) static::XSD_NS]->addType(new SimpleType($globalSchemas[(string) static::XSD_NS], 'anyType'));
1213
1214 47
            $globalSchemas[(string) static::XML_NS]->addSchema(
1215 47
                $globalSchemas[(string) static::XSD_NS],
1216 47
                (string) static::XSD_NS
1217
            );
1218 47
            $globalSchemas[(string) static::XSD_NS]->addSchema(
1219 47
                $globalSchemas[(string) static::XML_NS],
1220 47
                (string) static::XML_NS
1221
            );
1222
1223
            /**
1224
             * @var Closure
1225
             */
1226 47
            foreach ($callbacks as $callback) {
1227 47
                $callback();
1228
            }
1229
        }
1230
1231
        /**
1232
         * @var Schema
1233
         */
1234 47
        $out = $this->globalSchema;
1235
1236 47
        if (!($out instanceof Schema)) {
1237
            throw new TypeException('Globa schema not discoverd');
1238
        }
1239
1240 47
        return $out;
1241
    }
1242
1243 47
    private function readNode(DOMElement $node, string $file = 'schema.xsd'): Schema
1244
    {
1245 47
        $fileKey = $node->hasAttribute('targetNamespace') ? $this->getNamespaceSpecificFileIndex($file, $node->getAttribute('targetNamespace')) : $file;
1246 47
        $this->setLoadedFile($fileKey, $rootSchema = new Schema());
1247
1248 47
        $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...
1249 47
        $callbacks = $this->schemaNode($rootSchema, $node);
1250
1251 47
        foreach ($callbacks as $callback) {
1252 41
            call_user_func($callback);
1253
        }
1254
1255 47
        return $rootSchema;
1256
    }
1257
1258
    /**
1259
     * It is possible that a single file contains multiple <xsd:schema/> nodes, for instance in a WSDL file.
1260
     *
1261
     * Each of these  <xsd:schema/> nodes typically target a specific namespace. Append the target namespace to the
1262
     * file to distinguish between multiple schemas in a single file.
1263
     */
1264 47
    private function getNamespaceSpecificFileIndex(string $file, string $targetNamespace): string
1265
    {
1266 47
        return $file.'#'.$targetNamespace;
1267
    }
1268
1269
    /**
1270
     * @throws IOException
1271
     */
1272 46
    public function readString(string $content, string $file = 'schema.xsd'): Schema
1273
    {
1274 46
        $xml = new DOMDocument('1.0', 'UTF-8');
1275 46
        if (!$xml->loadXML($content)) {
1276
            throw new IOException("Can't load the schema");
1277
        }
1278 46
        $xml->documentURI = $file;
1279
1280 46
        return $this->readNode($xml->documentElement, $file);
1281
    }
1282
1283 1
    public function readFile(string $file): Schema
1284
    {
1285 1
        $xml = $this->getDOM($file);
1286
1287 1
        return $this->readNode($xml->documentElement, $file);
1288
    }
1289
1290
    /**
1291
     * @throws IOException
1292
     */
1293 47
    private function getDOM(string $file): DOMDocument
1294
    {
1295 47
        $xml = new DOMDocument('1.0', 'UTF-8');
1296 47
        if (!$xml->load($file)) {
1297
            throw new IOException("Can't load the file $file");
1298
        }
1299
1300 47
        return $xml;
1301
    }
1302
1303 47
    private static function againstDOMNodeList(
1304
        DOMElement $node,
1305
        Closure $againstNodeList
1306
    ) {
1307 47
        $limit = $node->childNodes->length;
1308 47
        for ($i = 0; $i < $limit; $i += 1) {
1309
            /**
1310
             * @var DOMNode
1311
             */
1312 47
            $childNode = $node->childNodes->item($i);
1313
1314 47
            if ($childNode instanceof DOMElement) {
1315 47
                $againstNodeList(
1316 47
                    $node,
1317 47
                    $childNode
1318
                );
1319
            }
1320
        }
1321 47
    }
1322
1323 47
    private function loadTypeWithCallback(
1324
        Schema $schema,
1325
        DOMElement $childNode,
1326
        Closure $callback
1327
    ) {
1328
        /**
1329
         * @var Closure|null $func
1330
         */
1331 47
        $func = null;
1332
1333 47
        switch ($childNode->localName) {
1334 47
            case 'complexType':
1335 47
                $func = $this->loadComplexType($schema, $childNode, $callback);
1336 47
                break;
1337 47
            case 'simpleType':
1338 47
                $func = $this->loadSimpleType($schema, $childNode, $callback);
1339 47
                break;
1340
        }
1341
1342 47
        if ($func instanceof Closure) {
1343 47
            call_user_func($func);
1344
        }
1345 47
    }
1346
1347 47
    private function loadElement(
1348
        Schema $schema,
1349
        DOMElement $node
1350
    ): Element {
1351 47
        $element = new Element($schema, $node->getAttribute('name'));
1352 47
        $element->setDoc($this->getDocumentation($node));
1353
1354 47
        $this->fillItem($element, $node);
1355
1356 47
        self::maybeSetMax($element, $node);
1357 47
        self::maybeSetMin($element, $node);
1358
1359 47
        $xp = new \DOMXPath($node->ownerDocument);
1360 47
        $xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema');
1361
1362 47
        if ($xp->query('ancestor::xs:choice', $node)->length) {
1363 47
            $element->setMin(0);
1364
        }
1365
1366 47
        if ($node->hasAttribute('nillable')) {
1367 3
            $element->setNil($node->getAttribute('nillable') == 'true');
1368
        }
1369 47
        if ($node->hasAttribute('form')) {
1370 3
            $element->setQualified($node->getAttribute('form') == 'qualified');
1371
        }
1372
1373 47
        return $element;
1374
    }
1375
1376 47
    private function addAttributeFromAttributeOrRef(
1377
        BaseComplexType $type,
1378
        DOMElement $childNode,
1379
        Schema $schema,
1380
        DOMElement $node
1381
    ): void {
1382 47
        $attribute = $this->getAttributeFromAttributeOrRef(
1383 47
            $childNode,
1384 47
            $schema,
1385 47
            $node
1386
        );
1387
1388 47
        $type->addAttribute($attribute);
1389 47
    }
1390
1391 47
    private function findSomethingLikeAttributeGroup(
1392
        Schema $schema,
1393
        DOMElement $node,
1394
        DOMElement $childNode,
1395
        AttributeContainer $addToThis
1396
    ): void {
1397 47
        $attribute = $this->findAttributeGroup($schema, $node, $childNode->getAttribute('ref'));
1398 47
        $addToThis->addAttribute($attribute);
1399 47
    }
1400
1401 47
    private function setLoadedFile(string $key, Schema $schema): Schema
1402
    {
1403 47
        $this->loadedFiles[$key] = $schema;
1404
1405 47
        return $schema;
1406
    }
1407
1408 47
    private function setSchemaThingsFromNode(
1409
        Schema $schema,
1410
        DOMElement $node,
1411
        Schema $parent = null
1412
    ): void {
1413 47
        $schema->setDoc($this->getDocumentation($node));
1414
1415 47
        if ($node->hasAttribute('targetNamespace')) {
1416 47
            $schema->setTargetNamespace($node->getAttribute('targetNamespace'));
1417
        } elseif ($parent instanceof Schema) {
1418
            $schema->setTargetNamespace($parent->getTargetNamespace());
1419
        }
1420 47
        $schema->setElementsQualification($node->getAttribute('elementFormDefault') == 'qualified');
1421 47
        $schema->setAttributesQualification($node->getAttribute('attributeFormDefault') == 'qualified');
1422 47
        $schema->setDoc($this->getDocumentation($node));
1423 47
    }
1424
}
1425