Completed
Push — 0.2 ( 0a2fba...4d90a5 )
by Asmir
02:19
created

SchemaReader::readNode()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 9
cts 9
cp 1
rs 9.7998
c 0
b 0
f 0
cc 4
nc 8
nop 2
crap 4
1
<?php
2
namespace GoetasWebservices\XML\XSDReader;
3
4
use DOMDocument;
5
use DOMElement;
6
use DOMNode;
7
use GoetasWebservices\XML\XSDReader\Exception\IOException;
8
use GoetasWebservices\XML\XSDReader\Exception\TypeException;
9
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Attribute;
10
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeDef;
11
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeRef;
12
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup;
13
use GoetasWebservices\XML\XSDReader\Schema\Element\Element;
14
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer;
15
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
16
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem;
17
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef;
18
use GoetasWebservices\XML\XSDReader\Schema\Element\Group;
19
use GoetasWebservices\XML\XSDReader\Schema\Element\GroupRef;
20
use GoetasWebservices\XML\XSDReader\Schema\Exception\TypeNotFoundException;
21
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Extension;
22
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Restriction;
23
use GoetasWebservices\XML\XSDReader\Schema\Item;
24
use GoetasWebservices\XML\XSDReader\Schema\Schema;
25
use GoetasWebservices\XML\XSDReader\Schema\Type\BaseComplexType;
26
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
27
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexTypeSimpleContent;
28
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType;
29
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
30
use GoetasWebservices\XML\XSDReader\Utils\UrlUtils;
31
32
class SchemaReader
33
{
34
35
    const XSD_NS = "http://www.w3.org/2001/XMLSchema";
36
37
    const XML_NS = "http://www.w3.org/XML/1998/namespace";
38
39
    private $loadedFiles = array();
40
41
    private $knownLocationSchemas = array();
42
43
    private $knownNamespaceSchemaLocations = array();
44
45
    private static $globalSchemaInfo = array(
46
        self::XML_NS => 'http://www.w3.org/2001/xml.xsd',
47
        self::XSD_NS => 'http://www.w3.org/2001/XMLSchema.xsd'
48
    );
49
50 55
    public function __construct()
51
    {
52 55
        $this->addKnownSchemaLocation('http://www.w3.org/2001/xml.xsd', __DIR__ . '/Resources/xml.xsd');
53 55
        $this->addKnownSchemaLocation('http://www.w3.org/2001/XMLSchema.xsd', __DIR__ . '/Resources/XMLSchema.xsd');
54 55
        $this->addKnownSchemaLocation('http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd', __DIR__ . '/Resources/oasis-200401-wss-wssecurity-secext-1.0.xsd');
55 55
        $this->addKnownSchemaLocation('http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd', __DIR__ . '/Resources/oasis-200401-wss-wssecurity-utility-1.0.xsd');
56 55
        $this->addKnownSchemaLocation('https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd', __DIR__ . '/Resources/xmldsig-core-schema.xsd');
57 55
        $this->addKnownSchemaLocation('http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd', __DIR__ . '/Resources/xmldsig-core-schema.xsd');
58 55
    }
59
60
    /**
61
     * Override remote location with a local file.
62
     *
63
     * @param string $remote remote schema URL
64
     * @param string $local  local file path
65
     */
66 55
    public function addKnownSchemaLocation($remote, $local)
67
    {
68 55
        $this->knownLocationSchemas[$remote] = $local;
69 55
    }
70
71
    /**
72
     * Specify schema location by namespace.
73
     * This can be used for schemas which import namespaces but do not specify schemaLocation attributes.
74
     *
75
     * @param string $namespace namespace
76
     * @param string $location  schema URL
77
     */
78 1
    public function addKnownNamespaceSchemaLocation($namespace, $location)
79
    {
80 1
        $this->knownNamespaceSchemaLocations[$namespace] = $location;
81 1
    }
82
83 46
    private function loadAttributeGroup(Schema $schema, DOMElement $node)
84
    {
85 46
        $attGroup = new AttributeGroup($schema, $node->getAttribute("name"));
86 46
        $attGroup->setDoc($this->getDocumentation($node));
87 46
        $schema->addAttributeGroup($attGroup);
88
89
        return function () use ($schema, $node, $attGroup) {
90 46
            foreach ($node->childNodes as $childNode) {
91 46
                switch ($childNode->localName) {
92 46 View Code Duplication
                    case 'attribute':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
93 46
                        if ($childNode->hasAttribute("ref")) {
94 46
                            $attribute = $this->findSomething('findAttribute', $schema, $node, $childNode->getAttribute("ref"));
95 46
                        } else {
96 46
                            $attribute = $this->loadAttribute($schema, $childNode);
97
                        }
98 46
                        $attGroup->addAttribute($attribute);
0 ignored issues
show
Bug introduced by
It seems like $attribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 94 can also be of type object<GoetasWebservices...ma\Element\ElementItem> or object<GoetasWebservices...eader\Schema\Type\Type>; however, GoetasWebservices\XML\XS...e\Group::addAttribute() does only seem to accept object<GoetasWebservices...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...
99 46
                        break;
100 46 View Code Duplication
                    case 'attributeGroup':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
101
102 1
                        $attribute = $this->findSomething('findAttributeGroup', $schema, $node, $childNode->getAttribute("ref"));
103 1
                        $attGroup->addAttribute($attribute);
0 ignored issues
show
Bug introduced by
It seems like $attribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 102 can also be of type object<GoetasWebservices...ma\Element\ElementItem> or object<GoetasWebservices...eader\Schema\Type\Type>; however, GoetasWebservices\XML\XS...e\Group::addAttribute() does only seem to accept object<GoetasWebservices...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...
104 1
                        break;
105 46
                }
106 46
            }
107 46
        };
108
    }
109
110 46
    private function loadAttribute(Schema $schema, DOMElement $node)
111
    {
112 46
        $attribute = new Attribute($schema, $node->getAttribute("name"));
113 46
        $attribute->setDoc($this->getDocumentation($node));
114 46
        $this->fillItem($attribute, $node);
115
116 46
        if ($node->hasAttribute("nillable")) {
117 1
            $attribute->setNil($node->getAttribute("nillable") == "true");
118 1
        }
119 46
        if ($node->hasAttribute("form")) {
120 1
            $attribute->setQualified($node->getAttribute("form") == "qualified");
121 1
        }
122 46
        if ($node->hasAttribute("use")) {
123 46
            $attribute->setUse($node->getAttribute("use"));
124 46
        }
125 46
        return $attribute;
126
    }
127
128
129 46 View Code Duplication
    private function loadAttributeDef(Schema $schema, DOMElement $node)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
130
    {
131 46
        $attribute = new AttributeDef($schema, $node->getAttribute("name"));
132
133 46
        $schema->addAttribute($attribute);
134
135
        return function () use ($attribute, $schema, $node) {
136 46
            $this->fillItem($attribute, $node);
137 46
        };
138
    }
139
140
    /**
141
     * @param DOMElement $node
142
     * @return string
143
     */
144 46
    private function getDocumentation(DOMElement $node)
145
    {
146 46
        $doc = '';
147 46
        foreach ($node->childNodes as $childNode) {
148 46
            if ($childNode->localName == "annotation") {
149 46
                foreach ($childNode->childNodes as $subChildNode) {
150 46
                    if ($subChildNode->localName == "documentation") {
151 46
                        $doc .= ($subChildNode->nodeValue);
152 46
                    }
153 46
                }
154 46
            }
155 46
        }
156 46
        $doc = preg_replace('/[\t ]+/', ' ', $doc);
157 46
        return trim($doc);
158
    }
159
160
    /**
161
     *
162
     * @param Schema $schema
163
     * @param DOMElement $node
164
     * @param Schema $parent
165
     * @return array
166
     */
167 46
    private function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null)
168
    {
169 46
        $schema->setDoc($this->getDocumentation($node));
170
171 46
        if ($node->hasAttribute("targetNamespace")) {
172 46
            $schema->setTargetNamespace($node->getAttribute("targetNamespace"));
173 46
        } elseif ($parent) {
174
            $schema->setTargetNamespace($parent->getTargetNamespace());
175
        }
176 46
        $schema->setElementsQualification($node->getAttribute("elementFormDefault") == "qualified");
177 46
        $schema->setAttributesQualification($node->getAttribute("attributeFormDefault") == "qualified");
178 46
        $schema->setDoc($this->getDocumentation($node));
179 46
        $functions = array();
180
181 46
        foreach ($node->childNodes as $childNode) {
182 46
            switch ($childNode->localName) {
183 46
                case 'include':
184 46
                case 'import':
185 46
                    $functions[] = $this->loadImport($schema, $childNode);
186 46
                    break;
187 46
                case 'element':
188 46
                    $functions[] = $this->loadElementDef($schema, $childNode);
189 46
                    break;
190 46
                case 'attribute':
191 46
                    $functions[] = $this->loadAttributeDef($schema, $childNode);
192 46
                    break;
193 46
                case 'attributeGroup':
194 46
                    $functions[] = $this->loadAttributeGroup($schema, $childNode);
195 46
                    break;
196 46
                case 'group':
197 46
                    $functions[] = $this->loadGroup($schema, $childNode);
198 46
                    break;
199 46
                case 'complexType':
200 46
                    $functions[] = $this->loadComplexType($schema, $childNode);
201 46
                    break;
202 46
                case 'simpleType':
203 46
                    $functions[] = $this->loadSimpleType($schema, $childNode);
204 46
                    break;
205 46
            }
206 46
        }
207
208 46
        return $functions;
209
    }
210
211 46
    private function loadElement(Schema $schema, DOMElement $node)
212
    {
213 46
        $element = new Element($schema, $node->getAttribute("name"));
214 46
        $element->setDoc($this->getDocumentation($node));
215
216 46
        $this->fillItem($element, $node);
217
218 46
        if ($node->hasAttribute("maxOccurs")) {
219 46
            $element->setMax($node->getAttribute("maxOccurs") == "unbounded" ? -1 : (int)$node->getAttribute("maxOccurs"));
220 46
        }
221 46
        if ($node->hasAttribute("minOccurs")) {
222 46
            $element->setMin((int)$node->getAttribute("minOccurs"));
223 46
        }
224
225 46
        $xp = new \DOMXPath($node->ownerDocument);
226 46
        $xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema');
227
        
228 46
        if ($xp->query('ancestor::xs:choice', $node)->length) {
229 46
            $element->setMin(0);
230 46
        }
231
232 46
        if ($node->hasAttribute("nillable")) {
233 3
            $element->setNil($node->getAttribute("nillable") == "true");
234 3
        }
235 46
        if ($node->hasAttribute("form")) {
236 3
            $element->setQualified($node->getAttribute("form") == "qualified");
237 3
        }
238 46
        return $element;
239
    }
240
241 46
    private function loadGroupRef(Group $referenced, DOMElement $node)
242
    {
243 46
        $ref = new GroupRef($referenced);
244 46
        $ref->setDoc($this->getDocumentation($node));
245
246 46 View Code Duplication
        if ($node->hasAttribute("maxOccurs")) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
247 46
            $ref->setMax($node->getAttribute("maxOccurs") == "unbounded" ? -1 : (int)$node->getAttribute("maxOccurs"));
248 46
        }
249 46
        if ($node->hasAttribute("minOccurs")) {
250 46
            $ref->setMin((int)$node->getAttribute("minOccurs"));
251 46
        }
252
253 46
        return $ref;
254
    }
255
256 46
    private function loadElementRef(ElementDef $referenced, DOMElement $node)
257
    {
258 46
        $ref = new ElementRef($referenced);
259 46
        $ref->setDoc($this->getDocumentation($node));
260
261 46 View Code Duplication
        if ($node->hasAttribute("maxOccurs")) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
262 46
            $ref->setMax($node->getAttribute("maxOccurs") == "unbounded" ? -1 : (int)$node->getAttribute("maxOccurs"));
263 46
        }
264 46
        if ($node->hasAttribute("minOccurs")) {
265 46
            $ref->setMin((int)$node->getAttribute("minOccurs"));
266 46
        }
267
        
268 46
        $xp = new \DOMXPath($node->ownerDocument);
269 46
        $xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema');
270
        
271 46
        if ($xp->query('ancestor::xs:choice', $node)->length) {
272 46
            $ref->setMin(0);
273 46
        }
274
        
275 46
        if ($node->hasAttribute("nillable")) {
276
            $ref->setNil($node->getAttribute("nillable") == "true");
277
        }
278 46
        if ($node->hasAttribute("form")) {
279
            $ref->setQualified($node->getAttribute("form") == "qualified");
280
        }
281
282 46
        return $ref;
283
    }
284
285
286 46
    private function loadAttributeRef(AttributeDef $referencedAttribiute, DOMElement $node)
287
    {
288 46
        $attribute = new AttributeRef($referencedAttribiute);
289 46
        $attribute->setDoc($this->getDocumentation($node));
290
291 46
        if ($node->hasAttribute("nillable")) {
292
            $attribute->setNil($node->getAttribute("nillable") == "true");
293
        }
294 46
        if ($node->hasAttribute("form")) {
295
            $attribute->setQualified($node->getAttribute("form") == "qualified");
296
        }
297 46
        if ($node->hasAttribute("use")) {
298
            $attribute->setUse($node->getAttribute("use"));
299
        }
300 46
        return $attribute;
301
    }
302
303 46
    private function loadSequence(ElementContainer $elementContainer, DOMElement $node, $max = null)
304
    {
305 46
        $max = $max || $node->getAttribute("maxOccurs") == "unbounded" || $node->getAttribute("maxOccurs") > 1 ? 2 : null;
306
307 46
        foreach ($node->childNodes as $childNode) {
308
309 46
            switch ($childNode->localName) {
310 46
                case 'choice':
311 46
                case 'sequence':
312 46
                case 'all':
313 46
                    $this->loadSequence($elementContainer, $childNode, $max);
314 46
                    break;
315 46
                case 'element':
316 46
                    if ($childNode->hasAttribute("ref")) {
317 46
                        $referencedElement = $this->findSomething('findElement', $elementContainer->getSchema(), $node, $childNode->getAttribute("ref"));
318 46
                        $element = $this->loadElementRef($referencedElement, $childNode);
0 ignored issues
show
Bug introduced by
It seems like $referencedElement defined by $this->findSomething('fi...e->getAttribute('ref')) on line 317 can also be of type object<GoetasWebservices...ma\Element\ElementItem> or object<GoetasWebservices...eader\Schema\Type\Type>; however, GoetasWebservices\XML\XS...eader::loadElementRef() does only seem to accept object<GoetasWebservices...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...
319 46
                    } else {
320 46
                        $element = $this->loadElement($elementContainer->getSchema(), $childNode);
321
                    }
322 46
                    if ($max) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $max of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
323 46
                        $element->setMax($max);
324 46
                    }
325 46
                    $elementContainer->addElement($element);
326 46
                    break;
327 46 View Code Duplication
                case 'group':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
328 46
                    $referencedGroup = $this->findSomething('findGroup', $elementContainer->getSchema(), $node, $childNode->getAttribute("ref"));
329
330 46
                    $group = $this->loadGroupRef($referencedGroup, $childNode);
0 ignored issues
show
Bug introduced by
It seems like $referencedGroup defined by $this->findSomething('fi...e->getAttribute('ref')) on line 328 can also be of type object<GoetasWebservices...ma\Element\ElementItem> or object<GoetasWebservices...eader\Schema\Type\Type>; however, GoetasWebservices\XML\XS...aReader::loadGroupRef() 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...
331 46
                    $elementContainer->addElement($group);
332 46
                    break;
333 46
            }
334 46
        }
335 46
    }
336
337 46
    private function loadGroup(Schema $schema, DOMElement $node)
338
    {
339 46
        $group = new Group($schema, $node->getAttribute("name"));
340 46
        $group->setDoc($this->getDocumentation($node));
341
342 46 View Code Duplication
        if ($node->hasAttribute("maxOccurs")) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
343
            $group->setMax($node->getAttribute("maxOccurs") == "unbounded" ? -1 : (int)$node->getAttribute("maxOccurs"));
344
        }
345 46
        if ($node->hasAttribute("minOccurs")) {
346
            $group->setMin((int)$node->getAttribute("minOccurs"));
347
        }
348
349 46
        $schema->addGroup($group);
350
351
        return function () use ($group, $node) {
352 46
            foreach ($node->childNodes as $childNode) {
353 46
                switch ($childNode->localName) {
354 46
                    case 'sequence':
355 46
                    case 'choice':
356 46
                    case 'all':
357 46
                        $this->loadSequence($group, $childNode);
358 46
                        break;
359 46
                }
360 46
            }
361 46
        };
362
    }
363
364 46
    private function loadComplexType(Schema $schema, DOMElement $node, $callback = null)
365
    {
366 46
        $isSimple = false;
367
368 46
        foreach ($node->childNodes as $childNode) {
369 46
            if ($childNode->localName === "simpleContent") {
370 2
                $isSimple = true;
371 2
                break;
372
            }
373 46
        }
374
375 46
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute("name")) : new ComplexType($schema, $node->getAttribute("name"));
376
377 46
        $type->setDoc($this->getDocumentation($node));
378 46
        if ($node->getAttribute("name")) {
379 46
            $schema->addType($type);
380 46
        }
381
382
        return function () use ($type, $node, $schema, $callback) {
383
384 46
            $this->fillTypeNode($type, $node);
385
386 46
            foreach ($node->childNodes as $childNode) {
387 46
                switch ($childNode->localName) {
388 46
                    case 'sequence':
389 46
                    case 'choice':
390 46
                    case 'all':
391 46
                        $this->loadSequence($type, $childNode);
0 ignored issues
show
Bug introduced by
It seems like $type defined by $isSimple ? new \GoetasW...->getAttribute('name')) on line 375 can also be of type object<GoetasWebservices...mplexTypeSimpleContent>; 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...
392 46
                        break;
393 46
                    case 'attribute':
394 46
                        if ($childNode->hasAttribute("ref")) {
395 46
                            $referencedAttribute = $this->findSomething('findAttribute', $schema, $node, $childNode->getAttribute("ref"));
396 46
                            $attribute = $this->loadAttributeRef($referencedAttribute, $childNode);
0 ignored issues
show
Bug introduced by
It seems like $referencedAttribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 395 can also be of type object<GoetasWebservices...ma\Element\ElementItem> or object<GoetasWebservices...eader\Schema\Type\Type>; however, GoetasWebservices\XML\XS...der::loadAttributeRef() does only seem to accept object<GoetasWebservices...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...
397 46
                        } else {
398 46
                            $attribute = $this->loadAttribute($schema, $childNode);
399
                        }
400
401 46
                        $type->addAttribute($attribute);
402 46
                        break;
403 46 View Code Duplication
                    case 'group':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
404 1
                        $referencedGroup = $this->findSomething('findGroup', $schema, $node, $childNode->getAttribute("ref"));
405 1
                        $group = $this->loadGroupRef($referencedGroup, $childNode);
0 ignored issues
show
Bug introduced by
It seems like $referencedGroup defined by $this->findSomething('fi...e->getAttribute('ref')) on line 404 can also be of type object<GoetasWebservices...ma\Element\ElementItem> or object<GoetasWebservices...eader\Schema\Type\Type>; however, GoetasWebservices\XML\XS...aReader::loadGroupRef() 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...
406 1
                        $type->addElement($group);
0 ignored issues
show
Bug introduced by
The method addElement does only exist in GoetasWebservices\XML\XS...Schema\Type\ComplexType, but not in GoetasWebservices\XML\XS...omplexTypeSimpleContent.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
407 1
                        break;
408 46 View Code Duplication
                    case 'attributeGroup':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
409 2
                        $attribute = $this->findSomething('findAttributeGroup', $schema, $node, $childNode->getAttribute("ref"));
410 2
                        $type->addAttribute($attribute);
0 ignored issues
show
Bug introduced by
It seems like $attribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 409 can also be of type object<GoetasWebservices...ma\Element\ElementItem> or object<GoetasWebservices...eader\Schema\Type\Type>; however, GoetasWebservices\XML\XS...lexType::addAttribute() does only seem to accept object<GoetasWebservices...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...
411 2
                        break;
412 46
                }
413 46
            }
414
415 46
            if ($callback) {
416 46
                call_user_func($callback, $type);
417 46
            }
418 46
        };
419
    }
420
421 46
    private function loadSimpleType(Schema $schema, DOMElement $node, $callback = null)
422
    {
423 46
        $type = new SimpleType($schema, $node->getAttribute("name"));
424 46
        $type->setDoc($this->getDocumentation($node));
425 46
        if ($node->getAttribute("name")) {
426 46
            $schema->addType($type);
427 46
        }
428
429
        return function () use ($type, $node, $callback) {
430 46
            $this->fillTypeNode($type, $node);
431
432 46
            foreach ($node->childNodes as $childNode) {
433 46
                switch ($childNode->localName) {
434 46
                    case 'union':
435 46
                        $this->loadUnion($type, $childNode);
436 46
                        break;
437 46
                    case 'list':
438 46
                        $this->loadList($type, $childNode);
439 46
                        break;
440 46
                }
441 46
            }
442
443 46
            if ($callback) {
444 46
                call_user_func($callback, $type);
445 46
            }
446 46
        };
447
    }
448
449 46
    private function loadList(SimpleType $type, DOMElement $node)
450
    {
451 46 View Code Duplication
        if ($node->hasAttribute("itemType")) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
452 46
            $type->setList($this->findSomething('findType', $type->getSchema(), $node, $node->getAttribute("itemType")));
0 ignored issues
show
Bug introduced by
It seems like $this->findSomething('fi...tAttribute('itemType')) targeting GoetasWebservices\XML\XS...Reader::findSomething() can also be of type object<GoetasWebservices...ma\Element\ElementItem> or object<GoetasWebservices...eader\Schema\Type\Type>; however, GoetasWebservices\XML\XS...e\SimpleType::setList() does only seem to accept object<GoetasWebservices...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...
453 46
        } else {
454
            $addCallback = function ($list) use ($type) {
455 46
                $type->setList($list);
456 46
            };
457
458 46
            foreach ($node->childNodes as $childNode) {
459 46
                switch ($childNode->localName) {
460 46
                    case 'simpleType':
461 46
                        call_user_func($this->loadSimpleType($type->getSchema(), $childNode, $addCallback));
462 46
                        break;
463 46
                }
464 46
            }
465
        }
466 46
    }
467
468 46
    private function loadUnion(SimpleType $type, DOMElement $node)
469
    {
470 46
        if ($node->hasAttribute("memberTypes")) {
471 46
            $types = preg_split('/\s+/', $node->getAttribute("memberTypes"));
472 46
            foreach ($types as $typeName) {
473 46
                $type->addUnion($this->findSomething('findType', $type->getSchema(), $node, $typeName));
0 ignored issues
show
Bug introduced by
It seems like $this->findSomething('fi...ma(), $node, $typeName) targeting GoetasWebservices\XML\XS...Reader::findSomething() can also be of type object<GoetasWebservices...ma\Element\ElementItem> or object<GoetasWebservices...eader\Schema\Type\Type>; however, GoetasWebservices\XML\XS...\SimpleType::addUnion() does only seem to accept object<GoetasWebservices...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...
474 46
            }
475 46
        }
476
        $addCallback = function ($unType) use ($type) {
477 46
            $type->addUnion($unType);
478 46
        };
479
480 46
        foreach ($node->childNodes as $childNode) {
481 46
            switch ($childNode->localName) {
482 46
                case 'simpleType':
483 46
                    call_user_func($this->loadSimpleType($type->getSchema(), $childNode, $addCallback));
484 46
                    break;
485 46
            }
486 46
        }
487 46
    }
488
489 46
    private function fillTypeNode(Type $type, DOMElement $node, $checkAbstract = true)
490
    {
491
492 46
        if ($checkAbstract) {
493 46
            $type->setAbstract($node->getAttribute("abstract") === "true" || $node->getAttribute("abstract") === "1");
494 46
        }
495
496 46
        foreach ($node->childNodes as $childNode) {
497 46
            switch ($childNode->localName) {
498 46
                case 'restriction':
499 46
                    $this->loadRestriction($type, $childNode);
500 46
                    break;
501 46
                case 'extension':
502 46
                    $this->loadExtension($type, $childNode);
0 ignored issues
show
Compatibility introduced by
$type of type object<GoetasWebservices...eader\Schema\Type\Type> is not a sub-type of object<GoetasWebservices...a\Type\BaseComplexType>. It seems like you assume a child class of the class GoetasWebservices\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...
503 46
                    break;
504 46
                case 'simpleContent':
505 46
                case 'complexContent':
506 46
                    $this->fillTypeNode($type, $childNode, false);
507 46
                    break;
508 46
            }
509 46
        }
510 46
    }
511
512 46
    private function loadExtension(BaseComplexType $type, DOMElement $node)
513
    {
514 46
        $extension = new Extension();
515 46
        $type->setExtension($extension);
516
517 46
        if ($node->hasAttribute("base")) {
518 46
            $parent = $this->findSomething('findType', $type->getSchema(), $node, $node->getAttribute("base"));
519 46
            $extension->setBase($parent);
0 ignored issues
show
Bug introduced by
It seems like $parent defined by $this->findSomething('fi...->getAttribute('base')) on line 518 can also be of type object<GoetasWebservices...ma\Element\ElementItem>; however, GoetasWebservices\XML\XS...ritance\Base::setBase() does only seem to accept object<GoetasWebservices...eader\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...
520 46
        }
521
522 46
        foreach ($node->childNodes as $childNode) {
523 46
            switch ($childNode->localName) {
524 46
                case 'sequence':
525 46
                case 'choice':
526 46
                case 'all':
527 46
                    $this->loadSequence($type, $childNode);
0 ignored issues
show
Documentation introduced by
$type is of type object<GoetasWebservices...a\Type\BaseComplexType>, but the function expects a object<GoetasWebservices...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...
528 46
                    break;
529 46 View Code Duplication
                case 'attribute':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
530 46
                    if ($childNode->hasAttribute("ref")) {
531 46
                        $attribute = $this->findSomething('findAttribute', $type->getSchema(), $node, $childNode->getAttribute("ref"));
532 46
                    } else {
533 46
                        $attribute = $this->loadAttribute($type->getSchema(), $childNode);
534
                    }
535 46
                    $type->addAttribute($attribute);
0 ignored issues
show
Bug introduced by
It seems like $attribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 531 can also be of type object<GoetasWebservices...ma\Element\ElementItem> or object<GoetasWebservices...eader\Schema\Type\Type>; however, GoetasWebservices\XML\XS...lexType::addAttribute() does only seem to accept object<GoetasWebservices...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...
536 46
                    break;
537 46
                case 'attributeGroup':
538 46
                    $attribute = $this->findSomething('findAttributeGroup', $type->getSchema(), $node, $childNode->getAttribute("ref"));
539 46
                    $type->addAttribute($attribute);
0 ignored issues
show
Bug introduced by
It seems like $attribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 538 can also be of type object<GoetasWebservices...ma\Element\ElementItem> or object<GoetasWebservices...eader\Schema\Type\Type>; however, GoetasWebservices\XML\XS...lexType::addAttribute() does only seem to accept object<GoetasWebservices...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...
540 46
                    break;
541 46
            }
542 46
        }
543 46
    }
544
545 46
    private function loadRestriction(Type $type, DOMElement $node)
546
    {
547 46
        $restriction = new Restriction();
548 46
        $type->setRestriction($restriction);
549 46 View Code Duplication
        if ($node->hasAttribute("base")) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
550 46
            $restrictedType = $this->findSomething('findType', $type->getSchema(), $node, $node->getAttribute("base"));
551 46
            $restriction->setBase($restrictedType);
0 ignored issues
show
Bug introduced by
It seems like $restrictedType defined by $this->findSomething('fi...->getAttribute('base')) on line 550 can also be of type object<GoetasWebservices...ma\Element\ElementItem>; however, GoetasWebservices\XML\XS...ritance\Base::setBase() does only seem to accept object<GoetasWebservices...eader\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...
552 46
        } else {
553
            $addCallback = function ($restType) use ($restriction) {
554 46
                $restriction->setBase($restType);
555 46
            };
556
557 46
            foreach ($node->childNodes as $childNode) {
558 46
                switch ($childNode->localName) {
559 46
                    case 'simpleType':
560 46
                        call_user_func($this->loadSimpleType($type->getSchema(), $childNode, $addCallback));
561 46
                        break;
562 46
                }
563 46
            }
564
        }
565 46
        foreach ($node->childNodes as $childNode) {
566 46
            if (in_array($childNode->localName,
567
                [
568 46
                    'enumeration',
569 46
                    'pattern',
570 46
                    'length',
571 46
                    'minLength',
572 46
                    'maxLength',
573 46
                    'minInclusive',
574 46
                    'maxInclusive',
575 46
                    'minExclusive',
576 46
                    'maxExclusive',
577 46
                    'fractionDigits',
578 46
                    'totalDigits',
579
                    'whiteSpace'
580 46
                ], true)) {
581 46
                $restriction->addCheck($childNode->localName,
582
                    [
583 46
                        'value' => $childNode->getAttribute("value"),
584 46
                        'doc' => $this->getDocumentation($childNode)
585 46
                    ]);
586 46
            }
587 46
        }
588 46
    }
589
590 46
    private static function splitParts(DOMElement $node, $typeName)
591
    {
592 46
        $namespace = null;
0 ignored issues
show
Unused Code introduced by
$namespace is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
593 46
        $prefix = null;
594 46
        $name = $typeName;
595 46
        if (strpos($typeName, ':') !== false) {
596 46
            list ($prefix, $name) = explode(':', $typeName);
597 46
        }
598
599 46
        $namespace = $node->lookupNamespaceURI($prefix ?: null);
600
        return array(
601 46
            $name,
602 46
            $namespace,
603
            $prefix
604 46
        );
605
    }
606
607
    /**
608
     *
609
     * @param string $finder
610
     * @param Schema $schema
611
     * @param DOMElement $node
612
     * @param string $typeName
613
     * @throws TypeException
614
     * @return ElementItem|Group|AttributeItem|AttribiuteGroup|Type
615
     */
616 46
    private function findSomething($finder, Schema $schema, DOMElement $node, $typeName)
617
    {
618 46
        list ($name, $namespace) = self::splitParts($node, $typeName);
619
620 46
        $namespace = $namespace ?: $schema->getTargetNamespace();
621
622
        try {
623 46
            return $schema->$finder($name, $namespace);
624
        } catch (TypeNotFoundException $e) {
625
            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);
626
        }
627
    }
628
629 46 View Code Duplication
    private function loadElementDef(Schema $schema, DOMElement $node)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
630
    {
631 46
        $element = new ElementDef($schema, $node->getAttribute("name"));
632 46
        $schema->addElement($element);
633
634
        return function () use ($element, $node) {
635 46
            $this->fillItem($element, $node);
636 46
        };
637
    }
638
639 46
    private function fillItem(Item $element, DOMElement $node)
640
    {
641 46
        $localType = null;
642 46
        foreach ($node->childNodes as $childNode) {
643 46
            switch ($childNode->localName) {
644 46
                case 'complexType':
645 46
                case 'simpleType':
646 46
                    $localType = $childNode;
647 46
                    break 2;
648 46
            }
649 46
        }
650
651 46
        if ($localType) {
652
            $addCallback = function ($type) use ($element) {
653 46
                $element->setType($type);
654 46
            };
655 46
            switch ($localType->localName) {
656 46
                case 'complexType':
657 46
                    call_user_func($this->loadComplexType($element->getSchema(), $localType, $addCallback));
658 46
                    break;
659 46
                case 'simpleType':
660 46
                    call_user_func($this->loadSimpleType($element->getSchema(), $localType, $addCallback));
661 46
                    break;
662 46
            }
663 46
        } else {
664
665 46
            if ($node->getAttribute("type")) {
666 46
                $type = $this->findSomething('findType', $element->getSchema(), $node, $node->getAttribute("type"));
667 46
            } else {
668 46
                $type = $this->findSomething('findType', $element->getSchema(), $node, ($node->lookupPrefix(self::XSD_NS) . ":anyType"));
669
            }
670
671 46
            $element->setType($type);
0 ignored issues
show
Bug introduced by
It seems like $type can also be of type object<GoetasWebservices...ma\Element\ElementItem>; however, GoetasWebservices\XML\XS...\Schema\Item::setType() does only seem to accept object<GoetasWebservices...eader\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...
672
        }
673 46
    }
674
675 46
    private function loadImport(Schema $schema, DOMElement $node)
676
    {
677 46
        $base = urldecode($node->ownerDocument->documentURI);
678 46
        $schemaLocation = $node->getAttribute("schemaLocation");
679 46
        if (!$schemaLocation && $node->hasAttribute("namespace") && isset($this->knownNamespaceSchemaLocations[$node->getAttribute("namespace")])) {
680 1
            $schemaLocation = $this->knownNamespaceSchemaLocations[$node->getAttribute("namespace")];
681 1
        }
682 46
        $file = UrlUtils::resolveRelativeUrl($base, $schemaLocation);
683 46
        if ($node->hasAttribute("namespace")
684 46
            && isset(self::$globalSchemaInfo[$node->getAttribute("namespace")])
685 46
            && isset($this->loadedFiles[self::$globalSchemaInfo[$node->getAttribute("namespace")]])
686 46
        ) {
687
688 46
            $schema->addSchema($this->loadedFiles[self::$globalSchemaInfo[$node->getAttribute("namespace")]]);
689
690
            return function () {
691 46
            };
692 4
        } elseif ($node->hasAttribute("namespace")
693 4
            && isset($this->loadedFiles[$this->getNamespaceSpecificFileIndex($file, $node->getAttribute("namespace"))])) {
694 3
            $schema->addSchema($this->loadedFiles[$this->getNamespaceSpecificFileIndex($file, $node->getAttribute("namespace"))]);
695
            return function () {
696 3
            };
697 2
        } elseif (isset($this->loadedFiles[$file])) {
698
            $schema->addSchema($this->loadedFiles[$file]);
699
            return function () {
700
            };
701
        }
702
703 2
        if (!$node->getAttribute("namespace")) {
704 1
            $this->loadedFiles[$file] = $newSchema = $schema;
705 1
        } else {
706 1
            $this->loadedFiles[$file] = $newSchema = new Schema();
707 1
            $newSchema->addSchema($this->getGlobalSchema());
708
        }
709
710 2
        $xml = $this->getDOM(isset($this->knownLocationSchemas[$file]) ? $this->knownLocationSchemas[$file] : $file);
711
712 2
        $callbacks = $this->schemaNode($newSchema, $xml->documentElement, $schema);
713
714 2
        if ($node->getAttribute("namespace")) {
715 1
            $schema->addSchema($newSchema);
716 1
        }
717
718
719 2
        return function () use ($callbacks) {
720 2
            foreach ($callbacks as $callback) {
721 2
                call_user_func($callback);
722 2
            }
723 2
        };
724
    }
725
726
    private $globalSchema;
727
728
    /**
729
     *
730
     * @return Schema
731
     */
732 46
    public function getGlobalSchema()
733
    {
734 46
        if (!$this->globalSchema) {
735 46
            $callbacks = array();
736 46
            $globalSchemas = array();
737 46
            foreach (self::$globalSchemaInfo as $namespace => $uri) {
738 46
                $this->loadedFiles[$uri] = $globalSchemas [$namespace] = $schema = new Schema();
739 46
                if ($namespace === self::XSD_NS) {
740 46
                    $this->globalSchema = $schema;
741 46
                }
742 46
                $xml = $this->getDOM($this->knownLocationSchemas[$uri]);
743 46
                $callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement));
744 46
            }
745
746 46
            $globalSchemas[self::XSD_NS]->addType(new SimpleType($globalSchemas[self::XSD_NS], "anySimpleType"));
747 46
            $globalSchemas[self::XSD_NS]->addType(new SimpleType($globalSchemas[self::XSD_NS], "anyType"));
748
749 46
            $globalSchemas[self::XML_NS]->addSchema($globalSchemas[self::XSD_NS], self::XSD_NS);
750 46
            $globalSchemas[self::XSD_NS]->addSchema($globalSchemas[self::XML_NS], self::XML_NS);
751
752 46
            foreach ($callbacks as $callback) {
753 46
                $callback();
754 46
            }
755 46
        }
756 46
        return $this->globalSchema;
757
    }
758
759
    /**
760
     * @param DOMNode $node
761
     * @param string  $file
762
     * 
763
     * @return Schema
764
     */
765 46
    public function readNode(DOMNode $node, $file = 'schema.xsd')
766
    {
767 46
        $fileKey = $node instanceof DOMElement && $node->hasAttribute('targetNamespace') ? $this->getNamespaceSpecificFileIndex($file, $node->getAttribute('targetNamespace')) : $file;
768 46
        $this->loadedFiles[$fileKey] = $rootSchema = new Schema();
769
770 46
        $rootSchema->addSchema($this->getGlobalSchema());
771 46
        $callbacks = $this->schemaNode($rootSchema, $node);
0 ignored issues
show
Compatibility introduced by
$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...
772
773 46
        foreach ($callbacks as $callback) {
774 40
            call_user_func($callback);
775 46
        }
776
777 46
        return $rootSchema;
778
    }
779
780
    /**
781
     * It is possible that a single file contains multiple <xsd:schema/> nodes, for instance in a WSDL file.
782
     *
783
     * Each of these  <xsd:schema/> nodes typically target a specific namespace. Append the target namespace to the
784
     * file to distinguish between multiple schemas in a single file.
785
     *
786
     * @param string $file
787
     * @param string $targetNamespace
788
     *
789
     * @return string
790
     */
791 46
    private function getNamespaceSpecificFileIndex($file, $targetNamespace)
792
    {
793 46
        return $file . '#' . $targetNamespace;
794
    }
795
796
    /**
797
     * @param string $content
798
     * @param string $file
799
     *
800
     * @return Schema
801
     *
802
     * @throws IOException
803
     */
804 44
    public function readString($content, $file = 'schema.xsd')
805
    {
806 44
        $xml = new DOMDocument('1.0', 'UTF-8');
807 44
        if (!$xml->loadXML($content)) {
808
            throw new IOException("Can't load the schema");
809
        }
810 44
        $xml->documentURI = $file;
811
812 44
        return $this->readNode($xml->documentElement, $file);
813
    }
814
815
    /**
816
     * @param string $file
817
     *
818
     * @return Schema
819
     */
820 2
    public function readFile($file)
821
    {
822 2
        $xml = $this->getDOM($file);
823 2
        return $this->readNode($xml->documentElement, $file);
824
    }
825
826
    /**
827
     * @param string $file
828
     *
829
     * @return DOMDocument
830
     *
831
     * @throws IOException
832
     */
833 46
    private function getDOM($file)
834
    {
835 46
        $xml = new DOMDocument('1.0', 'UTF-8');
836 46
        if (!$xml->load($file)) {
837
            throw new IOException("Can't load the file $file");
838
        }
839 46
        return $xml;
840
    }
841
}
842