Completed
Pull Request — 3.x (#127)
by Joschi
02:04
created

Matcher   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 243
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 25
Bugs 0 Features 4
Metric Value
wmc 15
lcom 1
cbo 3
dl 0
loc 243
c 25
b 0
f 4
ccs 52
cts 52
cp 1
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getFailedRoute() 0 4 1
A getMatchedRoute() 0 4 1
A __construct() 0 9 1
A match() 0 16 3
A requestRoute() 0 8 2
A applyRules() 0 11 3
A ruleFailed() 0 18 3
A routeMatched() 0 9 1
1
<?php
2
/**
3
 *
4
 * This file is part of Aura for PHP.
5
 *
6
 * @license http://opensource.org/licenses/bsd-license.php BSD
7
 *
8
 */
9
namespace Aura\Router;
10
11
use Aura\Router\Exception;
12
use Aura\Router\Rule\RuleIterator;
13
use Psr\Http\Message\ServerRequestInterface;
14
use Psr\Log\LoggerInterface;
15
16
/**
17
 *
18
 * Matches against the route map.
19
 *
20
 * @package Aura.Router
21
 *
22
 */
23
class Matcher
24
{
25
    /**
26
     *
27
     * Logging information about which routes were attempted to match.
28
     *
29
     * @var array
30
     *
31
     */
32
    protected $logger;
33
34
    /**
35
     *
36
     * The map of all routes.
37
     *
38
     * @var Map
39
     *
40
     */
41
    protected $map;
42
43
    /**
44
     *
45
     * A collection of matching rules to iterate through.
46
     *
47
     * @var RuleIterator
48
     *
49
     */
50
    protected $ruleIterator;
51
52
    /**
53
     *
54
     * The Route object matched by the router.
55
     *
56
     * @var Route|false
57
     *
58
     */
59
    protected $matchedRoute;
60
61
    /**
62
     *
63
     * The first of the closest-matching failed routes.
64
     *
65
     * @var Route
66
     *
67
     */
68
    protected $failedRoute;
69
70
    /**
71
     *
72
     * The score of the closest-matching failed route.
73
     *
74
     * @var int
75
     *
76
     */
77
    protected $failedScore = 0;
78
79
    /**
80
     *
81
     * Constructor.
82
     *
83
     * @param Map $map A route collection object.
84
     *
85
     * @param LoggerInterface $logger A logger object.
86
     *
87
     * @param RuleIterator $ruleIterator A collection of matching rules.
88
     *
89
     */
90 5
    public function __construct(
91
        Map $map,
92
        LoggerInterface $logger,
93
        RuleIterator $ruleIterator
94
    ) {
95 5
        $this->map = $map;
96 5
        $this->logger = $logger;
0 ignored issues
show
Documentation Bug introduced by
It seems like $logger of type object<Psr\Log\LoggerInterface> is incompatible with the declared type array of property $logger.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
97 5
        $this->ruleIterator = $ruleIterator;
98 5
    }
99
100
    /**
101
     *
102
     * Gets a route that matches the request.
103
     *
104
     * @param ServerRequestInterface $request The incoming request.
105
     *
106
     * @return Route|false Returns a route object when it finds a match, or
107
     * boolean false if there is no match.
108
     *
109
     */
110 5
    public function match(ServerRequestInterface $request)
111
    {
112 5
        $this->matchedRoute = false;
113 5
        $this->failedRoute = null;
114 5
        $this->failedScore = 0;
115 5
        $path = $request->getUri()->getPath();
116
117 5
        foreach ($this->map as $name => $proto) {
118 5
            $route = $this->requestRoute($request, $proto, $name, $path);
119 5
            if ($route) {
120 3
                return $route;
121
            }
122 4
        }
123
124 3
        return false;
125
    }
126
127
    /**
128
     *
129
     * Match a request to a route.
130
     *
131
     * @param ServerRequestInterface $request The request to match against.
132
     *
133
     * @param Route $proto The proto-route to match against.
134
     *
135
     * @param string $name The route name.
136
     *
137
     * @param string $path The request path.
138
     *
139
     * @return mixed False on failure, or a Route on match.
140
     *
141
     */
142 5
    protected function requestRoute($request, $proto, $name, $path)
143
    {
144 5
        if (! $proto->isRoutable) {
0 ignored issues
show
Documentation introduced by
The property $isRoutable is declared protected in Aura\Router\Route. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
145 1
            return;
146
        }
147 5
        $route = clone $proto;
148 5
        return $this->applyRules($request, $route, $name, $path);
149
    }
150
151
    /**
152
     *
153
     * Does the request match a route per the matching rules?
154
     *
155
     * @param ServerRequestInterface $request The request to match against.
156
     *
157
     * @param Route $route The route to match against.
158
     *
159
     * @param string $name The route name.
160
     *
161
     * @param string $path The request path.
162
     *
163
     * @return mixed False on failure, or a Route on match.
164
     *
165
     */
166 5
    protected function applyRules($request, $route, $name, $path)
167
    {
168 5
        $score = 0;
169 5
        foreach ($this->ruleIterator as $rule) {
170 5
            if (! $rule($request, $route)) {
171 4
                return $this->ruleFailed($request, $route, $name, $path, $rule, $score);
172
            }
173 5
            $score ++;
174 5
        }
175 3
        return $this->routeMatched($route, $name, $path);
176
    }
177
178
    /**
179
     *
180
     * A matching rule failed.
181
     *
182
     * @param ServerRequestInterface $request The request to match against.
183
     *
184
     * @param Route $route The route to match against.
185
     *
186
     * @param string $name The route name.
187
     *
188
     * @param string $path The request path.
189
     *
190
     * @param mixed $rule The rule that failed.
191
     *
192
     * @param int $score The failure score.
193
     *
194
     * @return false
195
     *
196
     */
197 4
    protected function ruleFailed($request, $route, $name, $path, $rule, $score)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
198
    {
199 4
        $ruleClass = get_class($rule);
200 4
        $route->failedRule($ruleClass);
201
202 4
        if (! $this->failedRoute || $score > $this->failedScore) {
203 4
            $this->failedRoute = $route;
204 4
            $this->failedScore = $score;
205 4
        }
206
207 4
        $this->logger->debug("{path} FAILED {ruleClass} ON {name}", [
0 ignored issues
show
Bug introduced by
The method debug cannot be called on $this->logger (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
208 4
            'path' => $path,
209 4
            'ruleClass' => $ruleClass,
210
            'name' => $name
211 4
        ]);
212
213 4
        return false;
214
    }
215
216
    /**
217
     *
218
     * The route matched.
219
     *
220
     * @param Route $route The route to match against.
221
     *
222
     * @param string $name The route name.
223
     *
224
     * @param string $path The request path.
225
     *
226
     * @return Route
227
     *
228
     */
229 3
    protected function routeMatched($route, $name, $path)
230
    {
231 3
        $this->logger->debug("{path} MATCHED ON {name}", [
0 ignored issues
show
Bug introduced by
The method debug cannot be called on $this->logger (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
232 3
            'path' => $path,
233 3
            'name' => $name,
234 3
        ]);
235 3
        $this->matchedRoute = $route;
236 3
        return $route;
237
    }
238
239
    /**
240
     *
241
     * Get the first of the closest-matching failed routes.
242
     *
243
     * @return Route
244
     *
245
     */
246 2
    public function getFailedRoute()
247
    {
248 2
        return $this->failedRoute;
249
    }
250
251
    /**
252
     *
253
     * Returns the result of the call to match() again so you don't need to
254
     * run the matching process again.
255
     *
256
     * @return Route|false|null Returns null if match() has not been called
257
     * yet, false if it has and there was no match, or a Route object if there
258
     * was a match.
259
     *
260
     */
261 3
    public function getMatchedRoute()
262
    {
263 3
        return $this->matchedRoute;
264
    }
265
}
266