Completed
Push — master ( a5882e...93cf61 )
by Asmir
11s
created

SchemaReader::fillTypeNode()   B

Complexity

Conditions 8
Paths 12

Size

Total Lines 21
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 19
cts 19
cp 1
rs 7.1428
c 0
b 0
f 0
cc 8
eloc 15
nc 12
nop 3
crap 8
1
<?php
2
3
namespace GoetasWebservices\XML\XSDReader;
4
5
use DOMDocument;
6
use DOMElement;
7
use DOMNode;
8
use GoetasWebservices\XML\XSDReader\Exception\IOException;
9
use GoetasWebservices\XML\XSDReader\Exception\TypeException;
10
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Attribute;
11
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeDef;
12
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeRef;
13
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup;
14
use GoetasWebservices\XML\XSDReader\Schema\Element\Element;
15
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer;
16
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
17
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem;
18
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef;
19
use GoetasWebservices\XML\XSDReader\Schema\Element\Group;
20
use GoetasWebservices\XML\XSDReader\Schema\Element\GroupRef;
21
use GoetasWebservices\XML\XSDReader\Schema\Exception\TypeNotFoundException;
22
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Extension;
23
use GoetasWebservices\XML\XSDReader\Schema\Inheritance\Restriction;
24
use GoetasWebservices\XML\XSDReader\Schema\Item;
25
use GoetasWebservices\XML\XSDReader\Schema\Schema;
26
use GoetasWebservices\XML\XSDReader\Schema\Type\BaseComplexType;
27
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
28
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexTypeSimpleContent;
29
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType;
30
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
31
use GoetasWebservices\XML\XSDReader\Utils\UrlUtils;
32
33
class SchemaReader
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 static $globalSchemaInfo = array(
44
        self::XML_NS => 'http://www.w3.org/2001/xml.xsd',
45
        self::XSD_NS => 'http://www.w3.org/2001/XMLSchema.xsd',
46
    );
47
48 54
    public function __construct()
49
    {
50 54
        $this->addKnownSchemaLocation('http://www.w3.org/2001/xml.xsd', __DIR__.'/Resources/xml.xsd');
51 54
        $this->addKnownSchemaLocation('http://www.w3.org/2001/XMLSchema.xsd', __DIR__.'/Resources/XMLSchema.xsd');
52 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');
53 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');
54 54
        $this->addKnownSchemaLocation('https://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd', __DIR__.'/Resources/xmldsig-core-schema.xsd');
55 54
        $this->addKnownSchemaLocation('http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd', __DIR__.'/Resources/xmldsig-core-schema.xsd');
56 54
    }
57
58 54
    public function addKnownSchemaLocation($remote, $local)
59
    {
60 54
        $this->knownLocationSchemas[$remote] = $local;
61 54
    }
62
63 45
    private function loadAttributeGroup(Schema $schema, DOMElement $node)
64
    {
65 45
        $attGroup = new AttributeGroup($schema, $node->getAttribute('name'));
66 45
        $attGroup->setDoc($this->getDocumentation($node));
67 45
        $schema->addAttributeGroup($attGroup);
68
69
        return function () use ($schema, $node, $attGroup) {
70 45
            foreach ($node->childNodes as $childNode) {
71 45
                switch ($childNode->localName) {
72 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...
73 45
                        if ($childNode->hasAttribute('ref')) {
74 45
                            $attribute = $this->findSomething('findAttribute', $schema, $node, $childNode->getAttribute('ref'));
75 45
                        } else {
76 45
                            $attribute = $this->loadAttribute($schema, $childNode);
77
                        }
78 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 74 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...
79 45
                        break;
80 45 View Code Duplication
                    case 'attributeGroup':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

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