Router   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 175
Duplicated Lines 0 %

Test Coverage

Coverage 93.15%

Importance

Changes 7
Bugs 0 Features 0
Metric Value
eloc 71
c 7
b 0
f 0
dl 0
loc 175
ccs 68
cts 73
cp 0.9315
rs 9.92
wmc 31

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getNotFoundAction() 0 10 1
A __set() 0 3 1
A dispatch() 0 3 1
A setSpecialAction() 0 3 1
A makePath() 0 14 3
B setAction() 0 30 8
C match() 0 40 13
A __construct() 0 6 3
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