YamlConverter   F
last analyzed

Complexity

Total Complexity 94

Size/Duplication

Total Lines 472
Duplicated Lines 13.77 %

Coupling/Cohesion

Components 1
Dependencies 18

Test Coverage

Coverage 86.17%

Importance

Changes 0
Metric Value
wmc 94
lcom 1
cbo 18
dl 65
loc 472
ccs 268
cts 311
cp 0.8617
rs 2
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 1
A convert() 9 9 2
A flattAttributes() 0 12 3
A flattElements() 0 12 3
A getTypes() 0 17 5
B navigate() 20 20 6
A visitTypeBase() 0 12 4
A visitElementDef() 0 23 4
A findPHPNamespace() 3 9 2
A findPHPName() 0 13 2
B visitType() 13 41 10
A visitTypeAnonymous() 0 19 3
A visitComplexType() 0 10 3
A visitSimpleType() 0 14 5
A visitBaseComplexType() 0 18 5
B handleClassExtension() 0 42 6
A visitAttribute() 5 33 5
B typeHasValue() 0 22 8
C visitElement() 10 70 12
A findPHPClass() 5 22 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like YamlConverter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use YamlConverter, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Goetas\Xsd\XsdToPhp\Jms;
3
4
use Doctrine\Common\Inflector\Inflector;
5
use Exception;
6
use Goetas\Xsd\XsdToPhp\AbstractConverter;
7
use Goetas\Xsd\XsdToPhp\Naming\NamingStrategy;
8
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeContainer;
9
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
10
use GoetasWebservices\XML\XSDReader\Schema\Element\Element;
11
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementContainer;
12
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
13
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementItem;
14
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef;
15
use GoetasWebservices\XML\XSDReader\Schema\Item;
16
use GoetasWebservices\XML\XSDReader\Schema\Schema;
17
use GoetasWebservices\XML\XSDReader\Schema\SchemaItem;
18
use GoetasWebservices\XML\XSDReader\Schema\Type\BaseComplexType;
19
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
20
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType;
21
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
22
23
class YamlConverter extends AbstractConverter
24
{
25
26 23
    public function __construct(NamingStrategy $namingStrategy)
27
    {
28
29 23
        parent::__construct($namingStrategy);
30
31
        $this->addAliasMap("http://www.w3.org/2001/XMLSchema", "dateTime", function (Type $type) {
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
32 3
            return 'GoetasWebservices\Xsd\XsdToPhp\XMLSchema\DateTime';
33 23
        });
34
        $this->addAliasMap("http://www.w3.org/2001/XMLSchema", "time", function (Type $type) {
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
35
            return 'GoetasWebservices\Xsd\XsdToPhp\XMLSchema\Time';
36 23
        });
37
        $this->addAliasMap("http://www.w3.org/2001/XMLSchema", "date", function (Type $type) {
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
38
            return "DateTime<'Y-m-d'>";
39 23
        });
40 23
    }
41
42
    private $classes = [];
43
44 23 View Code Duplication
    public function convert(array $schemas)
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...
45
    {
46 23
        $visited = array();
47 23
        $this->classes = array();
48 23
        foreach ($schemas as $schema) {
49 23
            $this->navigate($schema, $visited);
50 23
        }
51 23
        return $this->getTypes();
52
    }
53
54 12
    private function flattAttributes(AttributeContainer $container)
55
    {
56 12
        $items = array();
57 12
        foreach ($container->getAttributes() as $attr) {
58 5
            if ($attr instanceof AttributeContainer) {
59 1
                $items = array_merge($items, $this->flattAttributes($attr));
60 1
            } else {
61 5
                $items[] = $attr;
62
            }
63 12
        }
64 12
        return $items;
65
    }
66
67 12
    private function flattElements(ElementContainer $container)
68
    {
69 12
        $items = array();
70 12
        foreach ($container->getElements() as $attr) {
71 12
            if ($attr instanceof ElementContainer) {
72 3
                $items = array_merge($items, $this->flattElements($attr));
73 3
            } else {
74 12
                $items[] = $attr;
75
            }
76 12
        }
77 12
        return $items;
78
    }
79
80
    /**
81
     *
82
     * @return PHPClass[]
83
     */
84
    public function getTypes()
85
    {
86 23
        uasort($this->classes, function ($a, $b) {
87 8
            return strcmp(key($a), key($b));
88 23
        });
89
90 23
        $ret = array();
91
92 23
        foreach ($this->classes as $definition) {
93 22
            $classname = key($definition["class"]);
94 22
            if (strpos($classname, '\\') !== false && (!isset($definition["skip"]) || !$definition["skip"])) {
95 22
                $ret[$classname] = $definition["class"];
96 22
            }
97 23
        }
98
99 23
        return $ret;
100
    }
101
102 23 View Code Duplication
    private function navigate(Schema $schema, array &$visited)
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...
103
    {
104 23
        if (isset($visited[spl_object_hash($schema)])) {
105
            return;
106
        }
107 23
        $visited[spl_object_hash($schema)] = true;
108
109 23
        foreach ($schema->getTypes() as $type) {
110 10
            $this->visitType($type);
111 23
        }
112 23
        foreach ($schema->getElements() as $element) {
113 15
            $this->visitElementDef($schema, $element);
114 23
        }
115
116 23
        foreach ($schema->getSchemas() as $schildSchema) {
117 23
            if (!in_array($schildSchema->getTargetNamespace(), $this->baseSchemas, true)) {
118 2
                $this->navigate($schildSchema, $visited);
119 2
            }
120 23
        }
121 23
    }
122
123 17
    private function visitTypeBase(&$class, &$data, Type $type, $name)
124
    {
125 17
        if ($type instanceof BaseComplexType) {
126 12
            $this->visitBaseComplexType($class, $data, $type, $name);
127 12
        }
128 17
        if ($type instanceof ComplexType) {
129 12
            $this->visitComplexType($class, $data, $type);
130 12
        }
131 17
        if ($type instanceof SimpleType) {
132 7
            $this->visitSimpleType($class, $data, $type, $name);
133 7
        }
134 17
    }
135
136 15
    private function &visitElementDef(Schema $schema, ElementDef $element)
137
    {
138 15
        if (!isset($this->classes[spl_object_hash($element)])) {
139 15
            $className = $this->findPHPNamespace($element) . "\\" . $this->getNamingStrategy()->getItemName($element);
140 15
            $class = array();
141 15
            $data = array();
142 15
            $ns = $className;
143 15
            $class[$ns] = &$data;
144 15
            $data["xml_root_name"] = $element->getName();
145
146 15
            if ($schema->getTargetNamespace()) {
147 14
                $data["xml_root_namespace"] = $schema->getTargetNamespace();
148 14
            }
149 15
            $this->classes[spl_object_hash($element)]["class"] = &$class;
150
151 15
            if (!$element->getType()->getName()) {
152 8
                $this->visitTypeBase($class, $data, $element->getType(), $element->getName());
153 8
            } else {
154 7
                $this->handleClassExtension($class, $data, $element->getType(), $element->getName());
155
            }
156 15
        }
157 15
        return $this->classes[spl_object_hash($element)]["class"];
158
    }
159
160 22
    private function findPHPNamespace(SchemaItem $item)
161
    {
162 22
        $schema = $item->getSchema();
163
164 22 View Code Duplication
        if (!isset($this->namespaces[$schema->getTargetNamespace()])) {
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...
165
            throw new Exception(sprintf("Can't find a PHP namespace to '%s' namespace", $schema->getTargetNamespace()));
166
        }
167 22
        return $this->namespaces[$schema->getTargetNamespace()];
168
    }
169
170
171 10
    private function findPHPName(Type $type)
172
    {
173 10
        $schema = $type->getSchema();
174
175 10
        if ($alias = $this->getTypeAlias($type, $schema)) {
176
            return $alias;
177
        }
178
179 10
        $ns = $this->findPHPNamespace($type);
180 10
        $name = $this->getNamingStrategy()->getTypeName($type);
181
182 10
        return $ns . "\\" . $name;
183
    }
184
185
186 10
    private function &visitType(Type $type, $force = false)
187
    {
188
189 10
        if (!isset($this->classes[spl_object_hash($type)])) {
190
191 10
            if ($alias = $this->getTypeAlias($type)) {
192
                $class = array();
193
                $class[$alias] = array();
194
195
                $this->classes[spl_object_hash($type)]["class"] = &$class;
196
                $this->classes[spl_object_hash($type)]["skip"] = true;
197
                return $class;
198
            }
199
200 10
            $className = $this->findPHPName($type);
201
202 10
            $class = array();
203 10
            $data = array();
204
205 10
            $class[$className] = &$data;
206
207 10
            $this->classes[spl_object_hash($type)]["class"] = &$class;
208
209 10
            $this->visitTypeBase($class, $data, $type, $type->getName());
210
211 10 View Code Duplication
            if ($type instanceof SimpleType) {
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...
212 1
                $this->classes[spl_object_hash($type)]["skip"] = true;
213 1
                return $class;
214
            }
215
216 10 View Code Duplication
            if (!$force && ($this->isArrayType($type) || $this->isArrayNestedElement($type))) {
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...
217 1
                $this->classes[spl_object_hash($type)]["skip"] = true;
218 1
                return $class;
219
            }
220 10 View Code Duplication
        } elseif ($force) {
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...
221 2
            if (!($type instanceof SimpleType) && !$this->getTypeAlias($type)) {
222 2
                $this->classes[spl_object_hash($type)]["skip"] = false;
223 2
            }
224 2
        }
225 10
        return $this->classes[spl_object_hash($type)]["class"];
226
    }
227
228 3
    private function &visitTypeAnonymous(Type $type, $parentName, $parentClass)
229
    {
230 3
        $class = array();
231 3
        $data = array();
232
233 3
        $name = $this->getNamingStrategy()->getAnonymousTypeName($type, $parentName);
234
235 3
        $class[key($parentClass) . "\\" . $name] = &$data;
236
237 3
        $this->visitTypeBase($class, $data, $type, $parentName);
238 3
        if ($parentName) {
239 3
            $this->classes[spl_object_hash($type)]["class"] = &$class;
240
241 3
            if ($type instanceof SimpleType) {
242 1
                $this->classes[spl_object_hash($type)]["skip"] = true;
243 1
            }
244 3
        }
245 3
        return $class;
246
    }
247
248 12
    private function visitComplexType(&$class, &$data, ComplexType $type)
249
    {
250 12
        $schema = $type->getSchema();
251 12
        if (!isset($data["properties"])) {
252
            $data["properties"] = array();
253
        }
254 12
        foreach ($this->flattElements($type) as $element) {
255 12
            $data["properties"][$this->getNamingStrategy()->getPropertyName($element)] = $this->visitElement($class, $schema, $element);
256 12
        }
257 12
    }
258
259 7
    private function visitSimpleType(&$class, &$data, SimpleType $type, $name)
260
    {
261 7
        if ($restriction = $type->getRestriction()) {
262 7
            $parent = $restriction->getBase();
263 7
            if ($parent instanceof Type) {
264 7
                $this->handleClassExtension($class, $data, $parent, $name);
265 7
            }
266 7
        } elseif ($unions = $type->getUnions()) {
267 1
            foreach ($unions as $i => $unon) {
268 1
                $this->handleClassExtension($class, $data, $unon, $name . $i);
269 1
                break;
270 1
            }
271 1
        }
272 7
    }
273
274 12
    private function visitBaseComplexType(&$class, &$data, BaseComplexType $type, $name)
275
    {
276 12
        $parent = $type->getParent();
277 12
        if ($parent) {
278 2
            $parentType = $parent->getBase();
279 2
            if ($parentType instanceof Type) {
280 2
                $this->handleClassExtension($class, $data, $parentType, $name);
281 2
            }
282 2
        }
283
284 12
        $schema = $type->getSchema();
285 12
        if (!isset($data["properties"])) {
286 12
            $data["properties"] = array();
287 12
        }
288 12
        foreach ($this->flattAttributes($type) as $attr) {
289 5
            $data["properties"][$this->getNamingStrategy()->getPropertyName($attr)] = $this->visitAttribute($class, $schema, $attr);
290 12
        }
291 12
    }
292
293 14
    private function handleClassExtension(&$class, &$data, Type $type, $parentName)
294
    {
295 14
        if ($alias = $this->getTypeAlias($type)) {
296
297
298 13
            $property = array();
299 13
            $property["expose"] = true;
300 13
            $property["xml_value"] = true;
301 13
            $property["access_type"] = "public_method";
302 13
            $property["accessor"]["getter"] = "value";
303 13
            $property["accessor"]["setter"] = "value";
304 13
            $property["type"] = $alias;
305
306 13
            $data["properties"]["__value"] = $property;
307
308
309 13
        } else {
310 2
            $extension = $this->visitType($type, true);
311
312 2
            if (isset($extension['properties']['__value']) && count($extension['properties']) === 1) {
313
                $data["properties"]["__value"] = $extension['properties']['__value'];
314
            } else {
315 2
                if ($type instanceof SimpleType) { // @todo ?? basta come controllo?
316 1
                    $property = array();
317 1
                    $property["expose"] = true;
318 1
                    $property["xml_value"] = true;
319 1
                    $property["access_type"] = "public_method";
320 1
                    $property["accessor"]["getter"] = "value";
321 1
                    $property["accessor"]["setter"] = "value";
322
323 1
                    if ($valueProp = $this->typeHasValue($type, $class, $parentName)) {
324 1
                        $property["type"] = $valueProp;
325 1
                    } else {
326 1
                        $property["type"] = key($extension);
327
                    }
328
329 1
                    $data["properties"]["__value"] = $property;
330
331 1
                }
332
            }
333
        }
334 14
    }
335
336 5
    private function visitAttribute(&$class, Schema $schema, AttributeItem $attribute)
337
    {
338 5
        $property = array();
339 5
        $property["expose"] = true;
340 5
        $property["access_type"] = "public_method";
341 5
        $property["serialized_name"] = $attribute->getName();
342
343 5
        $property["accessor"]["getter"] = "get" . Inflector::classify($attribute->getName());
344 5
        $property["accessor"]["setter"] = "set" . Inflector::classify($attribute->getName());
345
346 5
        $property["xml_attribute"] = true;
347
348 5
        if ($alias = $this->getTypeAlias($attribute)) {
349
            $property["type"] = $alias;
350
351 5
        } elseif ($itemOfArray = $this->isArrayType($attribute->getType())) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface GoetasWebservices\XML\XS...Attribute\AttributeItem as the method getType() does only exist in the following implementations of said interface: GoetasWebservices\XML\XS...ema\Attribute\Attribute, GoetasWebservices\XML\XS...\Attribute\AttributeDef, GoetasWebservices\XML\XS...\Attribute\AttributeRef.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
352
353 View Code Duplication
            if ($valueProp = $this->typeHasValue($itemOfArray, $class, 'xx')) {
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...
354
                $property["type"] = "GoetasWebservices\Xsd\XsdToPhp\Jms\SimpleListOf<" . $valueProp . ">";
355
            } else {
356
                $property["type"] = "GoetasWebservices\Xsd\XsdToPhp\Jms\SimpleListOf<" . $this->findPHPName($itemOfArray) . ">";
357
            }
358
359
            $property["xml_list"]["inline"] = false;
360
            $property["xml_list"]["entry_name"] = $itemOfArray->getName();
361
            if ($schema->getTargetNamespace()) {
362
                $property["xml_list"]["entry_namespace"] = $schema->getTargetNamespace();
363
            }
364
        } else {
365 5
            $property["type"] = $this->findPHPClass($class, $attribute);
0 ignored issues
show
Documentation introduced by
$attribute is of type object<GoetasWebservices...ttribute\AttributeItem>, but the function expects a object<GoetasWebservices...\XSDReader\Schema\Item>.

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...
366
        }
367 5
        return $property;
368
    }
369
370 3
    private function typeHasValue(Type $type, $parentClass, $name)
371
    {
372 3
        $collected = array();
0 ignored issues
show
Unused Code introduced by
$collected 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...
373
        do {
374 3
            if ($alias = $this->getTypeAlias($type)) {
375
                return $alias;
376
            } else {
377
378 3
                if ($type->getName()) {
379 1
                    $parentClass = $this->visitType($type);
380 1
                } else {
381 3
                    $parentClass = $this->visitTypeAnonymous($type, $name, $parentClass);
382
                }
383 3
                $props = reset($parentClass);
384 3
                if (isset($props['properties']['__value']) && count($props['properties']) === 1) {
385 2
                    return $props['properties']['__value']['type'];
386
                }
387
            }
388 3
        } while (method_exists($type, 'getRestriction') && $type->getRestriction() && $type = $type->getRestriction()->getBase());
389
390 3
        return false;
391
    }
392
393
    /**
394
     *
395
     * @param PHPClass $class
396
     * @param Schema $schema
397
     * @param Element $element
398
     * @param boolean $arrayize
399
     * @return \Goetas\Xsd\XsdToPhp\Structure\PHPProperty
400
     */
401 12
    private function visitElement(&$class, Schema $schema, ElementItem $element, $arrayize = true)
402
    {
403 12
        $property = array();
404 12
        $property["expose"] = true;
405 12
        $property["access_type"] = "public_method";
406 12
        $property["serialized_name"] = $element->getName();
407
408 12
        if ($element->getSchema()->getTargetNamespace()) {
409 10
            $property["xml_element"]["namespace"] = $element->getSchema()->getTargetNamespace();
410 10
        }
411
412 12
        $property["accessor"]["getter"] = "get" . Inflector::classify($element->getName());
413 12
        $property["accessor"]["setter"] = "set" . Inflector::classify($element->getName());
414 12
        $t = $element->getType();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface GoetasWebservices\XML\XS...ema\Element\ElementItem as the method getType() does only exist in the following implementations of said interface: GoetasWebservices\XML\XS...\Schema\Element\Element, GoetasWebservices\XML\XS...hema\Element\ElementDef, GoetasWebservices\XML\XS...hema\Element\ElementRef.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
415
416 12
        if ($arrayize) {
417
418 12
            if ($itemOfArray = $this->isArrayNestedElement($t)) {
419 1 View Code Duplication
                if (!$t->getName()) {
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...
420
                    $classType = $this->visitTypeAnonymous($t, $element->getName(), $class);
421
                } else {
422 1
                    $classType = $this->visitType($t);
423
                }
424
425 1
                $visited = $this->visitElement($classType, $schema, $itemOfArray, false);
426
427 1
                $property["type"] = "array<" . $visited["type"] . ">";
428 1
                $property["xml_list"]["inline"] = false;
429 1
                $property["xml_list"]["skip_when_empty"] = $element->getMin() === 0;
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface GoetasWebservices\XML\XS...ema\Element\ElementItem as the method getMin() does only exist in the following implementations of said interface: GoetasWebservices\XML\XS...\Schema\Element\Element, GoetasWebservices\XML\XS...hema\Element\ElementRef, GoetasWebservices\XML\XS...Schema\Element\GroupRef.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
430 1
                $property["xml_list"]["entry_name"] = $itemOfArray->getName();
431 1
                if ($schema->getTargetNamespace()) {
432 1
                    $property["xml_list"]["namespace"] = $schema->getTargetNamespace();
433 1
                }
434 1
                return $property;
435 12
            } elseif ($itemOfArray = $this->isArrayType($t)) {
436
437
                if (!$t->getName()) {
438
                    $visitedType = $this->visitTypeAnonymous($itemOfArray, $element->getName(), $class);
439
440 View Code Duplication
                    if ($prop = $this->typeHasValue($itemOfArray, $class, 'xx')) {
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...
441
                        $property["type"] = "array<" . $prop . ">";
442
                    } else {
443
                        $property["type"] = "array<" . key($visitedType) . ">";
444
                    }
445
                } else {
446
                    $this->visitType($itemOfArray);
447
                    $property["type"] = "array<" . $this->findPHPName($itemOfArray) . ">";
448
                }
449
450
                $property["xml_list"]["inline"] = false;
451
                $property["xml_list"]["entry_name"] = $itemOfArray->getName();
452
                if ($schema->getTargetNamespace()) {
453
                    $property["xml_list"]["namespace"] = $schema->getTargetNamespace();
454
                }
455
                return $property;
456 12
            } elseif ($this->isArrayElement($element)) {
457 4
                $property["xml_list"]["inline"] = true;
458 4
                $property["xml_list"]["entry_name"] = $element->getName();
459 4
                if ($schema->getTargetNamespace()) {
460 3
                    $property["xml_list"]["namespace"] = $schema->getTargetNamespace();
461 3
                }
462
463 4
                $property["type"] = "array<" . $this->findPHPClass($class, $element) . ">";
0 ignored issues
show
Documentation introduced by
$element is of type object<GoetasWebservices...ma\Element\ElementItem>, but the function expects a object<GoetasWebservices...\XSDReader\Schema\Item>.

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...
464 4
                return $property;
465
            }
466 11
        }
467
468 12
        $property["type"] = $this->findPHPClass($class, $element);
0 ignored issues
show
Documentation introduced by
$element is of type object<GoetasWebservices...ma\Element\ElementItem>, but the function expects a object<GoetasWebservices...\XSDReader\Schema\Item>.

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...
469 12
        return $property;
470
    }
471
472 12
    private function findPHPClass(&$class, Item $node)
473
    {
474 12
        $type = $node->getType();
475
476 12
        if ($alias = $this->getTypeAlias($node->getType())) {
477 12
            return $alias;
478
        }
479 5
        if ($node instanceof ElementRef) {
480 4
            $elementRef = $this->visitElementDef($node->getSchema(), $node->getReferencedElement());
481 4
            return key($elementRef);
482
        }
483 3
        if ($valueProp = $this->typeHasValue($type, $class, '')) {
484 2
            return $valueProp;
485
        }
486 3 View Code Duplication
        if (!$node->getType()->getName()) {
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...
487 2
            $visited = $this->visitTypeAnonymous($node->getType(), $node->getName(), $class);
488 2
        } else {
489 1
            $visited = $this->visitType($node->getType());
490
        }
491
492 3
        return key($visited);
493
    }
494
}
495