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

SchemaReader::loadComplexType()   C

Complexity

Conditions 13
Paths 12

Size

Total Lines 51
Code Lines 33

Duplication

Lines 4
Ratio 7.84 %

Code Coverage

Tests 38
CRAP Score 13

Importance

Changes 0
Metric Value
dl 4
loc 51
ccs 38
cts 38
cp 1
rs 5.6547
c 0
b 0
f 0
cc 13
eloc 33
nc 12
nop 3
crap 13

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