Passed
Push — master ( ad31bb...824b59 )
by Mikael
02:28 queued 17s
created

Route   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 242
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 92.77%

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 34
c 2
b 0
f 1
lcom 1
cbo 0
dl 0
loc 242
ccs 77
cts 83
cp 0.9277
rs 9.2

9 Methods

Rating   Name   Duplication   Size   Complexity  
A set() 0 12 2
B checkPartAsArgument() 0 18 5
B checkPartMatchingType() 0 23 5
A matchPart() 0 19 4
B matchRequestMethod() 0 9 6
D match() 0 35 9
A handle() 0 4 1
A setName() 0 5 1
A getRule() 0 4 1
1
<?php
2
3
namespace Anax\Route;
4
5
/**
6
 * A container for routes.
7
 *
8
 */
9
class Route
10
{
11
    /**
12
     * @var string       $name      a name for this route.
13
     * @var string|array $method    the method(s) to support
14
     * @var string       $rule      the path rule for this route
15
     * @var callable     $action    the callback to handle this route
16
     * @var null|array   $arguments arguments for the callback, extracted
17
     *                              from path
18
     */
19
    private $name;
20
    private $method;
21
    private $rule;
22
    private $action;
23
    private $arguments = [];
24
25
26
27
    /**
28
     * Set values for route.
29
     *
30
     * @param null|string       $rule   for this route
31
     * @param callable          $action callable to implement a controller for
32
     *                                  the route
33
     * @param null|string|array $method as request method to support
34
     *
35
     * @return $this
36
     */
37 24
    public function set($rule, $action = null, $method = null)
38
    {
39 24
        $this->rule = $rule;
40 24
        $this->action = $action;
41
42 24
        $this->method = $method;
43 24
        if (is_string($method)) {
44 3
            $this->method = array_map("trim", explode("|", $method));
45 3
        }
46
47 24
        return $this;
48
    }
49
50
51
52
    /**
53
     * Check if part of route is a argument and optionally match type
54
     * as a requirement {argument:type}.
55
     *
56
     * @param string $rulePart   the rule part to check.
57
     * @param string $queryPart  the query part to check.
58
     * @param array  &$args      add argument to args array if matched
59
     *
60
     * @return boolean
61
     */
62 3
    private function checkPartAsArgument($rulePart, $queryPart, &$args)
63
    {
64 3
        if (substr($rulePart, -1) == "}"
65 3
            && !is_null($queryPart)
66 3
        ) {
67 3
            $part = substr($rulePart, 1, -1);
68 3
            $pos = strpos($part, ":");
69 3
            if ($pos !== false) {
70 1
                $type = substr($part, $pos + 1);
71 1
                if (! $this->checkPartMatchingType($queryPart, $type)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->checkPartMatchingType($queryPart, $type) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
72 1
                    return false;
73
                }
74 1
            }
75 3
            $args[] = $queryPart;
76 3
            return true;
77
        }
78 1
        return false;
79
    }
80
81
82
83
    /**
84
     * Check if value is matching a certain type of values.
85
     *
86
     * @param string $rulePart   the rule part to check.
0 ignored issues
show
Bug introduced by
There is no parameter named $rulePart. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
87
     * @param string $queryPart  the query part to check.
0 ignored issues
show
Bug introduced by
There is no parameter named $queryPart. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
88
     * @param array  &$args      add argument to args array if matched
89
     *
90
     * @return boolean
91
     */
92 1
    private function checkPartMatchingType($value, $type)
93
    {
94
        switch ($type) {
95 1
            case "digit":
96 1
                return ctype_digit($value);
97
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
98
99 1
            case "hex":
100 1
                return ctype_xdigit($value);
101
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
102
103 1
            case "alpha":
104 1
                return ctype_alpha($value);
105
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
106
107 1
            case "alphanum":
108 1
                return ctype_alnum($value);
109
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
110
111
            default:
112
                return false;
113
        }
114
    }
115
116
117
118
    /**
119
     * Match part of rule and query.
120
     *
121
     * @param string $rulePart   the rule part to check.
122
     * @param string $queryPart  the query part to check.
123
     * @param array  &$args      add argument to args array if matched
124
     *
125
     * @return boolean
126
     */
127 18
    private function matchPart($rulePart, $queryPart, &$args)
128
    {
129 18
        $match = false;
0 ignored issues
show
Unused Code introduced by
$match 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...
130 18
        $first = isset($rulePart[0]) ? $rulePart[0] : '';
131
        switch ($first) {
132 18
            case '*':
133 1
                $match = true;
134 1
                break;
135
136 18
            case '{':
137 3
                $match = $this->checkPartAsArgument($rulePart, $queryPart, $args);
138 3
                break;
139
140 17
            default:
141 17
                $match = ($rulePart == $queryPart);
142 17
                break;
143 17
        }
144 18
        return $match;
145
    }
146
147
148
149
    /**
150
     * Check if the request method matches.
151
     *
152
     * @param string $method as request method
153
     *
154
     * @return boolean true if request method matches
155
     */
156 23
    public function matchRequestMethod($method)
157
    {
158 23
        if ($method && is_array($this->method) && !in_array($method, $this->method)
159 23
            || (is_null($method) && !empty($this->method))
160 23
        ) {
161 6
            return false;
162
        }
163 23
        return true;
164
    }
165
166
167
168
    /**
169
     * Check if the route matches a query and request method.
170
     *
171
     * @param string $query  to match against
172
     * @param string $method as request method
173
     *
174
     * @return boolean true if query matches the route
175
     */
176 23
    public function match($query, $method = null)
177 1
    {
178 23
        if (!$this->matchRequestMethod($method)) {
179 6
            return false;
180
        }
181
182
        // If any/default */** route, match anything
183 23
        if (is_null($this->rule)
184 21
            || in_array($this->rule, ["*", "**"])
185 23
        ) {
186 5
            return true;
187
        }
188
189
        // Check all parts to see if they matches
190 18
        $ruleParts  = explode('/', $this->rule);
191 18
        $queryParts = explode('/', $query);
192 18
        $ruleCount = max(count($ruleParts), count($queryParts));
193 18
        $args = [];
194
195 18
        for ($i = 0; $i < $ruleCount; $i++) {
196 18
            $rulePart  = isset($ruleParts[$i])  ? $ruleParts[$i]  : null;
197 18
            $queryPart = isset($queryParts[$i]) ? $queryParts[$i] : null;
198
199 18
            if ($rulePart === "**") {
200 1
                break;
201
            }
202
203 18
            if (!$this->matchPart($rulePart, $queryPart, $args)) {
204 8
                return false;
205
            }
206 18
        }
207
208 18
        $this->arguments = $args;
209 18
        return true;
210
    }
211
212
213
214
    /**
215
     * Handle the action for the route.
216
     *
217
     * @return void
218
     */
219 13
    public function handle()
220
    {
221 13
        return call_user_func($this->action, ...$this->arguments);
222
    }
223
224
225
226
    /**
227
     * Set the name of the route.
228
     *
229
     * @param string $name set a name for the route
230
     *
231
     * @return $this
232
     */
233
    public function setName($name)
234
    {
235
        $this->name = $name;
236
        return $this;
237
    }
238
239
240
241
    /**
242
     * Get the rule for the route.
243
     *
244
     * @return string
245
     */
246 12
    public function getRule()
247
    {
248 12
        return $this->rule;
249
    }
250
}
251