Completed
Pull Request — 0.2 (#47)
by Carsten
05:52
created

SchemaReader::addKnownNamespaceSchemaLocation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 1
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 54
    );
49
50 54
    public function __construct()
51 54
    {
52 54
        $this->addKnownSchemaLocation('http://www.w3.org/2001/xml.xsd', __DIR__ . '/Resources/xml.xsd');
53 54
        $this->addKnownSchemaLocation('http://www.w3.org/2001/XMLSchema.xsd', __DIR__ . '/Resources/XMLSchema.xsd');
54 54
        $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 54
        $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 54
        $this->addKnownSchemaLocation('https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd', __DIR__ . '/Resources/xmldsig-core-schema.xsd');
57
        $this->addKnownSchemaLocation('http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd', __DIR__ . '/Resources/xmldsig-core-schema.xsd');
58 54
    }
59
60 54
    /**
61 54
     * Override remote location with a local file.
62
     *
63 45
     * @param string $remote remote schema URL
64
     * @param string $local  local file path
65 45
     */
66 45
    public function addKnownSchemaLocation($remote, $local)
67 45
    {
68
        $this->knownLocationSchemas[$remote] = $local;
69
    }
70 45
71 45
    /**
72 45
     * Specify schema location by namespace.
73 45
     * This can be used for schemas which import namespaces but do not specify schemaLocation attributes.
74 45
     *
75 45
     * @param string $namespace namespace
76 45
     * @param string $location  schema URL
77
     */
78 45
    public function addKnownNamespaceSchemaLocation($namespace, $location)
79 45
    {
80 45
        $this->knownNamespaceSchemaLocations[$namespace] = $location;
81
    }
82 1
83 1
    private function loadAttributeGroup(Schema $schema, DOMElement $node)
84 1
    {
85 45
        $attGroup = new AttributeGroup($schema, $node->getAttribute("name"));
86 45
        $attGroup->setDoc($this->getDocumentation($node));
87 45
        $schema->addAttributeGroup($attGroup);
88
89
        return function () use ($schema, $node, $attGroup) {
90 45
            foreach ($node->childNodes as $childNode) {
91
                switch ($childNode->localName) {
92 45 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 45
                        if ($childNode->hasAttribute("ref")) {
94 45
                            $attribute = $this->findSomething('findAttribute', $schema, $node, $childNode->getAttribute("ref"));
95
                        } else {
96 45
                            $attribute = $this->loadAttribute($schema, $childNode);
97 1
                        }
98 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 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 45
                        break;
100 1 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 1
102 45
                        $attribute = $this->findSomething('findAttributeGroup', $schema, $node, $childNode->getAttribute("ref"));
103 45
                        $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 45
                        break;
105 45
                }
106
            }
107
        };
108
    }
109 45
110
    private function loadAttribute(Schema $schema, DOMElement $node)
111 45
    {
112
        $attribute = new Attribute($schema, $node->getAttribute("name"));
113 45
        $attribute->setDoc($this->getDocumentation($node));
114
        $this->fillItem($attribute, $node);
115
116 45
        if ($node->hasAttribute("nillable")) {
117 45
            $attribute->setNil($node->getAttribute("nillable") == "true");
118
        }
119
        if ($node->hasAttribute("form")) {
120
            $attribute->setQualified($node->getAttribute("form") == "qualified");
121
        }
122
        if ($node->hasAttribute("use")) {
123
            $attribute->setUse($node->getAttribute("use"));
124 45
        }
125
        return $attribute;
126 45
    }
127 45
128 45
129 45 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 45
    {
131 45
        $attribute = new AttributeDef($schema, $node->getAttribute("name"));
132 45
133 45
        $schema->addAttribute($attribute);
134 45
135 45
        return function () use ($attribute, $schema, $node) {
136 45
            $this->fillItem($attribute, $node);
137 45
        };
138
    }
139
140
    /**
141
     * @param DOMElement $node
142
     * @return string
143
     */
144
    private function getDocumentation(DOMElement $node)
145
    {
146
        $doc = '';
147 45
        foreach ($node->childNodes as $childNode) {
148
            if ($childNode->localName == "annotation") {
149 45
                foreach ($childNode->childNodes as $subChildNode) {
150
                    if ($subChildNode->localName == "documentation") {
151 45
                        $doc .= ($subChildNode->nodeValue);
152 45
                    }
153 45
                }
154
            }
155
        }
156 45
        $doc = preg_replace('/[\t ]+/', ' ', $doc);
157 45
        return trim($doc);
158 45
    }
159 45
160
    /**
161 45
     *
162 45
     * @param Schema $schema
163 45
     * @param DOMElement $node
164 45
     * @param Schema $parent
165 45
     * @return array
166 45
     */
167 45
    private function schemaNode(Schema $schema, DOMElement $node, Schema $parent = null)
168 45
    {
169 45
        $schema->setDoc($this->getDocumentation($node));
170 45
171 45
        if ($node->hasAttribute("targetNamespace")) {
172 45
            $schema->setTargetNamespace($node->getAttribute("targetNamespace"));
173 45
        } elseif ($parent) {
174 45
            $schema->setTargetNamespace($parent->getTargetNamespace());
175 45
        }
176 45
        $schema->setElementsQualification($node->getAttribute("elementFormDefault") == "qualified");
177 45
        $schema->setAttributesQualification($node->getAttribute("attributeFormDefault") == "qualified");
178 45
        $schema->setDoc($this->getDocumentation($node));
179 45
        $functions = array();
180 45
181 45
        foreach ($node->childNodes as $childNode) {
182 45
            switch ($childNode->localName) {
183 45
                case 'include':
184 45
                case 'import':
185 45
                    $functions[] = $this->loadImport($schema, $childNode);
186 45
                    break;
187
                case 'element':
188 45
                    $functions[] = $this->loadElementDef($schema, $childNode);
189
                    break;
190
                case 'attribute':
191 45
                    $functions[] = $this->loadAttributeDef($schema, $childNode);
192
                    break;
193 45
                case 'attributeGroup':
194 45
                    $functions[] = $this->loadAttributeGroup($schema, $childNode);
195
                    break;
196 45
                case 'group':
197
                    $functions[] = $this->loadGroup($schema, $childNode);
198 45
                    break;
199 45
                case 'complexType':
200 45
                    $functions[] = $this->loadComplexType($schema, $childNode);
201 45
                    break;
202 45
                case 'simpleType':
203 45
                    $functions[] = $this->loadSimpleType($schema, $childNode);
204
                    break;
205 45
            }
206 45
        }
207
208 45
        return $functions;
209 45
    }
210 45
211
    private function loadElement(Schema $schema, DOMElement $node)
212 45
    {
213 3
        $element = new Element($schema, $node->getAttribute("name"));
214 3
        $element->setDoc($this->getDocumentation($node));
215 45
216 3
        $this->fillItem($element, $node);
217 3
218 45
        if ($node->hasAttribute("maxOccurs")) {
219
            $element->setMax($node->getAttribute("maxOccurs") == "unbounded" ? -1 : (int)$node->getAttribute("maxOccurs"));
220
        }
221 45
        if ($node->hasAttribute("minOccurs")) {
222
            $element->setMin((int)$node->getAttribute("minOccurs"));
223 45
        }
224 45
225
        $xp = new \DOMXPath($node->ownerDocument);
226 45
        $xp->registerNamespace('xs', 'http://www.w3.org/2001/XMLSchema');
227 45
        
228 45
        if ($xp->query('ancestor::xs:choice', $node)->length) {
229 45
            $element->setMin(0);
230 45
        }
231 45
232
        if ($node->hasAttribute("nillable")) {
233 45
            $element->setNil($node->getAttribute("nillable") == "true");
234
        }
235
        if ($node->hasAttribute("form")) {
236 45
            $element->setQualified($node->getAttribute("form") == "qualified");
237
        }
238 45
        return $element;
239 45
    }
240
241 45
    private function loadGroupRef(Group $referenced, DOMElement $node)
242 45
    {
243 45
        $ref = new GroupRef($referenced);
244 45
        $ref->setDoc($this->getDocumentation($node));
245 45
246 45 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 45
            $ref->setMax($node->getAttribute("maxOccurs") == "unbounded" ? -1 : (int)$node->getAttribute("maxOccurs"));
248
        }
249
        if ($node->hasAttribute("minOccurs")) {
250 45
            $ref->setMin((int)$node->getAttribute("minOccurs"));
251
        }
252
253
        return $ref;
254 45
    }
255
256
    private function loadElementRef(ElementDef $referenced, DOMElement $node)
257
    {
258 45
        $ref = new ElementRef($referenced);
259
        $ref->setDoc($this->getDocumentation($node));
260 45
261 45 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
            $ref->setMax($node->getAttribute("maxOccurs") == "unbounded" ? -1 : (int)$node->getAttribute("maxOccurs"));
263 45
        }
264
        if ($node->hasAttribute("minOccurs")) {
265
            $ref->setMin((int)$node->getAttribute("minOccurs"));
266 45
        }
267
        if ($node->hasAttribute("nillable")) {
268
            $ref->setNil($node->getAttribute("nillable") == "true");
269 45
        }
270
        if ($node->hasAttribute("form")) {
271
            $ref->setQualified($node->getAttribute("form") == "qualified");
272 45
        }
273
274
        return $ref;
275 45
    }
276
277 45
278
    private function loadAttributeRef(AttributeDef $referencedAttribiute, DOMElement $node)
279 45
    {
280
        $attribute = new AttributeRef($referencedAttribiute);
281 45
        $attribute->setDoc($this->getDocumentation($node));
282 45
283 45
        if ($node->hasAttribute("nillable")) {
284 45
            $attribute->setNil($node->getAttribute("nillable") == "true");
285 45
        }
286 45
        if ($node->hasAttribute("form")) {
287 45
            $attribute->setQualified($node->getAttribute("form") == "qualified");
288 45
        }
289 45
        if ($node->hasAttribute("use")) {
290 45
            $attribute->setUse($node->getAttribute("use"));
291 45
        }
292 45
        return $attribute;
293
    }
294 45
295 45
    private function loadSequence(ElementContainer $elementContainer, DOMElement $node, $max = null)
296 45
    {
297 45
        $max = $max || $node->getAttribute("maxOccurs") == "unbounded" || $node->getAttribute("maxOccurs") > 1 ? 2 : null;
298 45
299 45
        foreach ($node->childNodes as $childNode) {
300 45
301
            switch ($childNode->localName) {
302 45
                case 'choice':
303 45
                case 'sequence':
304 45
                case 'all':
305 45
                    $this->loadSequence($elementContainer, $childNode, $max);
306 45
                    break;
307 45
                case 'element':
308
                    if ($childNode->hasAttribute("ref")) {
309 45
                        $referencedElement = $this->findSomething('findElement', $elementContainer->getSchema(), $node, $childNode->getAttribute("ref"));
310
                        $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 309 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...
311 45
                    } else {
312 45
                        $element = $this->loadElement($elementContainer->getSchema(), $childNode);
313
                    }
314 45
                    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...
315
                        $element->setMax($max);
316
                    }
317 45
                    $elementContainer->addElement($element);
318
                    break;
319 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...
320
                    $referencedGroup = $this->findSomething('findGroup', $elementContainer->getSchema(), $node, $childNode->getAttribute("ref"));
321 45
322
                    $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 320 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...
323
                    $elementContainer->addElement($group);
324 45
                    break;
325 45
            }
326 45
        }
327 45
    }
328 45
329 45
    private function loadGroup(Schema $schema, DOMElement $node)
330 45
    {
331 45
        $group = new Group($schema, $node->getAttribute("name"));
332 45
        $group->setDoc($this->getDocumentation($node));
333 45
334 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...
335
            $group->setMax($node->getAttribute("maxOccurs") == "unbounded" ? -1 : (int)$node->getAttribute("maxOccurs"));
336 45
        }
337
        if ($node->hasAttribute("minOccurs")) {
338 45
            $group->setMin((int)$node->getAttribute("minOccurs"));
339
        }
340 45
341 45
        $schema->addGroup($group);
342 2
343 2
        return function () use ($group, $node) {
344
            foreach ($node->childNodes as $childNode) {
345 45
                switch ($childNode->localName) {
346
                    case 'sequence':
347 45
                    case 'choice':
348
                    case 'all':
349 45
                        $this->loadSequence($group, $childNode);
350 45
                        break;
351 45
                }
352 45
            }
353
        };
354
    }
355
356 45
    private function loadComplexType(Schema $schema, DOMElement $node, $callback = null)
357
    {
358 45
        $isSimple = false;
359 45
360 45
        foreach ($node->childNodes as $childNode) {
361 45
            if ($childNode->localName === "simpleContent") {
362 45
                $isSimple = true;
363 45
                break;
364 45
            }
365 45
        }
366 45
367 45
        $type = $isSimple ? new ComplexTypeSimpleContent($schema, $node->getAttribute("name")) : new ComplexType($schema, $node->getAttribute("name"));
368 45
369 45
        $type->setDoc($this->getDocumentation($node));
370 45
        if ($node->getAttribute("name")) {
371
            $schema->addType($type);
372
        }
373 45
374 45
        return function () use ($type, $node, $schema, $callback) {
375 45
376 1
            $this->fillTypeNode($type, $node);
377 1
378 1
            foreach ($node->childNodes as $childNode) {
379 1
                switch ($childNode->localName) {
380 45
                    case 'sequence':
381 2
                    case 'choice':
382 2
                    case 'all':
383 2
                        $this->loadSequence($type, $childNode);
0 ignored issues
show
Bug introduced by
It seems like $type defined by $isSimple ? new \GoetasW...->getAttribute('name')) on line 367 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...
384 45
                        break;
385 45
                    case 'attribute':
386
                        if ($childNode->hasAttribute("ref")) {
387 45
                            $referencedAttribute = $this->findSomething('findAttribute', $schema, $node, $childNode->getAttribute("ref"));
388 45
                            $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 387 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...
389 45
                        } else {
390 45
                            $attribute = $this->loadAttribute($schema, $childNode);
391
                        }
392
393 45
                        $type->addAttribute($attribute);
394
                        break;
395 45 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...
396 45
                        $referencedGroup = $this->findSomething('findGroup', $schema, $node, $childNode->getAttribute("ref"));
397 45
                        $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 396 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...
398 45
                        $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...
399 45
                        break;
400 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...
401
                        $attribute = $this->findSomething('findAttributeGroup', $schema, $node, $childNode->getAttribute("ref"));
402 45
                        $type->addAttribute($attribute);
0 ignored issues
show
Bug introduced by
It seems like $attribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 401 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...
403
                        break;
404 45
                }
405 45
            }
406 45
407 45
            if ($callback) {
408 45
                call_user_func($callback, $type);
409 45
            }
410 45
        };
411 45
    }
412 45
413 45
    private function loadSimpleType(Schema $schema, DOMElement $node, $callback = null)
414
    {
415 45
        $type = new SimpleType($schema, $node->getAttribute("name"));
416 45
        $type->setDoc($this->getDocumentation($node));
417 45
        if ($node->getAttribute("name")) {
418 45
            $schema->addType($type);
419
        }
420
421 45
        return function () use ($type, $node, $callback) {
422
            $this->fillTypeNode($type, $node);
423 45
424 45
            foreach ($node->childNodes as $childNode) {
425 45
                switch ($childNode->localName) {
426
                    case 'union':
427 45
                        $this->loadUnion($type, $childNode);
428 45
                        break;
429
                    case 'list':
430 45
                        $this->loadList($type, $childNode);
431 45
                        break;
432 45
                }
433 45
            }
434 45
435 45
            if ($callback) {
436 45
                call_user_func($callback, $type);
437
            }
438 45
        };
439
    }
440 45
441
    private function loadList(SimpleType $type, DOMElement $node)
442 45
    {
443 45 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...
444 45
            $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...
445 45
        } else {
446 45
            $addCallback = function ($list) use ($type) {
447 45
                $type->setList($list);
448
            };
449 45
450 45
            foreach ($node->childNodes as $childNode) {
451
                switch ($childNode->localName) {
452 45
                    case 'simpleType':
453 45
                        call_user_func($this->loadSimpleType($type->getSchema(), $childNode, $addCallback));
454 45
                        break;
455 45
                }
456 45
            }
457 45
        }
458 45
    }
459 45
460
    private function loadUnion(SimpleType $type, DOMElement $node)
461 45
    {
462
        if ($node->hasAttribute("memberTypes")) {
463
            $types = preg_split('/\s+/', $node->getAttribute("memberTypes"));
464 45
            foreach ($types as $typeName) {
465 45
                $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...
466 45
            }
467
        }
468 45
        $addCallback = function ($unType) use ($type) {
469 45
            $type->addUnion($unType);
470 45
        };
471 45
472 45
        foreach ($node->childNodes as $childNode) {
473 45
            switch ($childNode->localName) {
474 45
                case 'simpleType':
475 45
                    call_user_func($this->loadSimpleType($type->getSchema(), $childNode, $addCallback));
476 45
                    break;
477 45
            }
478 45
        }
479 45
    }
480 45
481 45
    private function fillTypeNode(Type $type, DOMElement $node, $checkAbstract = true)
482 45
    {
483
484 45
        if ($checkAbstract) {
485
            $type->setAbstract($node->getAttribute("abstract") === "true" || $node->getAttribute("abstract") === "1");
486 45
        }
487 45
488
        foreach ($node->childNodes as $childNode) {
489 45
            switch ($childNode->localName) {
490 45
                case 'restriction':
491 45
                    $this->loadRestriction($type, $childNode);
492 45
                    break;
493
                case 'extension':
494 45
                    $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...
495 45
                    break;
496 45
                case 'simpleContent':
497 45
                case 'complexContent':
498 45
                    $this->fillTypeNode($type, $childNode, false);
499 45
                    break;
500 45
            }
501 45
        }
502 45
    }
503 45
504 45
    private function loadExtension(BaseComplexType $type, DOMElement $node)
505 45
    {
506
        $extension = new Extension();
507 45
        $type->setExtension($extension);
508 45
509 45
        if ($node->hasAttribute("base")) {
510 45
            $parent = $this->findSomething('findType', $type->getSchema(), $node, $node->getAttribute("base"));
511 45
            $extension->setBase($parent);
0 ignored issues
show
Bug introduced by
It seems like $parent defined by $this->findSomething('fi...->getAttribute('base')) on line 510 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...
512 45
        }
513 45
514 45
        foreach ($node->childNodes as $childNode) {
515 45
            switch ($childNode->localName) {
516
                case 'sequence':
517 45
                case 'choice':
518
                case 'all':
519 45
                    $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...
520 45
                    break;
521 45 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...
522 45
                    if ($childNode->hasAttribute("ref")) {
523 45
                        $attribute = $this->findSomething('findAttribute', $type->getSchema(), $node, $childNode->getAttribute("ref"));
524 45
                    } else {
525
                        $attribute = $this->loadAttribute($type->getSchema(), $childNode);
526 45
                    }
527 45
                    $type->addAttribute($attribute);
0 ignored issues
show
Bug introduced by
It seems like $attribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 523 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...
528
                    break;
529 45
                case 'attributeGroup':
530 45
                    $attribute = $this->findSomething('findAttributeGroup', $type->getSchema(), $node, $childNode->getAttribute("ref"));
531 45
                    $type->addAttribute($attribute);
0 ignored issues
show
Bug introduced by
It seems like $attribute defined by $this->findSomething('fi...e->getAttribute('ref')) on line 530 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...
532 45
                    break;
533 45
            }
534 45
        }
535 45
    }
536
537 45
    private function loadRestriction(Type $type, DOMElement $node)
538 45
    {
539
        $restriction = new Restriction();
540 45
        $type->setRestriction($restriction);
541 45 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...
542 45
            $restrictedType = $this->findSomething('findType', $type->getSchema(), $node, $node->getAttribute("base"));
543 45
            $restriction->setBase($restrictedType);
0 ignored issues
show
Bug introduced by
It seems like $restrictedType defined by $this->findSomething('fi...->getAttribute('base')) on line 542 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...
544 45
        } else {
545 45
            $addCallback = function ($restType) use ($restriction) {
546 45
                $restriction->setBase($restType);
547 45
            };
548 45
549 45
            foreach ($node->childNodes as $childNode) {
550 45
                switch ($childNode->localName) {
551
                    case 'simpleType':
552 45
                        call_user_func($this->loadSimpleType($type->getSchema(), $childNode, $addCallback));
553 45
                        break;
554
                }
555 45
            }
556 45
        }
557 45
        foreach ($node->childNodes as $childNode) {
558 45
            if (in_array($childNode->localName,
559 45
                [
560 45
                    'enumeration',
561
                    'pattern',
562 45
                    'length',
563
                    'minLength',
564 45
                    'maxLength',
565 45
                    'minInclusive',
566 45
                    'maxInclusive',
567 45
                    'minExclusive',
568 45
                    'maxExclusive',
569 45
                    'fractionDigits',
570
                    'totalDigits',
571 45
                    'whiteSpace'
572
                ], true)) {
573 45
                $restriction->addCheck($childNode->localName,
574 45
                    [
575
                        'value' => $childNode->getAttribute("value"),
576 45
                        'doc' => $this->getDocumentation($childNode)
577
                    ]);
578
            }
579
        }
580
    }
581
582
    private static function splitParts(DOMElement $node, $typeName)
583
    {
584
        $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...
585
        $prefix = null;
586
        $name = $typeName;
587
        if (strpos($typeName, ':') !== false) {
588 45
            list ($prefix, $name) = explode(':', $typeName);
589
        }
590 45
591
        $namespace = $node->lookupNamespaceURI($prefix ?: null);
592 45
        return array(
593
            $name,
594
            $namespace,
595 45
            $prefix
596
        );
597
    }
598
599
    /**
600
     *
601 45
     * @param string $finder
602
     * @param Schema $schema
603 45
     * @param DOMElement $node
604 45
     * @param string $typeName
605
     * @throws TypeException
606
     * @return ElementItem|Group|AttributeItem|AttribiuteGroup|Type
607 45
     */
608 45
    private function findSomething($finder, Schema $schema, DOMElement $node, $typeName)
609
    {
610
        list ($name, $namespace) = self::splitParts($node, $typeName);
611 45
612
        $namespace = $namespace ?: $schema->getTargetNamespace();
613 45
614 45
        try {
615 45
            return $schema->$finder($name, $namespace);
616 45
        } catch (TypeNotFoundException $e) {
617 45
            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);
618 45
        }
619 45
    }
620 45
621 45 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...
622
    {
623 45
        $element = new ElementDef($schema, $node->getAttribute("name"));
624
        $schema->addElement($element);
625 45
626 45
        return function () use ($element, $node) {
627 45
            $this->fillItem($element, $node);
628 45
        };
629 45
    }
630 45
631 45
    private function fillItem(Item $element, DOMElement $node)
632 45
    {
633 45
        $localType = null;
634 45
        foreach ($node->childNodes as $childNode) {
635 45
            switch ($childNode->localName) {
636
                case 'complexType':
637 45
                case 'simpleType':
638 45
                    $localType = $childNode;
639 45
                    break 2;
640 45
            }
641
        }
642
643 45
        if ($localType) {
644
            $addCallback = function ($type) use ($element) {
645 45
                $element->setType($type);
646
            };
647 45
            switch ($localType->localName) {
648
                case 'complexType':
649 45
                    call_user_func($this->loadComplexType($element->getSchema(), $localType, $addCallback));
650 45
                    break;
651 45
                case 'simpleType':
652 45
                    call_user_func($this->loadSimpleType($element->getSchema(), $localType, $addCallback));
653 45
                    break;
654 45
            }
655
        } else {
656 45
657
            if ($node->getAttribute("type")) {
658
                $type = $this->findSomething('findType', $element->getSchema(), $node, $node->getAttribute("type"));
659 45
            } else {
660 3
                $type = $this->findSomething('findType', $element->getSchema(), $node, ($node->lookupPrefix(self::XSD_NS) . ":anyType"));
661 3
            }
662 2
663
            $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...
664 2
        }
665 1
    }
666
667
    private function loadImport(Schema $schema, DOMElement $node)
668
    {
669
        $base = urldecode($node->ownerDocument->documentURI);
670
        $schemaLocation = $node->getAttribute("schemaLocation");
671 1
        if (!$schemaLocation && $node->hasAttribute("namespace") && isset($this->knownNamespaceSchemaLocations[$node->getAttribute("namespace")])) {
672 1
            $schemaLocation = $this->knownNamespaceSchemaLocations[$node->getAttribute("namespace")];
673 1
        }
674
        $file = UrlUtils::resolveRelativeUrl($base, $schemaLocation);
675
        if ($node->hasAttribute("namespace")
676
            && isset(self::$globalSchemaInfo[$node->getAttribute("namespace")])
677
            && isset($this->loadedFiles[self::$globalSchemaInfo[$node->getAttribute("namespace")]])
678 1
        ) {
679
680 1
            $schema->addSchema($this->loadedFiles[self::$globalSchemaInfo[$node->getAttribute("namespace")]]);
681
682 1
            return function () {
683
            };
684
        } elseif ($node->hasAttribute("namespace")
685
            && isset($this->loadedFiles[$this->getNamespaceSpecificFileIndex($file, $node->getAttribute("namespace"))])) {
686
            $schema->addSchema($this->loadedFiles[$this->getNamespaceSpecificFileIndex($file, $node->getAttribute("namespace"))]);
687 1
            return function () {
688 1
            };
689 1
        } elseif (isset($this->loadedFiles[$file])) {
690 1
            $schema->addSchema($this->loadedFiles[$file]);
691 1
            return function () {
692
            };
693
        }
694
695
        if (!$node->getAttribute("namespace")) {
696
            $this->loadedFiles[$file] = $newSchema = $schema;
697
        } else {
698
            $this->loadedFiles[$file] = $newSchema = new Schema();
699
            $newSchema->addSchema($this->getGlobalSchema());
700 45
        }
701
702 45
        $xml = $this->getDOM(isset($this->knownLocationSchemas[$file]) ? $this->knownLocationSchemas[$file] : $file);
703 45
704 45
        $callbacks = $this->schemaNode($newSchema, $xml->documentElement, $schema);
705 45
706 45
        if ($node->getAttribute("namespace")) {
707 45
            $schema->addSchema($newSchema);
708 45
        }
709 45
710 45
711 45
        return function () use ($callbacks) {
712 45
            foreach ($callbacks as $callback) {
713
                call_user_func($callback);
714 45
            }
715 45
        };
716
    }
717 45
718 45
    private $globalSchema;
719
720 45
    /**
721 45
     *
722 45
     * @return Schema
723 45
     */
724 45
    public function getGlobalSchema()
725
    {
726
        if (!$this->globalSchema) {
727
            $callbacks = array();
728
            $globalSchemas = array();
729
            foreach (self::$globalSchemaInfo as $namespace => $uri) {
730
                $this->loadedFiles[$uri] = $globalSchemas [$namespace] = $schema = new Schema();
731
                if ($namespace === self::XSD_NS) {
732
                    $this->globalSchema = $schema;
733 45
                }
734
                $xml = $this->getDOM($this->knownLocationSchemas[$uri]);
735 45
                $callbacks = array_merge($callbacks, $this->schemaNode($schema, $xml->documentElement));
736 45
            }
737
738 45
            $globalSchemas[self::XSD_NS]->addType(new SimpleType($globalSchemas[self::XSD_NS], "anySimpleType"));
739 45
            $globalSchemas[self::XSD_NS]->addType(new SimpleType($globalSchemas[self::XSD_NS], "anyType"));
740
741 45
            $globalSchemas[self::XML_NS]->addSchema($globalSchemas[self::XSD_NS], self::XSD_NS);
742 39
            $globalSchemas[self::XSD_NS]->addSchema($globalSchemas[self::XML_NS], self::XML_NS);
743 45
744
            foreach ($callbacks as $callback) {
745 45
                $callback();
746
            }
747
        }
748
        return $this->globalSchema;
749
    }
750
751
    /**
752
     * @param DOMNode $node
753
     * @param string  $file
754
     * 
755
     * @return Schema
756
     */
757
    public function readNode(DOMNode $node, $file = 'schema.xsd')
758
    {
759 45
        $fileKey = $node instanceof DOMElement && $node->hasAttribute('targetNamespace') ? $this->getNamespaceSpecificFileIndex($file, $node->getAttribute('targetNamespace')) : $file;
760
        $this->loadedFiles[$fileKey] = $rootSchema = new Schema();
761 45
762
        $rootSchema->addSchema($this->getGlobalSchema());
763
        $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...
764
765
        foreach ($callbacks as $callback) {
766
            call_user_func($callback);
767
        }
768
769
        return $rootSchema;
770
    }
771
772 44
    /**
773
     * It is possible that a single file contains multiple <xsd:schema/> nodes, for instance in a WSDL file.
774 44
     *
775 44
     * Each of these  <xsd:schema/> nodes typically target a specific namespace. Append the target namespace to the
776
     * file to distinguish between multiple schemas in a single file.
777
     *
778 44
     * @param string $file
779
     * @param string $targetNamespace
780 44
     *
781
     * @return string
782
     */
783
    private function getNamespaceSpecificFileIndex($file, $targetNamespace)
784
    {
785
        return $file . '#' . $targetNamespace;
786
    }
787
788 1
    /**
789
     * @param string $content
790 1
     * @param string $file
791 1
     *
792
     * @return Schema
793
     *
794
     * @throws IOException
795
     */
796
    public function readString($content, $file = 'schema.xsd')
797
    {
798
        $xml = new DOMDocument('1.0', 'UTF-8');
799
        if (!$xml->loadXML($content)) {
800
            throw new IOException("Can't load the schema");
801 45
        }
802
        $xml->documentURI = $file;
803 45
804 45
        return $this->readNode($xml->documentElement, $file);
805
    }
806
807 45
    /**
808
     * @param string $file
809
     *
810
     * @return Schema
811
     */
812
    public function readFile($file)
813
    {
814
        $xml = $this->getDOM($file);
815
        return $this->readNode($xml->documentElement, $file);
816
    }
817
818
    /**
819
     * @param string $file
820
     *
821
     * @return DOMDocument
822
     *
823
     * @throws IOException
824
     */
825
    private function getDOM($file)
826
    {
827
        $xml = new DOMDocument('1.0', 'UTF-8');
828
        if (!$xml->load($file)) {
829
            throw new IOException("Can't load the file $file");
830
        }
831
        return $xml;
832
    }
833
}
834