Passed
Push — master ( 076438...996d22 )
by Oleg
03:39
created

Router::buildResult()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 24
rs 8.6845
cc 4
eloc 14
nc 4
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/lugnsk/micro
12
 * @copyright Copyright &copy; 2013 Oleg Lunegov
13
 * @license /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 $config
31
     *
32
     * @result void
33
     */
34
    public function __construct(array $config = [])
35
    {
36
        $this->routes = $config['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
        $attributes = $this->parseUri($uriBlocks, $patBlocks);
98
        if (!$attributes) {
99
            return false;
100
        }
101
102
        $result = $this->buildResult($attributes, $repBlocks);
103
        if ($result === null || $result === false) {
104
            return false;
105
        }
106
107
        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...
108
            return $result;
109
        }
110
111
        $result .= '?';
112
        foreach ($attributes AS $key => $val) {
113
            if ($key !== $val) {
114
                $result .= '&' . $key . '=' . $val;
115
            }
116
        }
117
118
        return $result;
119
    }
120
121
    /**
122
     * Match patBlocks in uriBlocks
123
     *
124
     * @access private
125
     *
126
     * @param array $uriBlocks uri blocks from URL
127
     * @param array $patBlocks pattern blocks from valid URL
128
     *
129
     * @return array|bool
130
     */
131
    private function parseUri(array $uriBlocks = [], array $patBlocks = [])
132
    {
133
        $attr = [];
134
135
        foreach (range(0, count($uriBlocks) - 1) AS $i) {
136
            if (0 === strpos($patBlocks[$i], '<')) {
137
                $cut = strpos($patBlocks[$i], ':');
138
139
                if (preg_match('/' . substr($patBlocks[$i], $cut + 1, -1) . '/', $uriBlocks[$i])) {
140
                    $attr[substr($patBlocks[$i], 1, $cut - 1)] = $uriBlocks[$i];
141
                } else {
142
                    return false;
143
                }
144
145
            } elseif ($uriBlocks[$i] !== $patBlocks[$i]) {
146
                return false;
147
            } else {
148
                $attr[$uriBlocks[$i]] = $patBlocks[$i];
149
            }
150
        }
151
152
        return $attr;
153
    }
154
155
    /**
156
     * Replacement $result with repBlocks
157
     *
158
     * @access private
159
     *
160
     * @param array $attr elements of valid URL
161
     * @param array $repBlocks replacement blocks from valid URL
162
     *
163
     * @return bool|null|string
164
     */
165
    private function buildResult(&$attr, $repBlocks)
166
    {
167
        $result = null;
168
169
        foreach ($repBlocks AS $value) {
170
            if (0 !== strpos($value, '<')) {
171
                $result .= '/' . $value;
172
173
                unset($attr[$value]);
174
            } else {
175
                $element = substr($value, 1, -1);
176
177
                if (!empty($attr[$element])) {
178
                    $result .= '/' . $attr[$element];
179
180
                    unset($attr[$element]);
181
                } else {
182
                    return false;
183
                }
184
            }
185
        }
186
187
        return $result;
188
    }
189
}
190