1 | <?php |
||
2 | |||
3 | namespace Teto\Routing; |
||
4 | |||
5 | use function array_diff; |
||
6 | use function array_fill_keys; |
||
7 | use function array_filter; |
||
8 | use function array_keys; |
||
9 | use function array_values; |
||
10 | use function count; |
||
11 | use function explode; |
||
12 | use function in_array; |
||
13 | use function preg_match; |
||
14 | use function strlen; |
||
15 | use function strpos; |
||
16 | use function substr; |
||
17 | |||
18 | /** |
||
19 | * Action object |
||
20 | * |
||
21 | * @author USAMI Kenta <[email protected]> |
||
22 | * @copyright 2016 BaguetteHQ |
||
23 | * @license http://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 |
||
24 | */ |
||
25 | class Action |
||
26 | { |
||
27 | use \Teto\Object\TypeAssert; |
||
28 | const WILDCARD = '*'; |
||
29 | |||
30 | /** @var string[] */ |
||
31 | public $methods; |
||
32 | /** @var string[] */ |
||
33 | public $split_path; |
||
34 | /** @var array */ |
||
35 | public $param_pos; |
||
36 | /** @var mixed */ |
||
37 | public $value; |
||
38 | /** @var array */ |
||
39 | public $param; |
||
40 | /** @var string */ |
||
41 | public $extension; |
||
42 | /** @var bool */ |
||
43 | public $is_wildcard; |
||
44 | 52 | /** @var array */ |
|
45 | public $available_extensions; |
||
46 | 52 | ||
47 | private static $enum_values = [ |
||
48 | 52 | 'methods' => ['GET', 'POST'], |
|
49 | 52 | ]; |
|
50 | 52 | ||
51 | 52 | /** |
|
52 | 52 | * @param string[] $methods |
|
53 | 52 | * @param string[] $split_path |
|
54 | 52 | * @param array $param_pos |
|
55 | 52 | * @param string[] $available_extensions |
|
56 | 30 | * @param mixed $value |
|
57 | 52 | */ |
|
58 | public function __construct(array $methods, array $split_path, array $param_pos, array $available_extensions, $value) |
||
59 | { |
||
60 | static::assertMethods($methods); |
||
61 | |||
62 | $this->methods = $methods; |
||
63 | $this->split_path = $split_path; |
||
64 | $this->param_pos = $param_pos; |
||
65 | 59 | $this->value = $value; |
|
66 | $this->param = []; |
||
67 | 59 | $this->is_wildcard = in_array(self::WILDCARD, $available_extensions, true); |
|
68 | $this->available_extensions |
||
69 | 59 | = empty($available_extensions) ? ['' => true] |
|
70 | 59 | : array_fill_keys($available_extensions, true) ; |
|
71 | 7 | } |
|
72 | |||
73 | /** |
||
74 | 54 | * @param string $request_method |
|
75 | 42 | * @param string[] $request_path |
|
76 | 7 | * @param string $extension |
|
77 | * @return Action|false |
||
78 | 42 | */ |
|
79 | public function match($request_method, array $request_path, $extension) |
||
80 | { |
||
81 | 54 | $request_len = count($request_path); |
|
82 | 51 | ||
83 | if (!in_array($request_method, $this->methods, true) || |
||
84 | 3 | $request_len !== count($this->split_path)) { |
|
85 | return false; |
||
86 | } |
||
87 | 51 | ||
88 | 15 | if ($this->available_extensions === ['' => true]) { |
|
89 | if (strlen($extension) > 0) { |
||
90 | $request_path[$request_len - 1] .= '.' . $extension; |
||
91 | 36 | } |
|
92 | 36 | $extension = ''; |
|
93 | } |
||
94 | 36 | ||
95 | 34 | if ($this->matchExtension($extension)) { |
|
96 | 20 | $this->extension = $extension; |
|
97 | 20 | } else { |
|
98 | return false; |
||
99 | } |
||
100 | 18 | ||
101 | 18 | if (empty($this->param_pos) && ($request_path === $this->split_path)) { |
|
102 | 18 | return $this; |
|
103 | 18 | } |
|
104 | 22 | ||
105 | 6 | foreach ($this->split_path as $i => $p) { |
|
106 | 24 | $q = $request_path[$i]; |
|
107 | |||
108 | if (isset($this->param_pos[$i])) { |
||
109 | if (!preg_match($p, $q, $matches)) { |
||
110 | 16 | $this->param = []; |
|
111 | return false; |
||
112 | } |
||
113 | |||
114 | $k = $this->param_pos[$i]; |
||
115 | $param_tmp = $this->param; |
||
116 | $param_tmp[$k] = isset($matches[1]) ? $matches[1] : $matches[0]; |
||
117 | 54 | $this->param = $param_tmp; |
|
118 | } elseif ($q !== $p) { |
||
119 | 54 | $this->param = []; |
|
120 | 50 | return false; |
|
121 | } |
||
122 | 4 | } |
|
123 | |||
124 | return $this; |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | * @param string $extension |
||
129 | * @return boolean |
||
130 | */ |
||
131 | public function matchExtension($extension) |
||
132 | 16 | { |
|
133 | if (isset($this->available_extensions[$extension])) { |
||
134 | 16 | return true; |
|
135 | } else { |
||
136 | 16 | return $this->is_wildcard && $extension !== ''; |
|
137 | 11 | } |
|
138 | 11 | } |
|
139 | 11 | ||
140 | /** |
||
141 | 11 | * @param array $param |
|
142 | 4 | * @param string $ext |
|
143 | 4 | * @param boolean $strict |
|
144 | * @return string |
||
145 | */ |
||
146 | public function makePath(array $param, $ext, $strict) |
||
147 | 12 | { |
|
148 | 9 | $path = ''; |
|
149 | 7 | ||
150 | 7 | if ($strict) { |
|
151 | $got_keys = array_keys($param); |
||
152 | $expects = array_values($this->param_pos); |
||
153 | 6 | $diff = array_diff($got_keys, $expects); |
|
154 | |||
155 | 6 | if ($diff !== []) { |
|
156 | 3 | $json = json_encode(array_values($diff)); |
|
157 | throw new \DomainException('unnecessary parameters: ' . $json); |
||
158 | } |
||
159 | 4 | } |
|
160 | |||
161 | foreach ($this->split_path as $i => $pattern) { |
||
162 | 9 | if (!isset($this->param_pos[$i])) { |
|
163 | 1 | $path .= '/' . $pattern; |
|
164 | continue; |
||
165 | } |
||
166 | 9 | ||
167 | $name = $this->param_pos[$i]; |
||
168 | |||
169 | if (!isset($param[$name]) || !preg_match($pattern, $param[$name], $matches)) { |
||
170 | throw new \DomainException("Error"); |
||
171 | } |
||
172 | |||
173 | $path .= '/' . $param[$name]; |
||
174 | } |
||
175 | |||
176 | if ($ext !== null && $ext !== '') { |
||
177 | 24 | $path .= '.' . $ext; |
|
178 | } |
||
179 | 24 | ||
180 | return ($path === '') ? '/' : $path; |
||
181 | 24 | } |
|
182 | |||
183 | 24 | /** |
|
184 | * @param string $method_str ex. "GET|POST" |
||
185 | * @param string $path ex. "/dir_name/path" |
||
186 | * @param mixed $value |
||
187 | * @param string[] $ext |
||
188 | * @param array $params |
||
189 | * @return Action new instance object |
||
190 | */ |
||
191 | 28 | public static function create($method_str, $path, $value, array $ext, array $params = []) |
|
192 | { |
||
193 | 28 | $methods = explode('|', $method_str); |
|
194 | list($split_path, $param_pos) |
||
195 | 28 | = self::parsePathParam($path, $params); |
|
196 | |||
197 | 25 | return new Action($methods, $split_path, $param_pos, $ext, $value); |
|
198 | 25 | } |
|
199 | 25 | ||
200 | 25 | /** |
|
201 | * @param string $path |
||
202 | 25 | * @param array $params |
|
203 | 25 | * @return array [$split_path, $param_pos] |
|
204 | 25 | */ |
|
205 | public static function parsePathParam($path, array $params) |
||
206 | { |
||
207 | 25 | $split_path = array_values(array_filter(explode('/', $path), 'strlen')); |
|
208 | 25 | ||
209 | if (empty($params)) { return [$split_path, []]; } |
||
210 | 25 | ||
211 | 25 | $new_split_path = []; |
|
212 | $param_pos = []; |
||
213 | foreach ($split_path as $i => $p) { |
||
214 | $variable = null; |
||
215 | 25 | ||
216 | if (strpos($p, ':') !== false) { |
||
217 | $v = substr($p, 1); |
||
218 | if (isset($params[$v])) { $variable = $v; } |
||
219 | } |
||
220 | |||
221 | if ($variable === null) { |
||
222 | $new_split_path[] = $p; |
||
223 | } else { |
||
224 | $param_pos[$i] = $variable; |
||
225 | $new_split_path[] = $params[$v]; |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
![]() |
|||
226 | 40 | } |
|
227 | } |
||
228 | 40 | ||
229 | 40 | return [$new_split_path, $param_pos]; |
|
230 | } |
||
231 | 40 | ||
232 | /** |
||
233 | * @param string[] $methods ex. ['GET', 'POST', 'PUT', 'DELETE'] |
||
234 | */ |
||
235 | public static function setHTTPMethod(array $methods) |
||
236 | { |
||
237 | self::$enum_values['methods'] = $methods; |
||
238 | } |
||
239 | |||
240 | protected static function assertMethods(array $methods) |
||
241 | { |
||
242 | foreach ($methods as $m) { |
||
243 | self::assertValue('enum', 'methods', $m, false); |
||
244 | } |
||
245 | } |
||
246 | } |
||
247 |