Completed
Push — master ( 8dd423...4c2657 )
by Alejandro
06:28
created

Route::filterPattern()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
rs 9.4285
cc 2
eloc 10
nc 2
nop 0
1
<?php
2
3
namespace Tight;
4
5
/*
6
 * The MIT License
7
 *
8
 * Copyright 2016 Alejandro Peña Florentín ([email protected])
9
 *
10
 * Permission is hereby granted, free of charge, to any person obtaining a copy
11
 * of this software and associated documentation files (the "Software"), to deal
12
 * in the Software without restriction, including without limitation the rights
13
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
 * copies of the Software, and to permit persons to whom the Software is
15
 * furnished to do so, subject to the following conditions:
16
 *
17
 * The above copyright notice and this permission notice shall be included in
18
 * all copies or substantial portions of the Software.
19
 *
20
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
 * THE SOFTWARE.
27
 */
28
29
/**
30
 * Description of Route
31
 *
32
 * @author Alejandro Peña Florentín ([email protected])
33
 */
34
class Route
35
{
36
37
    const METHOD_GET = "get";
38
    const METHOD_POST = "post";
39
    const METHOD_UPDATE = "update";
40
    const METHOD_DELETE = "delete";
41
    const PARAMETER_CHARACTER = ":";
42
43
    protected $methods = [];
44
45
    /**
46
     * @var array Key-value array of URL parameters
47
     */
48
    protected $params = [];
49
50
    /**
51
     * @var array[Callable] Middleware to be run before only this route instance
52
     */
53
    protected $middleware = [];
54
55
    /**
56
     *
57
     * @var string The route pattern (e.g. "/books/:id")
58
     */
59
    protected $pattern = "";
60
    protected $callable;
61
    private $argsPattern = self::PARAMETER_CHARACTER . "(\\w+)";
62
63
    public function __construct($pattern, $callable) {
64
        $this->setPattern($pattern);
65
        $this->setCallable($callable);
66
        $this->filterPattern();
67
    }
68
69
    private function filterPattern() {
70
        $pattern = $this->getPattern();
71
        $match = [];
72
        $pattern = str_replace("/", "\\/", $pattern);
73
        if (preg_match_all("/" . $this->argsPattern . "/", $pattern, $match)) {
74
            $strings = $match[0];
75
            $parameters = $match[1];
76
            $this->params = $parameters;
77
            $pattern = str_replace($strings, str_replace(self::PARAMETER_CHARACTER, "", $this->argsPattern), $pattern);
78
        }
79
        $this->setPattern("/^" . $pattern . "$/");
80
    }
81
82
    public function setPattern($pattern) {
83
        if (!is_string($pattern)) {
84
            throw new \InvalidArgumentException('Route name must be a string');
85
        }
86
        $this->pattern = $pattern;
87
        return $this;
88
    }
89
90
    public function setCallable($callback) {
91
        if (is_callable($callback))
92
            $this->callable = $callback;
93
    }
94
95
    /**
96
     * Add supported HTTP method(s)
97
     */
98
    public function setHttpMethods($methods) {
99
        $this->methods = $methods;
100
    }
101
102
    /**
103
     * Get middleware
104
     * @return array[Callable]
0 ignored issues
show
Documentation introduced by
The doc-type array[Callable] could not be parsed: Expected "]" at position 2, but found "Callable". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
105
     */
106
    public function getMiddleware() {
107
        return $this->middleware;
108
    }
109
110
    /**
111
     * Set middleware
112
     *
113
     * This method allows middleware to be assigned to a specific Route.
114
     * If the method argument `is_callable` (including callable arrays!),
115
     * we directly append the argument to `$this->middleware`. Else, we
116
     * assume the argument is an array of callables and merge the array
117
     * with `$this->middleware`.  Each middleware is checked for is_callable()
118
     * and an InvalidArgumentException is thrown immediately if it isn't.
119
     *
120
     * @param  Callable|array
121
     * @return \Tight\Route
122
     * @throws \InvalidArgumentException If argument is not callable or not an array of callables.
123
     */
124
    public function setMiddleware($middleware) {
125
        if (is_callable($middleware)) {
126
            $this->middleware[] = $middleware;
127
        } elseif (is_array($middleware)) {
128
            foreach ($middleware as $callable) {
129
                if (!is_callable($callable)) {
130
                    throw new \InvalidArgumentException('All Route middleware must be callable');
131
                }
132
            }
133
            $this->middleware = array_merge($this->middleware, $middleware);
134
        } else {
135
            throw new \InvalidArgumentException('Route middleware must be callable or an array of callables');
136
        }
137
138
        return $this;
139
    }
140
141
    /**
142
     * Get supported HTTP methods
143
     * @return array
144
     */
145
    public function getHttpMethods() {
146
        return $this->methods;
147
    }
148
149
    /**
150
     * Get the pattern
151
     * @return string Pattern
152
     */
153
    public function getPattern() {
154
        return $this->pattern;
155
    }
156
157
    /**
158
     * Append supported HTTP methods
159
     */
160
    public function appendHttpMethods() {
161
        $args = func_get_args();
162
        $this->methods = array_merge($this->methods, $args);
163
    }
164
165
    /**
166
     * Get route parameters
167
     * @return array Route parameters
168
     */
169
    public function getParams() {
170
        return $this->params;
171
    }
172
173
    public function match($uri) {
174
        $matches = [];
175
        if (preg_match_all($this->pattern, $uri, $matches)) {
176
            $params = $this->params;
177
            // Remove 1st match, the full string
178
            unset($matches[0]);
179
            // Fix array index
180
            $matches = array_values($matches);
181
            // Clear the array parameter
182
            $this->params = [];
183
            for ($index = 0; $index < count($params); $index++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
184
                $name = $params[$index];
185
                $value = $matches[$index][0];
186
                // Set parameter
187
                $this->params[$name] = $value;
188
            }
189
            return true;
190
        }
191
        return false;
192
    }
193
194
    /**
195
     * Dispatch route
196
     *
197
     * This method invokes the route object's callable. If middleware is
198
     * registered for the route, each callable middleware is invoked in
199
     * the order specified.
200
     *
201
     * @return bool
202
     */
203
    public function dispatch() {
204
        foreach ($this->middleware as $mw) {
205
            call_user_func_array($mw, array($this));
206
        }
207
        $return = call_user_func_array($this->callable, array_values($this->getParams()));
208
        return $return;
209
    }
210
211
}
212