Router::match()   C
last analyzed

Complexity

Conditions 13
Paths 58

Size

Total Lines 40
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 13.029

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 13
eloc 24
c 4
b 0
f 0
nc 58
nop 2
dl 0
loc 40
ccs 17
cts 18
cp 0.9444
crap 13.029
rs 6.6166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Teto\Routing;
4
5
use function array_filter;
6
use function array_shift;
7
use function array_values;
8
use function count;
9
use function explode;
10
use function implode;
11
use function is_numeric;
12
use function strpos;
13
14
/**
15
 * Router
16
 *
17
 * @author    USAMI Kenta <[email protected]>
18
 * @copyright 2016 BaguetteHQ
19
 * @license   http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0
20
 */
21
class Router
22
{
23
    const _ext = '?ext';
24
    const _sep = "\x1E";
25
26
    /** @var \Teto\Routing\Action[] */
27
    public $variable_actions = [];
28
29
    /** @var \Teto\Routing\Action[][] */
30
    public $fixed_actions = [];
31
32
    /** @var \Teto\Routing\Action[] */
33
    public $named_actions = [];
34
35
    /** @var array */
36
    public $error_action = [];
37
38
    public function __set($name, $value)
39
    {
40 24
        throw new \OutOfRangeException("Unexpected key:'$name'");
41
    }
42 24
43
    /**
44
     * @param  array  $route_map
45
     * @param  string $method
46
     * @param  string $path
47
     * @return \Teto\Routing\Action
48 24
     */
49
    public static function dispatch(array $route_map, $method, $path)
50 24
    {
51 24
        return (new Router($route_map))->match($method, $path);
52 24
    }
53 24
54
    /**
55 24
     * @param array $route_map
56
     */
57
    public function __construct(array $route_map)
58
    {
59
        foreach ($route_map as $k => $m) {
60
            ($k !== '#404')
61
                ? $this->setAction($k, $m)
62 48
                : $this->setSpecialAction($k, $m);
63
        }
64 48
    }
65 48
66
    /**
67
     * @param   string $method
68
     * @param   string $path
69 48
     * @return  \Teto\Routing\Action
70 48
     */
71
    public function match($method, $path)
72 48
    {
73
        if ($method === 'HEAD') { $method = 'GET'; }
74 48
        if (strpos($path, '//') !== false || strpos($path, self::_sep) !== false) {
75 40
            return $this->getNotFoundAction($method, $path);
76 40
        }
77 8
78 8
        $split_path = array_values(array_filter(explode('/', $path), 'strlen'));
79
        $count = count($split_path);
80
81
        $ext  = '';
82
83
        if ($count > 0) {
84
            $file = explode('.', $split_path[$count - 1], 2);
85 48
            if (isset($file[1]) && strlen($file[1]) > 0) {
86 48
                if (strlen($file[1]) > 0) {
87 14
                    list($split_path[$count - 1], $ext) = $file;
88 14
                } else {
89 14
                    $split_path[$count - 1] .= '.';
90
                }
91
            }
92
        }
93 34
94 30
        $fixed_key = implode(self::_sep, $split_path);
95 30
        if (isset($this->fixed_actions[$fixed_key][$method])) {
96 30
            $action = $this->fixed_actions[$fixed_key][$method];
97
            if ($matched = $action->match($method, $split_path, $ext)) {
98
                return $matched;
99
            }
100
        }
101 24
102
        if (isset($this->variable_actions[$count])) {
103
            foreach ($this->variable_actions[$count] as $action) {
104
                if ($matched = $action->match($method, $split_path, $ext)) {
105
                    return $matched;
106
                }
107
            }
108
        }
109 24
110
        return $this->getNotFoundAction($method, $path);
111 24
    }
112
113 24
    /**
114 24
     * @param   string $method
115 24
     * @param   string $path
116 24
     * @return  \Teto\Routing\Action
117 24
     */
118 24
    public function getNotFoundAction($method, $path)
119
    {
120
        $split_path = array_values(array_filter(explode('/', $path), 'strlen'));
121
122
        return new NotFoundAction(
123
            [$method],
124
            $split_path,
125
            [],
126 24
            [],
127
            $this->error_action['#404']
128 24
        );
129 24
    }
130 24
131
    /**
132 24
     * @param int|string $key
133
     * @param array      $action_tuple
134
     */
135 24
    public function setAction($key, array $action_tuple)
136 24
    {
137 24
        if (isset($action_tuple[self::_ext])) {
138 24
            $ext = $action_tuple[self::_ext];
139 24
            unset($action_tuple[self::_ext]);
140
        } else {
141 24
            $ext = [];
142 24
        }
143 24
144 24
        $method = array_shift($action_tuple);
145
        $path   = array_shift($action_tuple);
146 24
        $value  = array_shift($action_tuple) ?: true ;
147
        $params = array_shift($action_tuple) ?: [] ;
148 24
        $action = Action::create($method, $path, $value, $ext, $params);
149 24
150 24
        if (!empty($action->param_pos)) {
151
            $count  = count($action->split_path);
152
            if (!isset($this->variable_actions[$count])) {
153
                $this->variable_actions[$count] = [];
154 24
            }
155 24
            $this->variable_actions[$count][] = $action;
156
        } else {
157 24
            $fixed_key = implode(self::_sep, $action->split_path);
158
            foreach ($action->methods as $m) {
159
                $this->fixed_actions[$fixed_key][$m] = $action;
160
            }
161
        }
162
163 24
        if (!is_numeric($key)) {
164
            $this->named_actions[$key] = $action;
165 24
        }
166 24
    }
167
168
    /**
169
     * @param string $name
170
     * @param mixed  $value
171
     */
172
    public function setSpecialAction($name, $value)
173 15
    {
174
        $this->error_action[$name] = $value;
175 15
    }
176
177
    /**
178
     * @param string  $name
179 15
     * @param array   $param
180 1
     * @param boolean $strict
181 1
     */
182
    public function makePath($name, array $param = [], $strict = false)
183 14
    {
184
        if (empty($this->named_actions[$name])) {
185
            throw new \OutOfRangeException("\"$name\" is not exists.");
186 15
        }
187
188
        if (isset($param[self::_ext])) {
189
            $ext = $param[self::_ext];
190
            unset($param[self::_ext]);
191
        } else {
192
            $ext = null;
193
        }
194
195
        return $this->named_actions[$name]->makePath($param, $ext, $strict);
196
    }
197
}
198