Passed
Push — master ( 957c9d...4b4e9a )
by Бабичев
37:59 queued 32:50
created

Route::uriValid()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
ccs 5
cts 5
cp 1
crap 2
1
<?php
2
3
namespace Bavix\Router;
4
5
use Bavix\Slice\Slice;
6
7
class Route implements \Serializable
8
{
9
10
    /**
11
     * @var string
12
     */
13
    protected $defaultRegex = '[\w-А-ЯЁа-яё]+';
14
15
    /**
16
     * @var array
17
     */
18
    protected $http;
19
20
    /**
21
     * @var string
22
     */
23
    protected $path;
24
25
    /**
26
     * @var array
27
     */
28
    protected $regex;
29
30
    /**
31
     * @var string
32
     */
33
    protected $regexPath;
34
35
    /**
36
     * @var string
37
     */
38
    protected $filterPath;
39
40
    /**
41
     * @var array
42
     */
43
    protected $defaults;
44
45
    /**
46
     * @var array
47
     */
48
    protected $methods;
49
50
    /**
51
     * @var array
52
     */
53
    protected $attributes;
54
55
    /**
56
     * @var Slice
57
     */
58
    protected $slice;
59
60
    /**
61
     * Route constructor.
62
     *
63
     * @param array|\Traversable|Slice $data
64
     * @param string                   $defaultRegex
65
     */
66 4
    public function __construct($data, $defaultRegex = null)
67
    {
68 4
        $this->slice = Slice::from($data);
69
70 4
        if ($defaultRegex !== null)
71
        {
72
            $this->defaultRegex = $defaultRegex;
73
        }
74
75 4
        $this->reload();
76 4
    }
77
78
    /**
79
     * @return array
80
     */
81
    public function getHttp()
82
    {
83
        return $this->http;
84
    }
85
86
    /**
87
     * @return string
88
     */
89
    public function getPath()
90
    {
91
        return $this->path;
92
    }
93
94
    /**
95
     * @return array
96
     */
97
    public function getRegex()
98
    {
99
        return $this->regex;
100
    }
101
102
    /**
103
     * @return string
104
     */
105
    public function getRegexPath()
106
    {
107
        return $this->regexPath;
108
    }
109
110
    /**
111
     * @return string
112
     */
113
    public function getFilterPath()
114
    {
115
        return $this->filterPath;
116
    }
117
118
    /**
119
     * @return array
120
     */
121 1
    public function getDefaults()
122
    {
123 1
        return $this->defaults;
124
    }
125
126
    /**
127
     * @return array
128
     */
129 3
    public function getAttributes()
130
    {
131 3
        return $this->attributes ?? $this->defaults;
132
    }
133
134
    /**
135
     * @return string
136
     */
137 4
    protected function pathFilter()
138
    {
139 4
        return preg_replace_callback(
140 4
            '~\<(?<key>\w+)(\:(?<value>.+?))?\>~',
141 4
            function ($matches) {
142 4
                if (!empty($matches['value']) && empty($this->regex[$matches['key']]))
143
                {
144 2
                    $this->regex[$matches['key']] = $matches['value'];
145
                }
146
147 4
                return '<' . $matches['key'] . '>';
148 4
            },
149 4
            $this->path
150
        );
151
    }
152
153
    /**
154
     * @param string $path
155
     *
156
     * @return string
157
     */
158 4
    protected function quote($path)
159
    {
160 4
        $path = preg_quote($path, '()');
161 4
        $path = strtr($path, [
162 4
            '\\(' => '(',
163
            '\\)' => ')',
164
            '\\<' => '<',
165
            '\\>' => '>',
166
        ]);
167
168 4
        return $this->optional($path);
169
    }
170
171
    /**
172
     * @param string $rulePath
173
     *
174
     * @return string
175
     */
176 4
    protected function optional($rulePath)
177
    {
178 4
        return str_replace(')', ')?', $rulePath);
179
    }
180
181
    /**
182
     * @param string $route
183
     *
184
     * @return string
185
     */
186 4
    protected function toRegex($route)
187
    {
188 4
        $path = $this->quote($route);
189
190 4
        return preg_replace_callback(
191 4
            '~\<(?<key>[\w-]+)\>~',
192 4
            function ($matches) {
193 4
                return '(?<' . $matches['key'] . '>' . ($this->regex[$matches['key']] ?? $this->defaultRegex) . ')';
194 4
            },
195 4
            $path
196
        );
197
    }
198
199
    /**
200
     * @param string[] $matches
201
     *
202
     * @return array
203
     */
204
    protected function attributes($matches)
205
    {
206 3
        return array_filter($matches, function ($value, $key) {
207 3
            return !is_int($key) && (is_numeric($value) || !empty($value));
208 3
        }, ARRAY_FILTER_USE_BOTH);
209
    }
210
211
    /**
212
     * @param string $method
213
     *
214
     * @return bool
215
     */
216 4
    public function methodValid($method)
217
    {
218 4
        return empty($this->methods) ||
219 2
            in_array($method, $this->methods, true) ||
220 4
            ($method === 'AJAX' && isAjax());
221
    }
222
223
    /**
224
     * @param string $uri
225
     *
226
     * @return bool
227
     */
228 3
    public function uriValid($uri)
229
    {
230 3
        $result = preg_match('~^' . $this->regexPath . '$~u', $uri, $matches);
231
232 3
        if ($result)
233
        {
234 3
            $this->attributes = array_merge($this->defaults, $this->attributes($matches));
235
        }
236
237 3
        return $result !== 0;
238
    }
239
240
    /**
241
     * @param string $uri
242
     * @param string $method
243
     *
244
     * @return bool
245
     */
246 4
    public function test($uri, $method)
247
    {
248 4
        if (!$this->methodValid($method))
249
        {
250 1
            return false;
251
        }
252
253 3
        return $this->uriValid($uri);
254
    }
255
256
    /**
257
     * @param array  $http
258
     * @param string $path
259
     *
260
     * @return string
261
     */
262 4
    protected function regexUri(array $http, $path)
263
    {
264 4
        return $http['protocol'] . '\:\/{2}' . $http['host'] . $path;
265
    }
266
267
    /**
268
     * @return string
269
     */
270 4
    protected function regex()
271
    {
272 4
        $this->filterPath = $this->pathFilter();
273
274 4
        $regex = $this->toRegex($this->filterPath);
275 4
        $http  = $this->http;
276
277 4
        if (!$this->http['protocol'])
278
        {
279 4
            $http['protocol'] = 'https?';
280
        }
281
282 4
        if (!$this->http['host'])
283
        {
284 4
            $http['host'] = '[^\/]+';
285
        }
286
287 4
        return $this->regexUri($http, $regex);
288
    }
289
290
    /**
291
     * reload route
292
     */
293 4
    protected function reload()
294
    {
295
        $http = [
296 4
            'protocol' => null,
297
            'host'     => null
298
        ];
299
300 4
        $this->http      = (array)$this->slice->atData('http', $http);
301 4
        $this->defaults  = (array)$this->slice->atData('defaults');
302 4
        $this->methods   = (array)$this->slice->atData('methods');
303 4
        $this->regex     = (array)$this->slice->atData('regex');
304 4
        $this->path      = $this->slice->atData('path');
305 4
        $this->regexPath = $this->regex();
306 4
    }
307
308
    /**
309
     * @inheritdoc
310
     */
311
    public function serialize()
312
    {
313
        return serialize([
314
            'defaultRegex' => $this->defaultRegex,
315
            'http'         => $this->http,
316
            'path'         => $this->path,
317
            'regex'        => $this->regex,
318
            'regexPath'    => $this->regexPath,
319
            'filterPath'   => $this->filterPath,
320
            'defaults'     => $this->defaults,
321
            'methods'      => $this->methods,
322
            'attributes'   => $this->attributes,
323
            'slice'        => $this->slice,
324
        ]);
325
    }
326
327
    /**
328
     * @inheritdoc
329
     */
330
    public function unserialize($serialized)
331
    {
332
        $data = unserialize($serialized, []);
333
334
        foreach ($data as $variable => $value)
335
        {
336
            $this->{$variable} = $value;
337
        }
338
    }
339
340
341
}
342