Issues (18)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/SchemaReader.php (17 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
namespace Goetas\XML\XSDReader;
3
4
use DOMDocument;
5
use DOMElement;
6
use Goetas\XML\XSDReader\Utils\UrlUtils;
7
use Goetas\XML\XSDReader\Schema\Schema;
8
use Goetas\XML\XSDReader\Schema\Element\Element;
9
use Goetas\XML\XSDReader\Schema\Attribute\Attribute;
10
use Goetas\XML\XSDReader\Schema\Type\ComplexType;
11
use Goetas\XML\XSDReader\Schema\Type\SimpleType;
12
use Goetas\XML\XSDReader\Schema\Type\Type;
13
use Goetas\XML\XSDReader\Schema\Type\BaseComplexType;
14
use Goetas\XML\XSDReader\Schema\Item;
15
use Goetas\XML\XSDReader\Exception\TypeException;
16
use Goetas\XML\XSDReader\Exception\IOException;
17
use Goetas\XML\XSDReader\Schema\Attribute\Group as AttributeGroup;
18
use Goetas\XML\XSDReader\Schema\Element\Group;
19
use Goetas\XML\XSDReader\Schema\Element\ElementContainer;
20
use Goetas\XML\XSDReader\Schema\Type\ComplexTypeSimpleContent;
21
use Goetas\XML\XSDReader\Schema\Element\ElementDef;
22
use Goetas\XML\XSDReader\Schema\Inheritance\Restriction;
23
use Goetas\XML\XSDReader\Schema\Inheritance\Extension;
24
use Goetas\XML\XSDReader\Schema\Exception\TypeNotFoundException;
25
use Goetas\XML\XSDReader\Schema\Element\ElementRef;
26
use Goetas\XML\XSDReader\Schema\Attribute\AttributeRef;
27
use Goetas\XML\XSDReader\Schema\Element\ElementItem;
28
use Goetas\XML\XSDReader\Schema\Attribute\AttributeDef;
29
use Goetas\XML\XSDReader\Schema\Element\GroupRef;
30
31
class SchemaReader
32
{
33
34
    const XSD_NS = "http://www.w3.org/2001/XMLSchema";
35
36
    const XML_NS = "http://www.w3.org/XML/1998/namespace";
37
38
    private $loadedFiles = array();
39
40
    private $knowLocationSchemas = array();
41
42
    private static $globalSchemaInfo = array(
43
        self::XML_NS => 'http://www.w3.org/2001/xml.xsd',
44
        self::XSD_NS => 'http://www.w3.org/2001/XMLSchema.xsd'
45
    );
46
47
    public function __construct()
48
    {
49
        $this->addKnownSchemaLocation('http://www.w3.org/2001/xml.xsd', __DIR__ . '/Resources/xml.xsd');
50
        $this->addKnownSchemaLocation('http://www.w3.org/2001/XMLSchema.xsd', __DIR__ . '/Resources/XMLSchema.xsd');
51
    }
52
53
    public function addKnownSchemaLocation($remote, $local)
54
    {
55
        $this->knowLocationSchemas[$remote] = $local;
56
    }
57
58
    private function loadAttributeGroup(Schema $schema, DOMElement $node)
59
    {
60
        $attGroup = new AttributeGroup($schema, $node->getAttribute("name"));
61
        $attGroup->setDoc($this->getDocumentation($node));
62
        $schema->addAttributeGroup($attGroup);
63
64
        return function () use($schema, $node, $attGroup)
65
        {
66
            foreach ($node->childNodes as $childNode) {
67
                switch ($childNode->localName) {
68
                    case 'attribute':
69
                        if ($childNode->hasAttribute("ref")) {
70
                            $attribute = $this->findSomething('findAttribute', $schema, $node, $childNode->getAttribute("ref"));
71
                        } else {
72
                            $attribute = $this->loadAttribute($schema, $childNode);
73
                        }
74
                        $attGroup->addAttribute($attribute);
0 ignored issues
show
It seems like $attribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 70 can also be of type object<Goetas\XML\XSDRea...ma\Element\ElementItem> or object<Goetas\XML\XSDReader\Schema\Type\Type>; however, Goetas\XML\XSDReader\Sch...e\Group::addAttribute() does only seem to accept object<Goetas\XML\XSDRea...ttribute\AttributeItem>, 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...
75
                        break;
76
                    case 'attributeGroup':
77
78
                        $attribute = $this->findSomething('findAttributeGroup', $schema, $node, $childNode->getAttribute("ref"));
79
                        $attGroup->addAttribute($attribute);
0 ignored issues
show
It seems like $attribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 78 can also be of type object<Goetas\XML\XSDRea...ma\Element\ElementItem> or object<Goetas\XML\XSDReader\Schema\Type\Type>; however, Goetas\XML\XSDReader\Sch...e\Group::addAttribute() does only seem to accept object<Goetas\XML\XSDRea...ttribute\AttributeItem>, 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...
80
                        break;
81
                }
82
            }
83
        };
84
    }
85
86
    private function loadAttribute(Schema $schema, DOMElement $node)
87
    {
88
        $attribute = new Attribute($schema, $node->getAttribute("name"));
89
        $attribute->setDoc($this->getDocumentation($node));
90
        $this->fillItem($attribute, $node);
91
92
        if ($node->hasAttribute("nillable")) {
93
            $attribute->setNil($node->getAttribute("nillable") == "true");
94
        }
95
        if ($node->hasAttribute("form")) {
96
            $attribute->setQualified($node->getAttribute("form") == "qualified");
97
        }
98
        if ($node->hasAttribute("use")) {
99
            $attribute->setUse($node->getAttribute("use"));
100
        }
101
        return $attribute;
102
    }
103
104
105
    private function loadAttributeDef(Schema $schema, DOMElement $node)
106
    {
107
        $attribute = new AttributeDef($schema, $node->getAttribute("name"));
108
109
        $schema->addAttribute($attribute);
110
111
        return function () use($attribute, $schema, $node)
112
        {
113
            $this->fillItem($attribute, $node);
114
        };
115
    }
116
117
    /**
118
     * @param DOMElement $node
119
     * @return string
120
     */
121
    private function getDocumentation(DOMElement $node)
122
    {
123
        $doc = '';
124
        foreach ($node->childNodes as $childNode) {
125
            if ($childNode->localName == "annotation") {
126
                foreach ($childNode->childNodes as $subChildNode) {
127
                    if ($subChildNode->localName == "documentation") {
128
                        $doc .= ($subChildNode->nodeValue);
129
                    }
130
                }
131
            }
132
        }
133
        $doc = preg_replace('/[\t ]+/', ' ', $doc);
134
        return trim($doc);
135
    }
136
137
    /**
138
     *
139
     * @param Schema $schema
140
     * @param DOMElement $node
141
     * @param Schema $parent
142
     * @return array
143
     */
144
    private function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null)
145
    {
146
        $schema->setDoc($this->getDocumentation($node));
147
148
        if ($node->hasAttribute("targetNamespace")) {
149
            $schema->setTargetNamespace($node->getAttribute("targetNamespace"));
150
        } elseif ($parent) {
151
            $schema->setTargetNamespace($parent->getTargetNamespace());
152
        }
153
        $schema->setElementsQualification(! $node->hasAttribute("elementFormDefault") || $node->getAttribute("elementFormDefault") == "qualified");
154
        $schema->setAttributesQualification(! $node->hasAttribute("attributeFormDefault") || $node->getAttribute("attributeFormDefault") == "qualified");
155
        $schema->setDoc($this->getDocumentation($node));
156
        $functions = array();
157
158
        foreach ($node->childNodes as $childNode) {
159
            switch ($childNode->localName) {
160
                case 'include':
161
                case 'import':
162
                    $functions[] = $this->loadImport($schema, $childNode);
163
                    break;
164
                case 'element':
165
                    $functions[] = $this->loadElementDef($schema, $childNode);
166
                    break;
167
                case 'attribute':
168
                    $functions[] = $this->loadAttributeDef($schema, $childNode);
169
                    break;
170
                case 'attributeGroup':
171
                    $functions[] = $this->loadAttributeGroup($schema, $childNode);
172
                    break;
173
                case 'group':
174
                    $functions[] = $this->loadGroup($schema, $childNode);
175
                    break;
176
                case 'complexType':
177
                    $functions[] = $this->loadComplexType($schema, $childNode);
178
                    break;
179
                case 'simpleType':
180
                    $functions[] = $this->loadSimpleType($schema, $childNode);
181
                    break;
182
            }
183
        }
184
185
        return $functions;
186
    }
187
188
    private function loadElement(Schema $schema, DOMElement $node)
189
    {
190
        $element = new Element($schema, $node->getAttribute("name"));
191
        $element->setDoc($this->getDocumentation($node));
192
193
        $this->fillItem($element, $node);
194
195
        if ($node->hasAttribute("maxOccurs")) {
196
            $element->setMax($node->getAttribute("maxOccurs") == "unbounded" ? - 1 : (int)$node->getAttribute("maxOccurs"));
197
        }
198
        if ($node->hasAttribute("minOccurs")) {
199
            $element->setMin((int)$node->getAttribute("minOccurs"));
200
        }
201
        if ($node->hasAttribute("nillable")) {
202
            $element->setNil($node->getAttribute("nillable") == "true");
203
        }
204
        if ($node->hasAttribute("form")) {
205
            $element->setQualified($node->getAttribute("form") == "qualified");
206
        }
207
        return $element;
208
    }
209
210
    private function loadGroupRef(Group $referenced, DOMElement $node)
211
    {
212
        $ref = new GroupRef($referenced);
213
        $ref->setDoc($this->getDocumentation($node));
214
215
        if ($node->hasAttribute("maxOccurs")) {
216
            $ref->setMax($node->getAttribute("maxOccurs") == "unbounded" ? - 1 : (int)$node->getAttribute("maxOccurs"));
217
        }
218
        if ($node->hasAttribute("minOccurs")) {
219
            $ref->setMin((int)$node->getAttribute("minOccurs"));
220
        }
221
222
        return $ref;
223
    }
224
225
    private function loadElementRef(ElementDef $referenced, DOMElement $node)
226
    {
227
        $ref = new ElementRef($referenced);
228
        $ref->setDoc($this->getDocumentation($node));
229
230
        if ($node->hasAttribute("maxOccurs")) {
231
            $ref->setMax($node->getAttribute("maxOccurs") == "unbounded" ? - 1 : (int)$node->getAttribute("maxOccurs"));
232
        }
233
        if ($node->hasAttribute("minOccurs")) {
234
            $ref->setMin((int)$node->getAttribute("minOccurs"));
235
        }
236
        if ($node->hasAttribute("nillable")) {
237
            $ref->setNil($node->getAttribute("nillable") == "true");
238
        }
239
        if ($node->hasAttribute("form")) {
240
            $ref->setQualified($node->getAttribute("form") == "qualified");
241
        }
242
243
        return $ref;
244
    }
245
246
247
    private function loadAttributeRef(AttributeDef $referencedAttribiute, DOMElement $node)
248
    {
249
        $attribute = new AttributeRef($referencedAttribiute);
250
        $attribute->setDoc($this->getDocumentation($node));
251
252
        if ($node->hasAttribute("nillable")) {
253
            $attribute->setNil($node->getAttribute("nillable") == "true");
254
        }
255
        if ($node->hasAttribute("form")) {
256
            $attribute->setQualified($node->getAttribute("form") == "qualified");
257
        }
258
        if ($node->hasAttribute("use")) {
259
            $attribute->setUse($node->getAttribute("use"));
260
        }
261
        return $attribute;
262
    }
263
264
    private function loadSequence(ElementContainer $elementContainer, DOMElement $node, $max = null)
265
    {
266
        $max = $max || $node->getAttribute("maxOccurs")=="unbounded" || $node->getAttribute("maxOccurs")>1 ? 2 :null;
267
268
        foreach ($node->childNodes as $childNode) {
269
270
            switch ($childNode->localName) {
271
                case 'choice':
272
                case 'sequence':
273
                case 'all':
274
                    $this->loadSequence($elementContainer, $childNode, $max);
275
                    break;
276
                case 'element':
277
                    if ($childNode->hasAttribute("ref")) {
278
                        $referencedElement = $this->findSomething('findElement', $elementContainer->getSchema(), $node, $childNode->getAttribute("ref"));
279
                        $element = $this->loadElementRef($referencedElement, $childNode);
0 ignored issues
show
It seems like $referencedElement defined by $this->findSomething('fi...e->getAttribute('ref')) on line 278 can also be of type object<Goetas\XML\XSDRea...ma\Element\ElementItem> or object<Goetas\XML\XSDReader\Schema\Type\Type>; however, Goetas\XML\XSDReader\Sch...eader::loadElementRef() does only seem to accept object<Goetas\XML\XSDRea...ema\Element\ElementDef>, 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...
280
                    } else {
281
                        $element = $this->loadElement($elementContainer->getSchema(), $childNode);
282
                    }
283
                    if ($max) {
284
                        $element->setMax($max);
285
                    }
286
                    $elementContainer->addElement($element);
287
                    break;
288
                case 'group':
289
                    $referencedGroup = $this->findSomething('findGroup', $elementContainer->getSchema(), $node, $childNode->getAttribute("ref"));
290
291
                    $group = $this->loadGroupRef($referencedGroup, $childNode);
0 ignored issues
show
It seems like $referencedGroup defined by $this->findSomething('fi...e->getAttribute('ref')) on line 289 can also be of type object<Goetas\XML\XSDRea...ma\Element\ElementItem> or object<Goetas\XML\XSDReader\Schema\Type\Type>; however, Goetas\XML\XSDReader\SchemaReader::loadGroupRef() does only seem to accept object<Goetas\XML\XSDReader\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...
292
                    $elementContainer->addElement($group);
293
                    break;
294
            }
295
        }
296
    }
297
298
    private function loadGroup(Schema $schema, DOMElement $node)
299
    {
300
        $group = new Group($schema, $node->getAttribute("name"));
301
        $group->setDoc($this->getDocumentation($node));
302
303
        if ($node->hasAttribute("maxOccurs")) {
304
            $group->setMax($node->getAttribute("maxOccurs") == "unbounded" ? - 1 : (int)$node->getAttribute("maxOccurs"));
305
        }
306
        if ($node->hasAttribute("minOccurs")) {
307
            $group->setMin((int)$node->getAttribute("minOccurs"));
308
        }
309
310
        $schema->addGroup($group);
311
312
        return function () use($group, $node)
313
        {
314
            foreach ($node->childNodes as $childNode) {
315
                switch ($childNode->localName) {
316
                    case 'sequence':
317
                    case 'choice':
318
                    case 'all':
319
                        $this->loadSequence($group, $childNode);
320
                        break;
321
                }
322
            }
323
        };
324
    }
325
326
    private function loadComplexType(Schema $schema, DOMElement $node, $callback = null)
327
    {
328
        $isSimple = false;
329
330
        foreach ($node->childNodes as $childNode) {
331
            if ($childNode->localName === "simpleContent") {
332
                $isSimple = true;
333
                break;
334
            }
335
        }
336
337
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute("name")) : new ComplexType($schema, $node->getAttribute("name"));
338
339
        $type->setDoc($this->getDocumentation($node));
340
        if ($node->getAttribute("name")) {
341
            $schema->addType($type);
342
        }
343
344
        return function () use($type, $node, $schema, $callback)
345
        {
346
347
            $this->fillTypeNode($type, $node);
348
349
            foreach ($node->childNodes as $childNode) {
350
                switch ($childNode->localName) {
351
                    case 'sequence':
352
                    case 'choice':
353
                    case 'all':
354
                        $this->loadSequence($type, $childNode);
0 ignored issues
show
It seems like $type defined by $isSimple ? new \Goetas\...->getAttribute('name')) on line 337 can also be of type object<Goetas\XML\XSDRea...mplexTypeSimpleContent>; however, Goetas\XML\XSDReader\SchemaReader::loadSequence() does only seem to accept object<Goetas\XML\XSDRea...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...
355
                        break;
356
                    case 'attribute':
357
                        if ($childNode->hasAttribute("ref")) {
358
                            $referencedAttribute = $this->findSomething('findAttribute', $schema, $node, $childNode->getAttribute("ref"));
359
                            $attribute = $this->loadAttributeRef($referencedAttribute, $childNode);
0 ignored issues
show
It seems like $referencedAttribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 358 can also be of type object<Goetas\XML\XSDRea...ma\Element\ElementItem> or object<Goetas\XML\XSDReader\Schema\Type\Type>; however, Goetas\XML\XSDReader\Sch...der::loadAttributeRef() does only seem to accept object<Goetas\XML\XSDRea...Attribute\AttributeDef>, 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...
360
                        } else {
361
                            $attribute = $this->loadAttribute($schema, $childNode);
362
                        }
363
364
                        $type->addAttribute($attribute);
365
                        break;
366
                    case 'attributeGroup':
367
                        $attribute = $this->findSomething('findAttributeGroup', $schema, $node, $childNode->getAttribute("ref"));
368
                        $type->addAttribute($attribute);
0 ignored issues
show
It seems like $attribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 367 can also be of type object<Goetas\XML\XSDRea...ma\Element\ElementItem> or object<Goetas\XML\XSDReader\Schema\Type\Type>; however, Goetas\XML\XSDReader\Sch...lexType::addAttribute() does only seem to accept object<Goetas\XML\XSDRea...ttribute\AttributeItem>, 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...
369
                        break;
370
                }
371
            }
372
373
            if ($callback) {
374
                call_user_func($callback, $type);
375
            }
376
        };
377
    }
378
379
    private function loadSimpleType(Schema $schema, DOMElement $node, $callback = null)
380
    {
381
        $type = new SimpleType($schema, $node->getAttribute("name"));
382
        $type->setDoc($this->getDocumentation($node));
383
        if ($node->getAttribute("name")) {
384
            $schema->addType($type);
385
        }
386
387
        return function () use($type, $node, $callback)
388
        {
389
            $this->fillTypeNode($type, $node);
390
391
            foreach ($node->childNodes as $childNode) {
392
                switch ($childNode->localName) {
393
                    case 'union':
394
                        $this->loadUnion($type, $childNode);
395
                        break;
396
                    case 'list':
397
                        $this->loadList($type, $childNode);
398
                        break;
399
                }
400
            }
401
402
            if ($callback) {
403
                call_user_func($callback, $type);
404
            }
405
        };
406
    }
407
408
    private function loadList(SimpleType $type, DOMElement $node)
409
    {
410
        if ($node->hasAttribute("itemType")) {
411
            $type->setList($this->findSomething('findType', $type->getSchema(), $node, $node->getAttribute("itemType")));
0 ignored issues
show
It seems like $this->findSomething('fi...tAttribute('itemType')) targeting Goetas\XML\XSDReader\SchemaReader::findSomething() can also be of type object<Goetas\XML\XSDRea...ma\Element\ElementItem> or object<Goetas\XML\XSDReader\Schema\Type\Type>; however, Goetas\XML\XSDReader\Sch...e\SimpleType::setList() does only seem to accept object<Goetas\XML\XSDRea...Schema\Type\SimpleType>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
412
        }else{
413
            $addCallback = function ($list) use($type)
414
            {
415
                $type->setList($list);
416
            };
417
418
            foreach ($node->childNodes as $childNode) {
419
                switch ($childNode->localName) {
420
                    case 'simpleType':
421
                        call_user_func($this->loadSimpleType($type->getSchema(), $childNode, $addCallback));
422
                        break;
423
                }
424
            }
425
        }
426
    }
427
428
    private function loadUnion(SimpleType $type, DOMElement $node)
429
    {
430
        if ($node->hasAttribute("memberTypes")) {
431
            $types = preg_split('/\s+/', $node->getAttribute("memberTypes"));
432
            foreach ($types as $typeName) {
433
                $type->addUnion($this->findSomething('findType', $type->getSchema(), $node, $typeName));
0 ignored issues
show
It seems like $this->findSomething('fi...ma(), $node, $typeName) targeting Goetas\XML\XSDReader\SchemaReader::findSomething() can also be of type object<Goetas\XML\XSDRea...ma\Element\ElementItem> or object<Goetas\XML\XSDReader\Schema\Type\Type>; however, Goetas\XML\XSDReader\Sch...\SimpleType::addUnion() does only seem to accept object<Goetas\XML\XSDRea...Schema\Type\SimpleType>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
434
            }
435
        }
436
        $addCallback = function ($unType) use($type)
437
        {
438
            $type->addUnion($unType);
439
        };
440
441
        foreach ($node->childNodes as $childNode) {
442
            switch ($childNode->localName) {
443
                case 'simpleType':
444
                    call_user_func($this->loadSimpleType($type->getSchema(), $childNode, $addCallback));
445
                    break;
446
            }
447
        }
448
    }
449
450
    private function fillTypeNode(Type $type, DOMElement $node, $checkAbstract = true)
451
    {
452
453
        if($checkAbstract){
454
            $type->setAbstract($node->getAttribute("abstract")==="true" || $node->getAttribute("abstract")==="1");
455
        }
456
457
        foreach ($node->childNodes as $childNode) {
458
            switch ($childNode->localName) {
459
                case 'restriction':
460
                    $this->loadRestriction($type, $childNode);
461
                    break;
462
                case 'extension':
463
                    $this->loadExtension($type, $childNode);
0 ignored issues
show
$type of type object<Goetas\XML\XSDReader\Schema\Type\Type> is not a sub-type of object<Goetas\XML\XSDRea...a\Type\BaseComplexType>. It seems like you assume a child class of the class Goetas\XML\XSDReader\Schema\Type\Type 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...
464
                    break;
465
                case 'simpleContent':
466
                case 'complexContent':
467
                    $this->fillTypeNode($type, $childNode, false);
468
                    break;
469
            }
470
        }
471
    }
472
473
    private function loadExtension(BaseComplexType $type, DOMElement $node)
474
    {
475
        $extension = new Extension();
476
        $type->setExtension($extension);
477
478
        if ($node->hasAttribute("base")) {
479
            $parent = $this->findSomething('findType', $type->getSchema(), $node, $node->getAttribute("base"));
480
            $extension->setBase($parent);
0 ignored issues
show
It seems like $parent defined by $this->findSomething('fi...->getAttribute('base')) on line 479 can also be of type object<Goetas\XML\XSDRea...ma\Element\ElementItem>; however, Goetas\XML\XSDReader\Sch...ritance\Base::setBase() does only seem to accept object<Goetas\XML\XSDReader\Schema\Type\Type>, 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...
481
        }
482
483
        foreach ($node->childNodes as $childNode) {
484
            switch ($childNode->localName) {
485
                case 'sequence':
486
                case 'choice':
487
                case 'all':
488
                    $this->loadSequence($type, $childNode);
0 ignored issues
show
$type is of type object<Goetas\XML\XSDRea...a\Type\BaseComplexType>, but the function expects a object<Goetas\XML\XSDRea...ement\ElementContainer>.

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...
489
                    break;
490
                case 'attribute':
491
                    if ($childNode->hasAttribute("ref")) {
492
                        $attribute = $this->findSomething('findAttribute', $type->getSchema(), $node, $childNode->getAttribute("ref"));
493
                    } else {
494
                        $attribute = $this->loadAttribute($type->getSchema(), $childNode);
495
                    }
496
                    $type->addAttribute($attribute);
0 ignored issues
show
It seems like $attribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 492 can also be of type object<Goetas\XML\XSDRea...ma\Element\ElementItem> or object<Goetas\XML\XSDReader\Schema\Type\Type>; however, Goetas\XML\XSDReader\Sch...lexType::addAttribute() does only seem to accept object<Goetas\XML\XSDRea...ttribute\AttributeItem>, 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...
497
                    break;
498
                case 'attributeGroup':
499
                    $attribute = $this->findSomething('findAttributeGroup', $type->getSchema(), $node, $childNode->getAttribute("ref"));
500
                    $type->addAttribute($attribute);
0 ignored issues
show
It seems like $attribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 499 can also be of type object<Goetas\XML\XSDRea...ma\Element\ElementItem> or object<Goetas\XML\XSDReader\Schema\Type\Type>; however, Goetas\XML\XSDReader\Sch...lexType::addAttribute() does only seem to accept object<Goetas\XML\XSDRea...ttribute\AttributeItem>, 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...
501
                    break;
502
            }
503
        }
504
    }
505
506
    private function loadRestriction(Type $type, DOMElement $node)
507
    {
508
        $restriction = new Restriction();
509
        $type->setRestriction($restriction);
510
        if ($node->hasAttribute("base")) {
511
            $restrictedType = $this->findSomething('findType', $type->getSchema(), $node, $node->getAttribute("base"));
512
            $restriction->setBase($restrictedType);
0 ignored issues
show
It seems like $restrictedType defined by $this->findSomething('fi...->getAttribute('base')) on line 511 can also be of type object<Goetas\XML\XSDRea...ma\Element\ElementItem>; however, Goetas\XML\XSDReader\Sch...ritance\Base::setBase() does only seem to accept object<Goetas\XML\XSDReader\Schema\Type\Type>, 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...
513
        } else {
514
            $addCallback = function ($restType) use($restriction)
515
            {
516
                $restriction->setBase($restType);
517
            };
518
519
            foreach ($node->childNodes as $childNode) {
520
                switch ($childNode->localName) {
521
                    case 'simpleType':
522
                        call_user_func($this->loadSimpleType($type->getSchema(), $childNode, $addCallback));
523
                        break;
524
                }
525
            }
526
        }
527
        foreach ($node->childNodes as $childNode) {
528
            if (in_array($childNode->localName,
529
                [
530
                    'enumeration',
531
                    'pattern',
532
                    'length',
533
                    'minLength',
534
                    'maxLength',
535
                    'minInclusive',
536
                    'maxInclusive',
537
                    'minExclusive',
538
                    'maxExclusive'
539
                ], true)) {
540
                $restriction->addCheck($childNode->localName,
541
                    [
542
                        'value' => $childNode->getAttribute("value"),
543
                        'doc' => $this->getDocumentation($childNode)
544
                    ]);
545
            }
546
        }
547
    }
548
549
    private static function splitParts(DOMElement $node, $typeName)
550
    {
551
        $namespace = null;
552
        $prefix = null;
553
        $name = $typeName;
554
        if (strpos($typeName, ':') !== false) {
555
            list ($prefix, $name) = explode(':', $typeName);
556
        }
557
558
        $namespace = $node->lookupNamespaceURI($prefix ?: null);
559
        return array(
560
            $name,
561
            $namespace,
562
            $prefix
563
        );
564
    }
565
566
    /**
567
     *
568
     * @param string $finder
569
     * @param Schema $schema
570
     * @param DOMElement $node
571
     * @param string $typeName
572
     * @throws TypeException
573
     * @return ElementItem|Group|AttributeItem|AttribiuteGroup|Type
574
     */
575
    private function findSomething($finder, Schema $schema, DOMElement $node, $typeName)
576
    {
577
        list ($name, $namespace) = self::splitParts($node, $typeName);
578
579
        $namespace = $namespace ?: $schema->getTargetNamespace();
580
581
        try {
582
            return $schema->$finder($name, $namespace);
583
        } catch (TypeNotFoundException $e) {
584
            throw new TypeException(sprintf("Can't find %s named {%s}#%s, at line %d in %s ", strtolower(substr($finder, 4)), $namespace, $name, $node->getLineNo(), $node->ownerDocument->documentURI), 0, $e);
585
        }
586
    }
587
588
    private function loadElementDef(Schema $schema, DOMElement $node)
589
    {
590
        $element = new ElementDef($schema, $node->getAttribute("name"));
591
        $schema->addElement($element);
592
593
        return function () use ($element, $node) {
594
            $this->fillItem($element, $node);
595
        };
596
    }
597
598
    private function fillItem(Item $element, DOMElement $node)
599
    {
600
        $localType = null;
601
        foreach ($node->childNodes as $childNode) {
602
            switch ($childNode->localName) {
603
                case 'complexType':
604
                case 'simpleType':
605
                    $localType = $childNode;
606
                    break 2;
607
            }
608
        }
609
610
        if ($localType) {
611
            $addCallback = function ($type) use($element) {
612
                $element->setType($type);
613
            };
614
            switch ($localType->localName) {
615
                case 'complexType':
616
                    call_user_func($this->loadComplexType($element->getSchema(), $localType, $addCallback));
617
                    break;
618
                case 'simpleType':
619
                    call_user_func($this->loadSimpleType($element->getSchema(), $localType, $addCallback));
620
                    break;
621
            }
622
        } else {
623
624
            if ($node->getAttribute("type")) {
625
                $type = $this->findSomething('findType', $element->getSchema(), $node, $node->getAttribute("type"));
626
            } else {
627
                $type = $this->findSomething('findType', $element->getSchema(), $node, ($node->lookupPrefix(self::XSD_NS).":anyType"));
628
            }
629
630
            $element->setType($type);
0 ignored issues
show
It seems like $type can also be of type object<Goetas\XML\XSDRea...ma\Element\ElementItem>; however, Goetas\XML\XSDReader\Schema\Item::setType() does only seem to accept object<Goetas\XML\XSDReader\Schema\Type\Type>, 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...
631
        }
632
    }
633
634
    private function loadImport(Schema $schema, DOMElement $node)
635
    {
636
        $base = urldecode($node->ownerDocument->documentURI);
637
        $file = UrlUtils::resolveRelativeUrl($base, $node->getAttribute("schemaLocation"));
638
        if ($node->hasAttribute("namespace")
639
            && isset(self::$globalSchemaInfo[$node->getAttribute("namespace")])
640
            && isset($this->loadedFiles[self::$globalSchemaInfo[$node->getAttribute("namespace")]])
641
            ){
642
643
            $schema->addSchema($this->loadedFiles[self::$globalSchemaInfo[$node->getAttribute("namespace")]]);
644
645
            return function ()
646
            {
647
            };
648
        }elseif (isset($this->loadedFiles[$file])) {
649
            $schema->addSchema($this->loadedFiles[$file]);
650
            return function () {
651
            };
652
        }
653
654
        if (!$node->getAttribute("namespace")){
655
            $this->loadedFiles[$file] = $newSchema = $schema;
656
        }else{
657
            $this->loadedFiles[$file] = $newSchema = new Schema();
658
            $newSchema->addSchema($this->getGlobalSchema());
659
        }
660
661
        $xml = $this->getDOM(isset($this->knowLocationSchemas[$file])?$this->knowLocationSchemas[$file]:$file);
662
663
        $callbacks = $this->schemaNode($newSchema, $xml->documentElement, $schema);
664
665
        if ($node->getAttribute("namespace")){
666
            $schema->addSchema($newSchema);
667
        }
668
669
670
        return function () use($callbacks)
671
        {
672
            foreach ($callbacks as $callback) {
673
                call_user_func($callback);
674
            }
675
        };
676
    }
677
678
    private $globalSchema;
679
680
    /**
681
     *
682
     * @return \Goetas\XML\XSDReader\Schema\Schema
683
     */
684
    public function getGlobalSchema()
685
    {
686
        if (! $this->globalSchema) {
687
            $callbacks = array();
688
            $globalSchemas = array();
689
            foreach (self::$globalSchemaInfo as $namespace => $uri) {
690
                $this->loadedFiles[$uri] = $globalSchemas [$namespace] = $schema = new Schema();
691
                if($namespace === self::XSD_NS){
692
                    $this->globalSchema = $schema;
693
                }
694
                $xml = $this->getDOM($this->knowLocationSchemas[$uri]);
695
                $callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement));
696
            }
697
698
            $globalSchemas[self::XSD_NS]->addType(new SimpleType($globalSchemas[self::XSD_NS], "anySimpleType"));
699
            $globalSchemas[self::XSD_NS]->addType(new SimpleType($globalSchemas[self::XSD_NS], "anyType"));
700
701
            $globalSchemas[self::XML_NS]->addSchema($globalSchemas[self::XSD_NS], self::XSD_NS);
702
            $globalSchemas[self::XSD_NS]->addSchema($globalSchemas[self::XML_NS], self::XML_NS);
703
704
            foreach ($callbacks as $callback) {
705
                $callback();
706
            }
707
        }
708
        return $this->globalSchema;
709
    }
710
711
    /**
712
     * @return \Goetas\XML\XSDReader\Schema\Schema
713
     */
714
    public function readNode(\DOMNode $node, $file = 'schema.xsd')
715
    {
716
        $this->loadedFiles[$file] = $rootSchema = new Schema();
717
718
        $rootSchema->addSchema($this->getGlobalSchema());
719
        $callbacks = $this->schemaNode($rootSchema, $node);
0 ignored issues
show
$node of type object<DOMNode> is not a sub-type of object<DOMElement>. It seems like you assume a child class of the class DOMNode 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...
720
721
        foreach ($callbacks as $callback) {
722
            call_user_func($callback);
723
        }
724
725
        return $rootSchema;
726
    }
727
728
729
    /**
730
     * @return \Goetas\XML\XSDReader\Schema\Schema
731
     */
732
    public function readString($content, $file = 'schema.xsd')
733
    {
734
        $xml = new DOMDocument('1.0', 'UTF-8');
735
        if (! $xml->loadXML($content)) {
736
            throw new IOException("Can't load the schema");
737
        }
738
        $xml->documentURI = $file;
739
740
        return $this->readNode($xml->documentElement, $file);
741
    }
742
    /**
743
     * @return \Goetas\XML\XSDReader\Schema\Schema
744
     */
745
    public function readFile($file)
746
    {
747
        $xml = $this->getDOM($file);
748
        return $this->readNode($xml->documentElement, $file);
749
    }
750
751
    /**
752
     * @param string $file
753
     * @throws IOException
754
     * @return \DOMDocument
755
     */
756
    private function getDOM($file)
757
    {
758
        $xml = new DOMDocument('1.0', 'UTF-8');
759
        if (! $xml->load($file)) {
760
            throw new IOException("Can't load the file $file");
761
        }
762
        return $xml;
763
    }
764
}
765