Test Failed
Push — master ( 08e051...05d5e7 )
by Kirill
02:27
created

HasInheritance::instanceOf()   B

Complexity

Conditions 9
Paths 7

Size

Total Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 9.1582

Importance

Changes 0
Metric Value
dl 0
loc 46
ccs 14
cts 16
cp 0.875
rs 7.6226
c 0
b 0
f 0
cc 9
nc 7
nop 1
crap 9.1582
1
<?php
2
/**
3
 * This file is part of Railt package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
declare(strict_types=1);
9
10
namespace Railt\Reflection\Definition\Behaviour;
11
12
use Railt\Reflection\Contracts\Definition\Behaviour\ProvidesInheritance;
13
use Railt\Reflection\Contracts\Definition\Behaviour\ProvidesInterfaces;
14
use Railt\Reflection\Contracts\Definition\Behaviour\ProvidesTypeDefinitions;
15
use Railt\Reflection\Contracts\Definition\TypeDefinition;
16
use Railt\Reflection\Type;
17
18
/**
19
 * Trait HasInheritance
20
 */
21
trait HasInheritance
22
{
23
    /**
24
     * @var string|null
25
     */
26
    protected $extends;
27
28
    /**
29
     * @var string[]|array
30
     */
31
    protected $extendedBy = [];
32
33
    /**
34
     * @param string|TypeDefinition $type
35
     * @return TypeDefinition
36
     */
37
    abstract protected function fetch($type): TypeDefinition;
38
39
    /**
40
     * @return iterable|TypeDefinition[]
41
     */
42
    public function inheritedBy(): iterable
43
    {
44
        foreach ($this->extendedBy as $parent) {
45
            yield $this->fetch($parent);
46
        }
47
    }
48
49
    /**
50
     * @return null|TypeDefinition
51
     */
52 8
    public function getInheritedParent(): ?TypeDefinition
53
    {
54 8
        return $this->extends ? $this->fetch($this->extends) : null;
55
    }
56
57
    /**
58
     * @return bool
59
     */
60
    public function hasInheritance(): bool
61
    {
62
        return $this->extends !== null;
63
    }
64
65
    /**
66
     * @param TypeDefinition|string $definition
67
     * @return ProvidesInheritance|$this
68
     */
69 25
    public function extends($definition): ProvidesInheritance
70
    {
71 25
        $this->extends = $this->nameOf($definition);
0 ignored issues
show
Bug introduced by
It seems like nameOf() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
72
73 25
        if ($definition instanceof ProvidesInheritance) {
74
            /** @var HasInheritance $definition */
75 25
            $definition->extendedBy[] = $this->getName();
0 ignored issues
show
Bug introduced by
It seems like getName() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
76
        }
77
78 25
        return $this;
79
    }
80
81
    /**
82
     * @param TypeDefinition|string $definition
83
     * @return ProvidesInheritance|$this
84
     */
85
    public function extendsBy($definition): ProvidesInheritance
86
    {
87
        /** @var HasInheritance $definition */
88
        $definition = $this->fetch($definition);
89
90
        $definition->extends($this);
91
92
        return $this;
93
    }
94
95
    /**
96
     * @param TypeDefinition|string $type
97
     * @return bool
98
     */
99 8
    public function extendsOf($type): bool
100
    {
101 8
        return $this->extends === $this->nameOf($type);
0 ignored issues
show
Bug introduced by
It seems like nameOf() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
102
    }
103
104
    /**
105
     * @param TypeDefinition|string $type
106
     * @return bool
107
     */
108 8
    public function instanceOf($type): bool
109
    {
110
        /**
111
         * @var TypeDefinition $type
112
         * @var TypeDefinition $context
113
         */
114 8
        [$type, $context] = [$this->fetch($type), $this];
0 ignored issues
show
Bug introduced by
The variable $context seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
115
116
        // Return a positive response if the child is an Any type implementation.
117 8
        if ($type::getType()->is(Type::ANY)) {
118 8
            return true;
119
        }
120
121
        // Return a positive response if the desired child is the same type from
122
        // which the search is performed.
123 8
        if ($type === $context) {
0 ignored issues
show
Bug introduced by
The variable $context seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
124 8
            return true;
125
        }
126
127
        // Return a positive response if the parent type (like Object or Interface)
128
        // can implement the desired type.
129 8
        if ($this instanceof ProvidesInterfaces && $this->isImplements($type->getName())) {
130
            return true;
131
        }
132
133
        // Return a positive response if the parent type (like Union) contains a
134
        // reference to the desired child type.
135 8
        if ($this instanceof ProvidesTypeDefinitions && $this->hasDefinition($type->getName())) {
136
            return true;
137
        }
138
139
        // Return a positive response if the parent type contains a reference
140
        // to the desired child when using inheritance.
141 8
        while ($context) {
0 ignored issues
show
Bug introduced by
The variable $context does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
142
            /** @var TypeDefinition $context */
143 8
            $context = $this->fetch($context);
144
145 8
            if ($context->extendsOf($type)) {
146 8
                return true;
147
            }
148
149 8
            $context = $context->getInheritedParent();
150
        }
151
152 8
        return false;
153
    }
154
}
155