PhpConverter   F
last analyzed

Complexity

Total Complexity 78

Size/Duplication

Total Lines 404
Duplicated Lines 25.25 %

Coupling/Cohesion

Components 1
Dependencies 19

Test Coverage

Coverage 91.12%

Importance

Changes 0
Metric Value
wmc 78
lcom 1
cbo 19
dl 102
loc 404
ccs 236
cts 259
cp 0.9112
rs 2.16
c 0
b 0
f 0

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 20 1
A convert() 9 9 2
A getTypes() 0 14 4
B navigate() 20 20 6
A visitTypeBase() 0 14 4
A visitGroup() 11 11 3
A visitAttributeGroup() 8 11 3
A visitElementDef() 3 25 4
A findPHPName() 3 30 4
B visitType() 13 37 11
A visitTypeAnonymous() 0 17 3
A visitComplexType() 12 12 3
B visitSimpleType() 0 29 9
A handleClassExtension() 0 14 2
A visitBaseComplexType() 8 20 5
A visitAttribute() 0 20 4
B visitElement() 10 41 7
A findPHPClass() 5 13 3

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 PhpConverter 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 PhpConverter, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace Goetas\Xsd\XsdToPhp\Php;
3
4
use Exception;
5
use Goetas\Xsd\XsdToPhp\AbstractConverter;
6
use Goetas\Xsd\XsdToPhp\Naming\NamingStrategy;
7
use Goetas\Xsd\XsdToPhp\Php\Structure\PHPArg;
8
use Goetas\Xsd\XsdToPhp\Php\Structure\PHPClass;
9
use Goetas\Xsd\XsdToPhp\Php\Structure\PHPClassOf;
10
use Goetas\Xsd\XsdToPhp\Php\Structure\PHPProperty;
11
use GoetasWebservices\XML\XSDReader\Schema\Attribute\AttributeItem;
12
use GoetasWebservices\XML\XSDReader\Schema\Attribute\Group as AttributeGroup;
13
use GoetasWebservices\XML\XSDReader\Schema\Element\Element;
14
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementDef;
15
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementRef;
16
use GoetasWebservices\XML\XSDReader\Schema\Element\ElementSingle;
17
use GoetasWebservices\XML\XSDReader\Schema\Element\Group;
18
use GoetasWebservices\XML\XSDReader\Schema\Item;
19
use GoetasWebservices\XML\XSDReader\Schema\Schema;
20
use GoetasWebservices\XML\XSDReader\Schema\Type\BaseComplexType;
21
use GoetasWebservices\XML\XSDReader\Schema\Type\ComplexType;
22
use GoetasWebservices\XML\XSDReader\Schema\Type\SimpleType;
23
use GoetasWebservices\XML\XSDReader\Schema\Type\Type;
24
25
class PhpConverter extends AbstractConverter
26
{
27
28 31
    public function __construct(NamingStrategy $namingStrategy)
29
    {
30 31
        parent::__construct($namingStrategy);
31
32
        $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...
33 3
            return "DateTime";
34 31
        });
35
        $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...
36
            return "DateTime";
37 31
        });
38
        $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...
39
            return "DateTime";
40 31
        });
41
        $this->addAliasMap("http://www.w3.org/2001/XMLSchema", "anySimpleType", 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...
42
            return "mixed";
43 31
        });
44
        $this->addAliasMap("http://www.w3.org/2001/XMLSchema", "anyType", 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...
45 2
            return "mixed";
46 31
        });
47 31
    }
48
49
    private $classes = [];
50
51 31 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...
52
    {
53 31
        $visited = array();
54 31
        $this->classes = array();
55 31
        foreach ($schemas as $schema) {
56 31
            $this->navigate($schema, $visited);
57 31
        }
58 31
        return $this->getTypes();
59
    }
60
61
    /**
62
     *
63
     * @return PHPClass[]
64
     */
65
    private function getTypes()
66
    {
67 31
        uasort($this->classes, function ($a, $b) {
68 20
            return strcmp($a["class"]->getFullName(), $b["class"]->getFullName());
69 31
        });
70 31
        $ret = array();
71 31
        foreach ($this->classes as $classData) {
72 30
            if (!isset($classData["skip"]) || !$classData["skip"]) {
73 30
                $ret[$classData["class"]->getFullName()] = $classData["class"];
74 30
            }
75 31
        }
76
77 31
        return $ret;
78
    }
79
80 31 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...
81
    {
82 31
        if (isset($visited[spl_object_hash($schema)])) {
83
            return;
84
        }
85 31
        $visited[spl_object_hash($schema)] = true;
86
87 31
        foreach ($schema->getTypes() as $type) {
88 17
            $this->visitType($type);
89 31
        }
90 31
        foreach ($schema->getElements() as $element) {
91 15
            $this->visitElementDef($element);
92 31
        }
93
94 31
        foreach ($schema->getSchemas() as $schildSchema) {
95 31
            if (!in_array($schildSchema->getTargetNamespace(), $this->baseSchemas, true)) {
96 1
                $this->navigate($schildSchema, $visited);
97 1
            }
98 31
        }
99 31
    }
100
101 25
    private function visitTypeBase(PHPClass $class, Type $type)
102
    {
103 25
        $class->setAbstract($type->isAbstract());
104
105 25
        if ($type instanceof SimpleType) {
106 9
            $this->visitSimpleType($class, $type);
107 9
        }
108 25
        if ($type instanceof BaseComplexType) {
109 20
            $this->visitBaseComplexType($class, $type);
110 20
        }
111 25
        if ($type instanceof ComplexType) {
112 18
            $this->visitComplexType($class, $type);
113 18
        }
114 25
    }
115
116 3 View Code Duplication
    private function visitGroup(PHPClass $class, Schema $schema, Group $group)
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...
117
    {
118 3
        foreach ($group->getElements() as $childGroup) {
119 3
            if ($childGroup instanceof Group) {
120 1
                $this->visitGroup($class, $schema, $childGroup);
121 1
            } else {
122 3
                $property = $this->visitElement($class, $schema, $childGroup);
123 3
                $class->addProperty($property);
124
            }
125 3
        }
126 3
    }
127
128 1
    private function visitAttributeGroup(PHPClass $class, Schema $schema, AttributeGroup $att)
129
    {
130 1 View Code Duplication
        foreach ($att->getAttributes() as $childAttr) {
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...
131 1
            if ($childAttr instanceof AttributeGroup) {
132 1
                $this->visitAttributeGroup($class, $schema, $childAttr);
133 1
            } else {
134 1
                $property = $this->visitAttribute($class, $schema, $childAttr);
135 1
                $class->addProperty($property);
136
            }
137 1
        }
138 1
    }
139
140 15
    private function visitElementDef(ElementDef $element)
141
    {
142 15
        if (!isset($this->classes[spl_object_hash($element)])) {
143 15
            $schema = $element->getSchema();
144
145 15
            $class = new PHPClass();
146 15
            $class->setDoc($element->getDoc());
147 15
            $class->setName($this->getNamingStrategy()->getItemName($element));
148 15
            $class->setDoc($element->getDoc());
149
150 15 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...
151
                throw new Exception(sprintf("Can't find a PHP namespace to '%s' namespace", $schema->getTargetNamespace()));
152
            }
153 15
            $class->setNamespace($this->namespaces[$schema->getTargetNamespace()]);
154
155 15
            $this->classes[spl_object_hash($element)]["class"] = $class;
156
157 15
            if (!$element->getType()->getName()) {
158 8
                $this->visitTypeBase($class, $element->getType());
159 8
            } else {
160 8
                $this->handleClassExtension($class, $element->getType());
161
            }
162 15
        }
163 15
        return $this->classes[spl_object_hash($element)]["class"];
164
    }
165
166 17
    private function findPHPName(Type $type)
167
    {
168 17
        $schema = $type->getSchema();
169
170 17
        if ($className = $this->getTypeAlias($type)) {
171
172
            if (($pos = strrpos($className, '\\')) !== false) {
173
                return [
174
                    substr($className, $pos + 1),
175
                    substr($className, 0, $pos)
176
                ];
177
            } else {
178
                return [
179
                    $className,
180
                    null
181
                ];
182
            }
183
        }
184
185 17
        $name = $this->getNamingStrategy()->getTypeName($type);
186
187 17 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...
188
            throw new Exception(sprintf("Can't find a PHP namespace to '%s' namespace", $schema->getTargetNamespace()));
189
        }
190 17
        $ns = $this->namespaces[$schema->getTargetNamespace()];
191
        return [
192 17
            $name,
193
            $ns
194 17
        ];
195
    }
196
197
    /**
198
     *
199
     * @param Type $type
200
     * @param boolean $force
201
     * @return \Goetas\Xsd\XsdToPhp\Php\Structure\PHPClass
202
     */
203 19
    private function visitType(Type $type, $force = false)
204
    {
205 19
        if (!isset($this->classes[spl_object_hash($type)])) {
206
207 19
            $this->classes[spl_object_hash($type)]["class"] = $class = new PHPClass();
208
209 19
            if ($alias = $this->getTypeAlias($type)) {
210 18
                $class->setName($alias);
211 18
                $this->classes[spl_object_hash($type)]["skip"] = true;
212 18
                return $class;
213
            }
214
215 17
            list ($name, $ns) = $this->findPHPName($type);
216 17
            $class->setName($name);
217 17
            $class->setNamespace($ns);
218
219 17
            $class->setDoc($type->getDoc() . PHP_EOL . "XSD Type: " . ($type->getName() ?: 'anonymous'));
220
221 17
            $this->visitTypeBase($class, $type);
222
223 17 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...
224 3
                $this->classes[spl_object_hash($type)]["skip"] = true;
225 3
                return $class;
226
            }
227 17 View Code Duplication
            if (($this->isArrayType($type) || $this->isArrayNestedElement($type)) && !$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...
228 4
                $this->classes[spl_object_hash($type)]["skip"] = true;
229 4
                return $class;
230
            }
231
232 17
            $this->classes[spl_object_hash($type)]["skip"] = !!$this->getTypeAlias($type);
233 17 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...
234 8
            if (!($type instanceof SimpleType) && !$this->getTypeAlias($type)) {
235 3
                $this->classes[spl_object_hash($type)]["skip"] = false;
236 3
            }
237 8
        }
238 17
        return $this->classes[spl_object_hash($type)]["class"];
239
    }
240
241
    /**
242
     * @param Type $type
243
     * @param string $name
244
     * @param PHPClass $parentClass
245
     * @return \Goetas\Xsd\XsdToPhp\Php\Structure\PHPClass
246
     */
247 3
    private function visitTypeAnonymous(Type $type, $name, PHPClass $parentClass)
248
    {
249 3
        if (!isset($this->classes[spl_object_hash($type)])) {
250 3
            $this->classes[spl_object_hash($type)]["class"] = $class = new PHPClass();
251 3
            $class->setName($this->getNamingStrategy()->getAnonymousTypeName($type, $name));
252
253 3
            $class->setNamespace($parentClass->getNamespace() . "\\" . $parentClass->getName());
254 3
            $class->setDoc($type->getDoc());
255
256 3
            $this->visitTypeBase($class, $type);
257
258 3
            if ($type instanceof SimpleType) {
259 2
                $this->classes[spl_object_hash($type)]["skip"] = true;
260 2
            }
261 3
        }
262 3
        return $this->classes[spl_object_hash($type)]["class"];
263
    }
264
265 18 View Code Duplication
    private function visitComplexType(PHPClass $class, ComplexType $type)
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...
266
    {
267 18
        $schema = $type->getSchema();
268 18
        foreach ($type->getElements() as $element) {
269 18
            if ($element instanceof Group) {
270 3
                $this->visitGroup($class, $schema, $element);
271 3
            } else {
272 17
                $property = $this->visitElement($class, $schema, $element);
273 17
                $class->addProperty($property);
274
            }
275 18
        }
276 18
    }
277
278 9
    private function visitSimpleType(PHPClass $class, SimpleType $type)
279
    {
280 9
        if ($restriction = $type->getRestriction()) {
281 8
            $parent = $restriction->getBase();
282
283 8
            if ($parent instanceof Type) {
284 8
                $this->handleClassExtension($class, $parent);
285 8
            }
286
287 8
            foreach ($restriction->getChecks() as $typeCheck => $checks) {
288 1
                foreach ($checks as $check) {
289 1
                    $class->addCheck('__value', $typeCheck, $check);
290 1
                }
291 8
            }
292 9
        } elseif ($unions = $type->getUnions()) {
293 1
            $types = array();
294 1
            foreach ($unions as $i => $unon) {
295 1
                if (!$unon->getName()) {
296 1
                    $types[] = $this->visitTypeAnonymous($unon, $type->getName() . $i, $class);
297 1
                } else {
298 1
                    $types[] = $this->visitType($unon);
299
                }
300 1
            }
301
302 1
            if ($candidato = reset($types)) {
303 1
                $class->setExtends($candidato);
304 1
            }
305 1
        }
306 9
    }
307
308 17
    private function handleClassExtension(PHPClass $class, Type $type)
309
    {
310
311 17
        if ($alias = $this->getTypeAlias($type)) {
312 16
            $c = PHPClass::createFromFQCN($alias);
313 16
            $val = new PHPProperty('__value');
314 16
            $val->setType($c);
315 16
            $c->addProperty($val);
316 16
            $class->setExtends($c);
317 16
        } else {
318 2
            $extension = $this->visitType($type, true);
319 2
            $class->setExtends($extension);
320
        }
321 17
    }
322
323 20
    private function visitBaseComplexType(PHPClass $class, BaseComplexType $type)
324
    {
325 20
        $parent = $type->getParent();
326 20
        if ($parent) {
327 4
            $parentType = $parent->getBase();
328 4
            if ($parentType instanceof Type) {
329 4
                $this->handleClassExtension($class, $parentType);
330 4
            }
331 4
        }
332 20
        $schema = $type->getSchema();
333
334 20 View Code Duplication
        foreach ($type->getAttributes() as $attr) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
335 7
            if ($attr instanceof AttributeGroup) {
336 1
                $this->visitAttributeGroup($class, $schema, $attr);
337 1
            } else {
338 7
                $property = $this->visitAttribute($class, $schema, $attr);
339 7
                $class->addProperty($property);
340
            }
341 20
        }
342 20
    }
343
344 7
    private function visitAttribute(PHPClass $class, Schema $schema, AttributeItem $attribute, $arrayize = true)
0 ignored issues
show
Unused Code introduced by
The parameter $schema 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...
345
    {
346 7
        $property = new PHPProperty();
347 7
        $property->setName($this->getNamingStrategy()->getPropertyName($attribute));
348
349 7
        if ($arrayize && $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...
350
            if ($attribute->getType()->getName()) {
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...
351
                $arg = new PHPArg($this->getNamingStrategy()->getPropertyName($attribute));
352
                $arg->setType($this->visitType($itemOfArray));
353
                $property->setType(new PHPClassOf($arg));
354
            } else {
355
                $property->setType($this->visitTypeAnonymous($attribute->getType(), $attribute->getName(), $class));
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...
356
            }
357
        } else {
358 7
            $property->setType($this->findPHPClass($class, $attribute, true));
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...
359
        }
360
361 7
        $property->setDoc($attribute->getDoc());
362 7
        return $property;
363
    }
364
365
    /**
366
     *
367
     * @param PHPClass $class
368
     * @param Schema $schema
369
     * @param Element $element
370
     * @param boolean $arrayize
371
     * @return \Goetas\Xsd\XsdToPhp\Structure\PHPProperty
372
     */
373 18
    private function visitElement(PHPClass $class, Schema $schema, ElementSingle $element, $arrayize = true)
374
    {
375 18
        $property = new PHPProperty();
376 18
        $property->setName($this->getNamingStrategy()->getPropertyName($element));
377 18
        $property->setDoc($element->getDoc());
378
379 18
        $t = $element->getType();
380
381 18
        if ($arrayize) {
382 18
            if ($itemOfArray = $this->isArrayType($t)) {
383 1 View Code Duplication
                if (!$itemOfArray->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...
384
                    $classType = $this->visitTypeAnonymous($itemOfArray, $element->getName(), $class);
385
                } else {
386 1
                    $classType = $this->visitType($itemOfArray);
387
                }
388
389 1
                $arg = new PHPArg($this->getNamingStrategy()->getPropertyName($element));
390 1
                $arg->setType($classType);
391 1
                $property->setType(new PHPClassOf($arg));
392 1
                return $property;
393 17
            } elseif ($itemOfArray = $this->isArrayNestedElement($t)) {
394 4 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...
395
                    $classType = $this->visitTypeAnonymous($t, $element->getName(), $class);
396
                } else {
397 4
                    $classType = $this->visitType($t);
398
                }
399 4
                $elementProp = $this->visitElement($classType, $schema, $itemOfArray, false);
400 4
                $property->setType(new PHPClassOf($elementProp));
401 4
                return $property;
402 17
            } elseif ($this->isArrayElement($element)) {
403 8
                $arg = new PHPArg($this->getNamingStrategy()->getPropertyName($element));
404 8
                $arg->setType($this->findPHPClass($class, $element));
0 ignored issues
show
Documentation introduced by
$element is of type object<GoetasWebservices...\Element\ElementSingle>, 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...
405 8
                $arg->setDefault('array()');
406 8
                $property->setType(new PHPClassOf($arg));
407 8
                return $property;
408
            }
409 12
        }
410
411 16
        $property->setType($this->findPHPClass($class, $element, true));
0 ignored issues
show
Documentation introduced by
$element is of type object<GoetasWebservices...\Element\ElementSingle>, 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...
412 16
        return $property;
413
    }
414
415 18
    private function findPHPClass(PHPClass $class, Item $node, $force = false)
416
    {
417
418 18
        if ($node instanceof ElementRef) {
419 5
            return $this->visitElementDef($node->getReferencedElement());
420
        }
421
422 17 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...
423 3
            return $this->visitTypeAnonymous($node->getType(), $node->getName(), $class);
424
        } else {
425 17
            return $this->visitType($node->getType(), $force);
426
        }
427
    }
428
}
429