1 <?php
2 3 4 5 6 7
8
9 namespace Cross\Core;
10
11 use Cross\Exception\CoreException;
12 use Cross\Http\Request;
13 use ReflectionFunction;
14 use Closure;
15
16 17 18 19 20
21 class Rest
22 {
23 24 25
26 protected $rules;
27
28 29 30
31 protected $request;
32
33 34 35
36 protected $delegate;
37
38 39 40
41 protected $request_type;
42
43 44 45
46 protected $request_string;
47
48 49 50
51 protected $custom_router_config = array();
52
53 54 55
56 private static $instance;
57
58 59 60 61 62 63
64 private function __construct(Delegate &$delegate)
65 {
66 $this->delegate = $delegate;
67 $this->request = $delegate->getRequest();
68 $this->request_type = strtoupper($this->request->getRequestType());
69 $this->request_string = $delegate->getRouter()->getUriRequest('/', $useless, false, false);
70 }
71
72 73 74 75 76 77 78
79 static function getInstance(Delegate &$delegate)
80 {
81 if (!self::$instance) {
82 self::$instance = new Rest($delegate);
83 }
84
85 return self::$instance;
86 }
87
88 89 90 91 92 93
94 function get($custom_router, Closure $process_closure)
95 {
96 $this->addCustomRouter('GET', $custom_router, $process_closure);
97 }
98
99 100 101 102 103 104
105 function post($custom_router, Closure $process_closure)
106 {
107 $this->addCustomRouter('POST', $custom_router, $process_closure);
108 }
109
110 111 112 113 114 115
116 function put($custom_router, Closure $process_closure)
117 {
118 $this->addCustomRouter('PUT', $custom_router, $process_closure);
119 }
120
121 122 123 124 125 126
127 function patch($custom_router, Closure $process_closure)
128 {
129 $this->addCustomRouter('PATCH', $custom_router, $process_closure);
130 }
131
132 133 134 135 136 137
138 function options($custom_router, Closure $process_closure)
139 {
140 $this->addCustomRouter('OPTIONS', $custom_router, $process_closure);
141 }
142
143 144 145 146 147 148
149 function delete($custom_router, Closure $process_closure)
150 {
151 $this->addCustomRouter('DELETE', $custom_router, $process_closure);
152 }
153
154 155 156 157 158 159
160 function head($custom_router, Closure $process_closure)
161 {
162 $this->addCustomRouter('HEAD', $custom_router, $process_closure);
163 }
164
165 166 167 168 169 170
171 function any($custom_router, Closure $process_closure)
172 {
173 $this->addCustomRouter($this->request_type, $custom_router, $process_closure);
174 }
175
176 177 178 179 180 181
182 function on($name, Closure $f)
183 {
184 $this->delegate->on($name, $f);
185 }
186
187 188 189 190 191
192 function rules(array $rules)
193 {
194 $this->rules = $rules;
195 }
196
197 198 199 200 201
202 function run()
203 {
204 $match = false;
205 $params = array();
206 $process_closure = null;
207 if (!empty($this->custom_router_config[$this->request_type])) {
208 $custom_routers = $this->custom_router_config[$this->request_type];
209 if (!empty($custom_routers['high']) && isset($custom_routers['high'][$this->request_string])) {
210 $match = true;
211 $process_closure = $custom_routers['high'][$this->request_string];
212 } elseif (!empty($custom_routers['current'])) {
213 $match = $this->matchProcess($custom_routers['current'], $process_closure, $params);
214 } elseif (!empty($custom_routers['global'])) {
215 $match = $this->matchProcess($custom_routers['global'], $process_closure, $params);
216 }
217 }
218
219 if ($match && $process_closure !== null) {
220 $this->response($process_closure, $params);
221 } else {
222 $closure_container = $this->delegate->getClosureContainer();
223 if ($closure_container->has('mismatching')) {
224 $closure_container->run('mismatching');
225 } else {
226 throw new CoreException('Not match uri');
227 }
228 }
229 }
230
231 232 233 234 235 236 237 238
239 private function matchProcess(array $routers, & $process_closure, & $params)
240 {
241 uasort($routers, function ($a, $b) {
242 return $a['params_count'] < $b['params_count'];
243 });
244
245 foreach ($routers as $router => $router_config) {
246 $params = array();
247 if (true === $this->matchCustomRouter($router, $router_config['params_key'], $params)) {
248 $process_closure = $router_config['process_closure'];
249 return true;
250 }
251 }
252
253 return false;
254 }
255
256 257 258 259 260 261 262 263
264 private function matchCustomRouter($custom_router, array $params_keys = array(), array & $params = array())
265 {
266 $request_uri_string = $this->request_string;
267 $custom_router_params_token = preg_replace("/\{:(.*?)\}/", '{PARAMS}', $custom_router);
268 while (strlen($custom_router_params_token) > 0) {
269 $defined_params_pos = strpos($custom_router_params_token, '{PARAMS}');
270 if ($defined_params_pos) {
271 $compare_ret = substr_compare($custom_router_params_token, $request_uri_string, 0, $defined_params_pos);
272 } else {
273 $compare_ret = strcmp($custom_router_params_token, $request_uri_string);
274 }
275
276 if ($compare_ret !== 0) {
277 return false;
278 }
279
280
281 $custom_router_params_token = substr($custom_router_params_token, $defined_params_pos + 8);
282 $request_uri_string = substr($request_uri_string, $defined_params_pos);
283
284 if ($custom_router_params_token) {
285
286 $next_defined_dot_pos = strpos($request_uri_string, $custom_router_params_token[0]);
287 $params_value = substr($request_uri_string, 0, $next_defined_dot_pos);
288 $request_uri_string = substr($request_uri_string, $next_defined_dot_pos);
289 } else {
290 $params_value = $request_uri_string;
291 }
292
293 $key_name = array_shift($params_keys);
294 if ($key_name && isset($this->rules[$key_name]) && !preg_match($this->rules[$key_name], $params_value)) {
295 return false;
296 }
297
298 if ($key_name) {
299 $params[$key_name] = $params_value;
300 }
301 }
302
303 return true;
304 }
305
306 307 308 309 310 311 312
313 private function response(Closure $process_closure, array $params = array())
314 {
315 try {
316 $ref = new ReflectionFunction($process_closure);
317 $closure_params = array();
318 $parameters = $ref->getParameters();
319 if (!empty($parameters)) {
320 foreach ($parameters as $p) {
321 if (!isset($params[$p->name])) {
322 throw new CoreException("未指定的参数: {$p->name}");
323 }
324
325 $closure_params[$p->name] = $params[$p->name];
326 }
327 }
328
329 $content = call_user_func_array($process_closure, $closure_params);
330 if (null != $content) {
331 $this->delegate->getResponse()->display($content);
332 }
333 } catch (\Exception $e) {
334 throw new CoreException('Reflection ' . $e->getMessage());
335 }
336 }
337
338 339 340 341 342 343 344
345 private function addCustomRouter($request_type, $custom_router, Closure $process_closure)
346 {
347 if ($this->request_type === $request_type) {
348 $custom_router = trim($custom_router);
349 $is_contain_params = preg_match_all("/(.*?)\{:(.*?)\}/", $custom_router, $params_keys);
350 if ($is_contain_params) {
351 $prefix_string_length = strlen($params_keys[1][0]);
352 $compare = substr_compare($this->request_string, $custom_router, 0, $prefix_string_length);
353 if ($compare === 0) {
354 $level = 'current';
355 if ($prefix_string_length == 1) {
356 $level = 'global';
357 }
358
359 $this->custom_router_config[$request_type][$level][$custom_router] = array(
360 'process_closure' => $process_closure,
361 'params_count' => count($params_keys[2]),
362 'params_key' => $params_keys[2],
363 );
364 }
365 } else {
366 $this->custom_router_config[$request_type]['high'][$custom_router] = $process_closure;
367 }
368 }
369 }
370 }
371