1 <?php
2 3 4 5 6 7
8
9 namespace Cross\Core;
10
11 use Cross\I\RequestCacheInterface;
12 use Cross\I\RouterInterface;
13 use Cross\Exception\CoreException;
14 use Cross\Cache\Driver\FileCacheDriver;
15 use Cross\Cache\Request\Memcache;
16 use Cross\Cache\Request\RedisCache;
17 use Cross\Cache\RequestCache;
18 use ReflectionMethod;
19 use ReflectionClass;
20 use Exception;
21 use Closure;
22
23 24 25 26 27
28 class Application
29 {
30 31 32 33 34
35 protected $action;
36
37 38 39 40 41
42 protected $params;
43
44 45 46 47 48
49 protected $controller;
50
51 52 53 54 55
56 private $app_name;
57
58 59 60 61 62
63 private $action_annotate;
64
65 66 67 68 69
70 private $ob_cache_status = true;
71
72 73 74
75 private $delegate;
76
77 78 79
80 private $config;
81
82 83 84 85 86 87
88 function __construct($app_name, Delegate &$delegate)
89 {
90 $this->app_name = $app_name;
91 $this->delegate = $delegate;
92 $this->config = $delegate->getConfig();
93 }
94
95 96 97 98 99 100 101 102 103
104 public function dispatcher($router, $args = array(), $return_response_content = false)
105 {
106 $init_prams = true;
107 $router = $this->parseRouter($router, $args, $init_prams);
108 $cr = $this->initController($router['controller'], $router['action']);
109
110 $closureContainer = $this->delegate->getClosureContainer();
111 $annotate_config = $this->getAnnotateConfig();
112
113 $action_params = array();
114 if (isset($annotate_config['params'])) {
115 $action_params = &$annotate_config['params'];
116 }
117
118 if ($init_prams) {
119 $this->initParams($router['params'], $action_params);
120 } elseif (is_array($router['params'])) {
121 $params = $router['params'] + $action_params;
122 $this->setParams($params);
123 } else {
124 $this->setParams($router['params']);
125 }
126
127 $closureContainer->run('dispatcher');
128
129 if (!empty($annotate_config['basicAuth'])) {
130 $this->delegate->getResponse()->basicAuth($annotate_config['basicAuth']);
131 }
132
133 $cache = false;
134 if (isset($annotate_config['cache'])) {
135 $cache = $this->initRequestCache($annotate_config['cache'], $action_params);
136 }
137
138 if ($cache && $cache->isValid()) {
139 $response_content = $cache->get();
140 } else {
141 $action = $this->getAction();
142 $controller_name = $this->getController();
143
144 $runtime_config = array(
145 'action_annotate' => $annotate_config,
146 'view_controller_namespace' => $this->getViewControllerNameSpace($controller_name),
147 'controller' => $controller_name,
148 'action' => $action,
149 'params' => $this->getParams(),
150 );
151
152 $closureContainer->add('~controller~runtime~', function () use (&$runtime_config) {
153 return $runtime_config;
154 });
155
156 try {
157 $cr->setStaticPropertyValue('app_delegate', $this->delegate);
158 $controller = $cr->newInstance();
159 } catch (Exception $e) {
160 throw new CoreException($e->getMessage(), 200, $e);
161 }
162
163 if ($this->delegate->getResponse()->isEndFlush()) {
164 return true;
165 }
166
167 if (isset($annotate_config['before'])) {
168 $this->callReliesControllerClosure($annotate_config['before'], $controller);
169 }
170
171 if ($this->ob_cache_status) {
172 ob_start();
173 $response_content = $controller->$action();
174 if (!$response_content) {
175 $response_content = ob_get_contents();
176 }
177 ob_end_clean();
178 } else {
179 $response_content = $controller->$action();
180 }
181
182 if ($cache) {
183 $cache->set($response_content);
184 }
185 }
186
187 if (!empty($annotate_config['response'])) {
188 $this->setResponseConfig($annotate_config['response']);
189 }
190
191 if ($return_response_content) {
192 return $response_content;
193 } else {
194 $this->delegate->getResponse()->display($response_content);
195 }
196
197 if (isset($annotate_config['after']) && isset($controller)) {
198 $this->callReliesControllerClosure($annotate_config['after'], $controller);
199 }
200
201 return true;
202 }
203
204 205 206 207 208
209 function setController($controller)
210 {
211 $this->controller = $controller;
212 }
213
214 215 216 217 218
219 function setAction($action)
220 {
221 $this->action = $action;
222 }
223
224 225 226 227 228
229 function setParams($params)
230 {
231 $paramsChecker = $this->delegate->getClosureContainer()->has('setParams', $closure);
232 if ($paramsChecker && is_array($params)) {
233 array_walk($params, $closure);
234 } elseif ($paramsChecker) {
235 call_user_func($closure, $params);
236 }
237
238 $this->params = $params;
239 }
240
241 242 243 244 245
246 public function setObStatus($status)
247 {
248 $this->ob_cache_status = (bool)$status;
249 }
250
251 252 253 254 255
256 function getController()
257 {
258 return $this->controller;
259 }
260
261 262 263 264 265
266 function getAction()
267 {
268 return $this->action;
269 }
270
271 272 273 274 275
276 function getParams()
277 {
278 return $this->params;
279 }
280
281 282 283 284 285
286 function getAnnotateConfig()
287 {
288 return $this->action_annotate;
289 }
290
291 292 293 294 295 296 297 298 299 300 301 302
303 public function instanceClass($class, $args = array())
304 {
305 try {
306 $rc = new ReflectionClass($class);
307
308 if ($rc->hasProperty('app_delegate')) {
309 $rc->setStaticPropertyValue('app_delegate', $this->delegate);
310 }
311
312 if ($rc->hasMethod('__construct')) {
313 if (!is_array($args)) {
314 $args = array($args);
315 }
316
317 return $rc->newInstanceArgs($args);
318 }
319
320 return $rc->newInstance();
321 } catch (\ReflectionException $e) {
322 return false;
323 }
324 }
325
326 327 328 329 330 331 332 333
334 public static function combineParamsAnnotateConfig(array $params = array(), array $annotate_params = array(), $op_mode = 1)
335 {
336 if (empty($params)) {
337 return $annotate_params;
338 }
339
340 if (!empty($annotate_params)) {
341 $params_set = array();
342 foreach ($annotate_params as $params_name => $default_value) {
343 if ($op_mode == 1) {
344 $params_value = array_shift($params);
345 } else {
346 if (isset($params[$params_name])) {
347 $params_value = $params[$params_name];
348 } else {
349 $params_value = $default_value;
350 }
351 }
352
353 if ($params_value != '') {
354 $params_set[$params_name] = $params_value;
355 } else {
356 $params_set[$params_name] = $default_value;
357 }
358 }
359 return $params_set;
360 }
361
362 return $params;
363 }
364
365 366 367 368 369 370 371
372 public static function stringParamsToAssociativeArray($stringParams, $separator)
373 {
374 return self::oneDimensionalToAssociativeArray(explode($separator, $stringParams));
375 }
376
377 378 379 380 381 382
383 public static function oneDimensionalToAssociativeArray(array $oneDimensional)
384 {
385 $result = array();
386 while ($p = array_shift($oneDimensional)) {
387 $result[$p] = array_shift($oneDimensional);
388 }
389
390 return $result;
391 }
392
393 394 395 396 397 398 399 400 401 402 403 404
405 private function parseRouter($router, $params = array(), &$init_params = true)
406 {
407 if ($router instanceof RouterInterface) {
408 $controller = $router->getController();
409 $action = $router->getAction();
410 $params = $router->getParams();
411 } elseif (is_array($router)) {
412 $init_params = false;
413 $controller = $router['controller'];
414 $action = $router['action'];
415 } else {
416 $init_params = false;
417 if (strpos($router, ':')) {
418 list($controller, $action) = explode(':', $router);
419 } else {
420 $controller = $router;
421 $action = Router::DEFAULT_ACTION;
422 }
423 }
424
425 return array('controller' => ucfirst($controller), 'action' => $action, 'params' => $params);
426 }
427
428 429 430 431 432 433
434 protected function getControllerNamespace($controller_name)
435 {
436 return 'app\\' . str_replace('/', '\\', $this->app_name) . '\\controllers\\' . $controller_name;
437 }
438
439 440 441 442 443 444
445 protected function getViewControllerNameSpace($controller_name)
446 {
447 return 'app\\' . str_replace('/', '\\', $this->app_name) . '\\views\\' . $controller_name . 'View';
448 }
449
450 451 452 453 454 455 456 457
458 private function initController($controller, $action = null)
459 {
460 $controller_name_space = $this->getControllerNamespace($controller);
461
462 try {
463 $class_reflection = new ReflectionClass($controller_name_space);
464 if ($class_reflection->isAbstract()) {
465 throw new CoreException("{$controller_name_space} 不允许访问的控制器");
466 }
467 } catch (Exception $e) {
468 throw new CoreException($e->getMessage());
469 }
470
471 $this->setController($controller);
472
473 $controller_annotate = array();
474 $class_annotate_content = $class_reflection->getDocComment();
475 if ($class_annotate_content) {
476 $controller_annotate = Annotate::getInstance($this->delegate)->parse($class_annotate_content);
477 }
478
479 if ($action) {
480 try {
481 $is_callable = new ReflectionMethod($controller_name_space, $action);
482 } catch (Exception $e) {
483 try {
484 $is_callable = new ReflectionMethod($controller_name_space, '__call');
485 } catch (Exception $e) {
486 throw new CoreException("{$controller_name_space}->{$action} 不能解析的请求");
487 }
488 }
489
490 if (isset($is_callable) && $is_callable->isPublic() && true !== $is_callable->isAbstract()) {
491 $this->setAction($action);
492
493 $this->setAnnotateConfig(Annotate::getInstance($this->delegate)->parse($is_callable->getDocComment()), $controller_annotate);
494 } else {
495 throw new CoreException("{$controller_name_space}->{$action} 不允许访问的方法");
496 }
497 }
498
499 return $class_reflection;
500 }
501
502 503 504 505 506 507
508 private function initParams($url_params, array $annotate_params = array())
509 {
510 $url_type = $this->config->get('url', 'type');
511 switch ($url_type) {
512 case 1:
513 case 5:
514 $params = self::combineParamsAnnotateConfig($url_params, $annotate_params);
515 break;
516
517 case 3:
518 case 4:
519 $url_params = self::oneDimensionalToAssociativeArray($url_params);
520 if (!empty($annotate_params)) {
521 $params = self::combineParamsAnnotateConfig($url_params, $annotate_params, 2);
522 } else {
523 $params = $url_params;
524 }
525 break;
526
527 default:
528 if (empty($url_params)) {
529 $params = $annotate_params;
530 } elseif (is_array($url_params) && !empty($annotate_params)) {
531 $params = array_merge($annotate_params, $url_params);
532 } else {
533 $params = $url_params;
534 }
535 }
536
537 $this->setParams($params);
538 }
539
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
557 private function initRequestCache(array $request_cache_config, array $annotate_params)
558 {
559 if (empty($request_cache_config[0])) {
560 return false;
561 }
562
563 if (!isset($request_cache_config[1]) || !is_array($request_cache_config[1])) {
564 throw new CoreException('请求缓存配置格式不正确');
565 }
566
567 if (empty($request_cache_config[2]) && !$this->delegate->getRequest()->isGetRequest()) {
568 return false;
569 }
570
571 $display_type = $this->config->get('sys', 'display');
572 $this->delegate->getResponse()->setContentType($display_type);
573
574 $default_cache_config = array(
575 'type' => 1,
576 'expire_time' => 3600,
577 'ignore_params' => false,
578 'cache_path' => $this->config->get('path', 'cache') . 'request' . DIRECTORY_SEPARATOR,
579 'key_dot' => DIRECTORY_SEPARATOR
580 );
581
582 $cache_config = &$request_cache_config[1];
583 foreach ($default_cache_config as $default_config_key => $default_value) {
584 if (!isset($cache_config[$default_config_key])) {
585 $cache_config[$default_config_key] = $default_value;
586 }
587 }
588
589 $params_cache_key = '';
590 $params = $this->getParams();
591 if (!$cache_config['ignore_params'] && !empty($params)) {
592 $params_member = &$params;
593 if (!empty($annotate_params)) {
594 foreach ($annotate_params as $k => &$v) {
595 if (isset($params[$k])) {
596 $v = $params[$k];
597 }
598 }
599 $params_member = $annotate_params;
600 }
601
602 $params_cache_key = md5(json_encode($params_member));
603 }
604
605 $cache_key = array(
606 'app_name' => $this->app_name,
607 'tpl_dir_name' => $this->config->get('sys', 'default_tpl_dir'),
608 'controller' => lcfirst($this->getController()),
609 'action' => $this->getAction()
610 );
611
612 $cache_config['key'] = implode($cache_config['key_dot'], $cache_key);
613 if ($params_cache_key) {
614 $cache_config['key'] .= '@' . $params_cache_key;
615 }
616
617 $closureContainer = $this->delegate->getClosureContainer();
618 $has_cache_closure = $closureContainer->has('cpCache');
619 if ($has_cache_closure) {
620 $cache_config['params'] = $params;
621 $cache_config['cache_key'] = $cache_key;
622 $cache_config['annotate_params'] = $annotate_params;
623 $enable_cache = $closureContainer->run('cpCache', array(&$cache_config));
624 unset($cache_config['cache_key_config'], $cache_config['params'], $cache_config['annotate_params']);
625 } else {
626 $enable_cache = $request_cache_config[0];
627 }
628
629 if ($enable_cache) {
630 return RequestCache::factory($cache_config['type'], $cache_config);
631 }
632
633 return false;
634 }
635
636 637 638 639 640
641 private function setResponseConfig(array $config)
642 {
643 if (isset($config['content_type'])) {
644 $this->delegate->getResponse()->setContentType($config['content_type']);
645 }
646
647 if (isset($config['status'])) {
648 $this->delegate->getResponse()->setResponseStatus($config['status']);
649 }
650 }
651
652 653 654 655 656 657
658 private function callReliesControllerClosure(Closure $closure, $controller)
659 {
660 $closure($controller);
661 }
662
663 664 665 666 667 668
669 private function setAnnotateConfig(array $annotate, array $controller_annotate)
670 {
671 if (empty($controller_annotate)) {
672 $this->action_annotate = $annotate;
673 } else {
674 $this->action_annotate = array_merge($controller_annotate, $annotate);
675 }
676 }
677 }
678
679