Router::parseUri()   B
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
c 0
b 0
f 0
rs 8.5906
nc 5
cc 5
eloc 14
nop 2
1
<?php /** MicroRouter */
2
3
namespace Micro\Web;
4
5
/**
6
 * Router class file.
7
 *
8
 * Routing user requests
9
 *
10
 * @author Oleg Lunegov <[email protected]>
11
 * @link https://github.com/linpax/microphp-framework
12
 * @copyright Copyright (c) 2013 Oleg Lunegov
13
 * @license https://github.com/linpax/microphp-framework/blob/master/LICENSE
14
 * @package Micro
15
 * @subpackage Web
16
 * @version 1.0
17
 * @since 1.0
18
 */
19
class Router implements IRouter
20
{
21
    /** @var array $routes routes for routing */
22
    public $routes = [];
23
24
25
    /**
26
     * Construct for route scanner
27
     *
28
     * @access public
29
     *
30
     * @param array $routes
31
     *
32
     * @result void
33
     */
34
    public function __construct(array $routes = [])
35
    {
36
        $this->routes = $routes;
37
    }
38
39
    /**
40
     * @inheritdoc
41
     */
42
    public function parse($uri, $method = 'GET')
43
    {
44
        // default path
45
        if ($uri === '/' || $uri === '' || $uri === '/default') {
46
            return '/default';
47
        }
48
49
        // scan routes
50
        foreach ($this->routes as $condition => $config) {
51
            if (is_array($config) && !empty($config['route'])) {
52
                if (!empty($config['verb']) && ($config['verb'] !== $method)) {
53
                    continue;
54
                }
55
56
                $replacement = $config['route'];
57
            } elseif (is_string($config)) {
58
                $replacement = $config;
59
            } else {
60
                continue;
61
            }
62
            // slice path
63
            if ($uri === $condition) {
64
                return $replacement;
65
            }
66
            // pattern path
67
            if ($validated = $this->validatedRule($uri, $condition, $replacement)) {
68
                return $validated;
69
            }
70
        }
71
72
        // return raw uri
73
        return $uri;
74
    }
75
76
    /**
77
     * Validated router rule
78
     *
79
     * @access private
80
     *
81
     * @param string $uri uri to validation
82
     * @param string $pattern checking pattern
83
     * @param string $replacement replacement for pattern
84
     *
85
     * @return string
86
     */
87
    private function validatedRule($uri, $pattern, $replacement)
88
    {
89
        $uriBlocks = explode('/', trim($uri, '/'));
90
        $patBlocks = explode('/', trim($pattern, '/'));
91
        $repBlocks = explode('/', trim($replacement, '/'));
92
93
        if (count($uriBlocks) !== count($patBlocks)) {
94
            return false;
95
        }
96
97
98
        $attributes = $this->parseUri($uriBlocks, $patBlocks);
99
        if (!$attributes) {
100
            return false;
101
        }
102
103
        $result = $this->buildResult($attributes, $repBlocks);
104
        if ($result === null || $result === false) {
105
            return false;
106
        }
107
108
        if (!$attributes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $attributes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
109
            return $result;
110
        }
111
112
        $result .= '?';
113
        foreach ($attributes AS $key => $val) {
114
            if ($key !== $val) {
115
                $result .= '&'.$key.'='.$val;
116
            }
117
        }
118
119
        return $result;
120
    }
121
122
    /**
123
     * Match patBlocks in uriBlocks
124
     *
125
     * @access private
126
     *
127
     * @param array $uriBlocks uri blocks from URL
128
     * @param array $patBlocks pattern blocks from valid URL
129
     *
130
     * @return array
131
     */
132
    private function parseUri(array $uriBlocks = [], array $patBlocks = [])
133
    {
134
        $attr = [];
135
136
        foreach (range(0, count($uriBlocks) - 1) AS $i) {
137
            if (0 === strpos($patBlocks[$i], '<')) {
138
                $cut = strpos($patBlocks[$i], ':');
139
140
                if (preg_match('/'.substr($patBlocks[$i], $cut + 1, -1).'/', $uriBlocks[$i])) {
141
                    $attr[substr($patBlocks[$i], 1, $cut - 1)] = $uriBlocks[$i];
142
                } else {
143
                    return null;
144
                }
145
146
            } elseif ($uriBlocks[$i] !== $patBlocks[$i]) {
147
                return null;
148
            } else {
149
                $attr[$uriBlocks[$i]] = $patBlocks[$i];
150
            }
151
        }
152
153
        return $attr;
154
    }
155
156
    /**
157
     * Replacement $result with repBlocks
158
     *
159
     * @access private
160
     *
161
     * @param array $attr elements of valid URL
162
     * @param array $repBlocks replacement blocks from valid URL
163
     *
164
     * @return bool|null|string
165
     */
166
    private function buildResult(&$attr, $repBlocks)
167
    {
168
        $result = null;
169
170
        foreach ($repBlocks AS $value) {
171
            if (0 !== strpos($value, '<')) {
172
                $result .= '/'.$value;
173
174
                unset($attr[$value]);
175
            } else {
176
                $element = substr($value, 1, -1);
177
178
                if (!empty($attr[$element])) {
179
                    $result .= '/'.$attr[$element];
180
181
                    unset($attr[$element]);
182
                } else {
183
                    return false;
184
                }
185
            }
186
        }
187
188
        return $result;
189
    }
190
}
191