Completed
Push — master ( fe63b4...6cf296 )
by Tomasz
02:42
created

RouteDetector::doDetectByInterfaces()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 3
nop 1
crap 3
1
<?php
2
3
namespace Gendoria\CommandQueue\RouteDetection\Detector;
4
5
use Gendoria\CommandQueue\RouteDetection\Detection\ClassDetection;
6
use Gendoria\CommandQueue\RouteDetection\Detection\DefaultDetection;
7
use Gendoria\CommandQueue\RouteDetection\Detection\DetectionInterface;
8
use InvalidArgumentException;
9
use ReflectionClass;
10
11
/**
12
 * Detector class used for match class expressions with arbitrary routes.
13
 *
14
 * @author Tomasz Struczyński <[email protected]>
15
 */
16
class RouteDetector
17
{
18
    /**
19
     * Simple routes in a form of [Class] => [PoolName].
20
     *
21
     * @var array
22
     */
23
    private $simpleRoutes = array();
24
25
    /**
26
     * Expression routes in a form of [ClassExpression] => [PoolName].
27
     *
28
     * @var array
29
     */
30
    private $regexpRoutes = array();
31
32
    /**
33
     * Default route.
34
     *
35
     * @var string
36
     */
37
    private $defaultRoute = '';
38
39
    /**
40
     * Class constructor.
41
     */
42 22
    public function __construct($defaultRoute = '')
43
    {
44 22
        $this->setDefault($defaultRoute);
45 22
    }
46
47
    /**
48
     * Add new route.
49
     *
50
     * @param string $expression Either simple expression, or RegExp describing route.
51
     * @param string $route
52
     *
53
     * @return bool True, if route has been set, false otherwise.
54
     */
55 16
    public function addRoute($expression, $route)
56
    {
57
        //Detect command expression
58 16
        if (strpos($expression, '*') !== false) {
59 2
            $expression = '|'.str_replace(array('*', '\\'), array('.*', '\\\\'), $expression).'|i';
60 2
            if (array_key_exists($expression, $this->regexpRoutes) && $this->regexpRoutes[$expression] == $route) {
61
                return false;
62
            }
63 2
            $this->regexpRoutes[$expression] = (string) $route;
64 2
        } else {
65 14
            if (array_key_exists($expression, $this->simpleRoutes) && $this->simpleRoutes[$expression] == $route) {
66 1
                return false;
67
            }
68 14
            $this->simpleRoutes[$expression] = (string) $route;
69
        }
70
71 16
        return true;
72
    }
73
74
    /**
75
     * Set default route.
76
     *
77
     * @param string $route
78
     */
79 19
    public function setDefault($route)
80
    {
81 19
        $this->defaultRoute = (string) $route;
82 19
    }
83
84
    /**
85
     * Get default route.
86
     *
87
     * @return string
88
     */
89 12
    protected function getDefault()
90
    {
91 12
        return $this->defaultRoute;
92
    }
93
94
    /**
95
     * Detect correct route for given class.
96
     *
97
     * @param string $className
98
     *
99
     * @return string Name.
100
     *
101
     * @throws InvalidArgumentException Thrown, if argument is not a class name.
102
     */
103 22
    public function detect($className)
104
    {
105 22
        if (!class_exists($className)) {
106 1
            throw new \InvalidArgumentexception('Given name is not a class');
107
        }
108
109 21
        return $this->doDetect($className)->getPoolName();
110
    }
111
112
    /**
113
     * Detect pool.
114
     *
115
     * @param string $className
116
     * @param boolean $performInterfaceDetection
117
     *
118
     * @return DetectionInterface
119
     */
120 21
    private function doDetect($className, $performInterfaceDetection = true)
121
    {
122 21
        $detection = $this->doDetectByRoutes($className);
123 21
        if ($detection) {
124 9
            return $detection;
125
        }
126
        //Nothing is found so far. We will check all of the class interfaces and base classes.
127
        //First - check base classes up to the 'root'
128 16
        $parentClass = get_parent_class($className);
129 16
        if ($parentClass) {
130 15
            $parentDetection = $this->doDetect($parentClass, false);
131 15
            if ($parentDetection instanceof ClassDetection) {
132 4
                return $parentDetection;
133
            }
134 11
        }
135
        //Check the class interfaces
136 12
        if ($performInterfaceDetection) {
137 12
            $definition = $this->doDetectByInterfaces($className);
138 12
            if ($definition) {
139 7
                return $definition;
140
            }
141 5
        }
142
143 12
        return new DefaultDetection($this->defaultRoute);
144
    }
145
146
    /**
147
     * Perform detection based on class name and routes registered for this class.
148
     *
149
     * @param string $className
150
     *
151
     * @return ClassDetection|null
152
     */
153 21
    private function doDetectByRoutes($className)
154
    {
155
        //If we have entry for a command class, we should always return it, as it is most specific.
156 21
        if (!empty($this->simpleRoutes[$className])) {
157 14
            return new ClassDetection($this->simpleRoutes[$className]);
158
        }
159
        //Now, we should check, if we have 'regexp' entry for class
160 18
        foreach ($this->regexpRoutes as $regexpRoute => $poolName) {
161 2
            if (preg_match($regexpRoute, $className)) {
162 2
                return new ClassDetection($poolName);
163
            }
164 16
        }
165 16
    }
166
167
    /**
168
     * Perform detection based on class interfaces.
169
     *
170
     * @param string $className
171
     *
172
     * @return DetectionInterface|null
173
     */
174 12
    private function doDetectByInterfaces($className)
175
    {
176 12
        $interfaces = $this->getOrderedInterfaces($className);
177 12
        foreach ($interfaces as $interface) {
178 12
            $candidate = $this->doDetectByRoutes($interface);
1 ignored issue
show
Bug introduced by
Are you sure the assignment to $candidate is correct as $this->doDetectByRoutes($interface) (which targets Gendoria\CommandQueue\Ro...tor::doDetectByRoutes()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
179 12
            if ($candidate) {
180 7
                return $candidate;
181
            }
182 9
        }
183
184 5
        return;
185
    }
186
187
    /**
188
     * Get a list of class interfaces ordered by plase of interface declaration.
189
     *
190
     * The list is ordered by place of interface declaration. Interfaces declared on most child class are first,
191
     * while those in base class(es) - last.
192
     *
193
     * @param string $className
194
     *
195
     * @return array
196
     */
197 12
    private function getOrderedInterfaces($className)
198
    {
199 12
        $interfacesArr = array();
200 12
        $classParents = class_parents($className);
201 12
        $reflection = new ReflectionClass($className);
202 12
        $interfacesArr[] = $reflection->getInterfaceNames();
203 12
        if (empty($interfacesArr[0])) {
204
            return array();
205
        }
206 12
        foreach ($classParents as $parentClass) {
207 11
            $reflection = new ReflectionClass($parentClass);
208 11
            array_unshift($interfacesArr, $reflection->getInterfaceNames());
209 11
            if (empty($interfacesArr[0])) {
210 8
                break;
211
            }
212 12
        }
213 12
        $interfaces = array();
214 12
        foreach ($interfacesArr as $classInterfaces) {
215 12
            foreach ($classInterfaces as $interface) {
216 12
                if (array_search($interface, $interfaces) === false) {
217 12
                    array_unshift($interfaces, $interface);
218 12
                }
219 12
            }
220 12
        }
221
222 12
        return $interfaces;
223
    }
224
}
225