Passed
Push — master ( 39666f...8b4565 )
by Бабичев
09:02
created

Route::uriValid()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 11
ccs 0
cts 5
cp 0
crap 6
rs 9.4285
c 0
b 0
f 0
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 Slice  $slice
64
     * @param string $defaultRegex
65
     */
66
    public function __construct(Slice $slice, $defaultRegex = null)
67
    {
68
        $this->slice = $slice;
69
70
        if ($defaultRegex !== null)
71
        {
72
            $this->defaultRegex = $defaultRegex;
73
        }
74
75
        $this->reload();
76
    }
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
    public function getDefaults()
122
    {
123
        return $this->defaults;
124
    }
125
126
    /**
127
     * @return array
128
     */
129
    public function getAttributes()
130
    {
131
        return $this->attributes ?? $this->defaults;
132
    }
133
134
    /**
135
     * @return string
136
     */
137
    protected function pathFilter()
138
    {
139
        return preg_replace_callback(
140
            '~\<(?<key>\w+)(\:(?<value>.+?))?\>~',
141
            function ($matches)
142
            {
143
                if (!empty($matches['value']) && empty($this->regex[$matches['key']]))
144
                {
145
                    $this->regex[$matches['key']] = $matches['value'];
146
                }
147
148
                return '<' . $matches['key'] . '>';
149
            },
150
            $this->path
151
        );
152
    }
153
154
    /**
155
     * @param string $path
156
     *
157
     * @return string
158
     */
159
    protected function quote($path)
160
    {
161
        $path = preg_quote($path, '()');
162
        $path = strtr($path, [
163
            '\\(' => '(',
164
            '\\)' => ')',
165
            '\\<' => '<',
166
            '\\>' => '>',
167
        ]);
168
169
        return $this->optional($path);
170
    }
171
172
    /**
173
     * @param string $rulePath
174
     *
175
     * @return string
176
     */
177
    protected function optional($rulePath)
178
    {
179
        return str_replace(')', ')?', $rulePath);
180
    }
181
182
    /**
183
     * @param string $route
184
     *
185
     * @return string
186
     */
187
    protected function toRegex($route)
188
    {
189
        $path = $this->quote($route);
190
191
        return preg_replace_callback(
192
            '~\<(?<key>[\w-]+)\>~',
193
            function ($matches)
194
            {
195
                return '(?<' . $matches['key'] . '>' . ($this->regex[$matches['key']] ?? $this->defaultRegex) . ')';
196
            },
197
            $path
198
        );
199
    }
200
201
    /**
202
     * @param string[] $matches
203
     *
204
     * @return array
205
     */
206
    protected function attributes($matches)
207
    {
208
        return array_filter($matches, function ($value, $key)
209
        {
210
            return !is_int($key) && (is_numeric($value) || !empty($value));
211
        }, ARRAY_FILTER_USE_BOTH);
212
    }
213
214
    /**
215
     * @param string $method
216
     *
217
     * @return bool
218
     */
219
    public function methodValid($method)
220
    {
221
        return empty($this->methods) ||
222
            in_array($method, $this->methods, true) ||
223
            ($method === 'AJAX' && isAjax());
224
    }
225
226
    /**
227
     * @param string $uri
228
     *
229
     * @return bool
230
     */
231
    public function uriValid($uri)
232
    {
233
        $result = preg_match('~^' . $this->regexPath . '$~u', $uri, $matches);
234
235
        if ($result)
236
        {
237
            $this->attributes = array_merge($this->defaults, $this->attributes($matches));
238
        }
239
240
        return $result !== 0;
241
    }
242
243
    /**
244
     * @param string $uri
245
     * @param string $method
246
     *
247
     * @return bool
248
     */
249
    public function test($uri, $method)
250
    {
251
        if (!$this->methodValid($method))
252
        {
253
            return false;
254
        }
255
256
        return $this->uriValid($uri);
257
    }
258
259
    /**
260
     * @param array  $http
261
     * @param string $path
262
     *
263
     * @return string
264
     */
265
    protected function regexUri(array $http, $path)
266
    {
267
        return $http['protocol'] . '\:\/{2}' . $http['host'] . $path;
268
    }
269
270
    /**
271
     * @return string
272
     */
273
    protected function regex()
274
    {
275
        $this->filterPath = $this->pathFilter();
276
277
        $regex = $this->toRegex($this->filterPath);
278
        $http  = $this->http;
279
280
        if (!$this->http['protocol'])
281
        {
282
            $http['protocol'] = 'https?';
283
        }
284
285
        if (!$this->http['host'])
286
        {
287
            $http['host'] = '[^\/]+';
288
        }
289
290
        return $this->regexUri($http, $regex);
291
    }
292
293
    /**
294
     * reload route
295
     */
296
    protected function reload()
297
    {
298
        $http = [
299
            'protocol' => null,
300
            'host'     => null
301
        ];
302
303
        $this->http      = (array)$this->slice->atData('http', $http);
304
        $this->defaults  = (array)$this->slice->atData('defaults');
305
        $this->methods   = (array)$this->slice->atData('methods');
306
        $this->regex     = (array)$this->slice->atData('regex');
307
        $this->path      = $this->slice->atData('path');
308
        $this->regexPath = $this->regex();
309
    }
310
311
    /**
312
     * @inheritdoc
313
     */
314
    public function serialize()
315
    {
316
        return serialize([
317
            'defaultRegex' => $this->defaultRegex,
318
            'http'         => $this->http,
319
            'path'         => $this->path,
320
            'regex'        => $this->regex,
321
            'regexPath'    => $this->regexPath,
322
            'filterPath'   => $this->filterPath,
323
            'defaults'     => $this->defaults,
324
            'methods'      => $this->methods,
325
            'attributes'   => $this->attributes,
326
            'slice'        => $this->slice,
327
        ]);
328
    }
329
330
    /**
331
     * @inheritdoc
332
     */
333
    public function unserialize($serialized)
334
    {
335
        $data = unserialize($serialized, []);
336
337
        foreach ($data as $variable => $value)
338
        {
339
            $this->{$variable} = $value;
340
        }
341
    }
342
343
344
}
345