Completed
Pull Request — master (#10)
by Davidson
02:21
created

SchemaReader::schemaNode()   C

Complexity

Conditions 12
Paths 30

Size

Total Lines 43
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 36
CRAP Score 12.0208

Importance

Changes 0
Metric Value
dl 0
loc 43
ccs 36
cts 38
cp 0.9474
rs 5.1612
c 0
b 0
f 0
cc 12
eloc 35
nc 30
nop 3
crap 12.0208

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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