CrossPHP
  • Namespace
  • Class
  • Download

Namespaces

  • Cross
    • Auth
    • Cache
      • Driver
      • Request
    • Core
    • DB
      • Connecter
      • Drivers
      • SQLAssembler
    • Exception
    • Http
    • I
    • Lib
      • Document
    • Module
    • MVC
    • Runtime
  • None

Classes

  • Cross\Auth\CookieAuth
  • Cross\Auth\SessionAuth
  • Cross\Cache\Driver\FileCacheDriver
  • Cross\Cache\Driver\MemcacheDriver
  • Cross\Cache\Driver\RedisDriver
  • Cross\Cache\Request\FileCache
  • Cross\Cache\Request\Memcache
  • Cross\Cache\Request\RedisCache
  • Cross\Cache\RequestCache
  • Cross\Core\Annotate
  • Cross\Core\Application
  • Cross\Core\ArrayMap
  • Cross\Core\Config
  • Cross\Core\CrossArray
  • Cross\Core\Delegate
  • Cross\Core\FrameBase
  • Cross\Core\Helper
  • Cross\Core\HttpAuth
  • Cross\Core\Loader
  • Cross\Core\Rest
  • Cross\Core\Router
  • Cross\DB\Connecter\BaseConnecter
  • Cross\DB\Connecter\MySQLConnecter
  • Cross\DB\Connecter\PgSQLConnecter
  • Cross\DB\Connecter\SQLiteConnecter
  • Cross\DB\DBFactory
  • Cross\DB\Drivers\CouchDriver
  • Cross\DB\Drivers\MongoDriver
  • Cross\DB\Drivers\PDOSqlDriver
  • Cross\DB\SQLAssembler\MySQLAssembler
  • Cross\DB\SQLAssembler\PgSQLAssembler
  • Cross\DB\SQLAssembler\SQLAssembler
  • Cross\DB\SQLAssembler\SQLiteAssembler
  • Cross\Http\Request
  • Cross\Http\Response
  • Cross\Lib\Array2XML
  • Cross\Lib\Document\CallTree
  • Cross\Lib\Document\CallTreeToHTML
  • Cross\Lib\Document\HTML
  • Cross\Lib\StringToPHPStream
  • Cross\Module\SQLModule
  • Cross\MVC\Controller
  • Cross\MVC\Module
  • Cross\MVC\View
  • Cross\Runtime\ClosureContainer

Interfaces

  • Cross\I\CacheInterface
  • Cross\I\HttpAuthInterface
  • Cross\I\PDOConnecter
  • Cross\I\RequestCacheInterface
  • Cross\I\RouterInterface
  • Cross\I\SqlInterface

Exceptions

  • Cross\Exception\CacheException
  • Cross\Exception\CoreException
  • Cross\Exception\CrossException
  • Cross\Exception\FrontException

Functions

  • ascLogo
  • line
  • tBody
  • th
  • tHead
  1 <?php
  2 /**
  3  * Cross - a micro PHP 5 framework
  4  *
  5  * @link        http://www.crossphp.com
  6  * @license     MIT License
  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  * @author wonli <wonli@live.com>
 25  * Class Application
 26  * @package Cross\Core
 27  */
 28 class Application
 29 {
 30     /**
 31      * action 名称
 32      *
 33      * @var string
 34      */
 35     protected $action;
 36 
 37     /**
 38      * 运行时的参数
 39      *
 40      * @var mixed
 41      */
 42     protected $params;
 43 
 44     /**
 45      * 控制器名称
 46      *
 47      * @var string
 48      */
 49     protected $controller;
 50 
 51     /**
 52      * 当前app名称
 53      *
 54      * @var string
 55      */
 56     private $app_name;
 57 
 58     /**
 59      * action 注释
 60      *
 61      * @var string
 62      */
 63     private $action_annotate;
 64 
 65     /**
 66      * 输出缓冲状态
 67      *
 68      * @var bool
 69      */
 70     private $ob_cache_status = true;
 71 
 72     /**
 73      * @var Delegate
 74      */
 75     private $delegate;
 76 
 77     /**
 78      * @var Config
 79      */
 80     private $config;
 81 
 82     /**
 83      * 实例化Application
 84      *
 85      * @param string $app_name
 86      * @param Delegate $delegate
 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      * @param object|string $router
 99      * @param array|string $args 指定参数
100      * @param bool $return_response_content 是否输出执行结果
101      * @return array|mixed|string
102      * @throws CoreException
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      * 设置controller
206      *
207      * @param $controller
208      */
209     function setController($controller)
210     {
211         $this->controller = $controller;
212     }
213 
214     /**
215      * 设置action
216      *
217      * @param $action
218      */
219     function setAction($action)
220     {
221         $this->action = $action;
222     }
223 
224     /**
225      * 设置params
226      *
227      * @param array|string $params
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      * @param mixed $status
245      */
246     public function setObStatus($status)
247     {
248         $this->ob_cache_status = (bool)$status;
249     }
250 
251     /**
252      * 获取控制器名称
253      *
254      * @return mixed
255      */
256     function getController()
257     {
258         return $this->controller;
259     }
260 
261     /**
262      * 获取action名称
263      *
264      * @return string
265      */
266     function getAction()
267     {
268         return $this->action;
269     }
270 
271     /**
272      * 获取参数
273      *
274      * @return mixed
275      */
276     function getParams()
277     {
278         return $this->params;
279     }
280 
281     /**
282      * 获取action注释配置
283      *
284      * @return array|bool
285      */
286     function getAnnotateConfig()
287     {
288         return $this->action_annotate;
289     }
290 
291     /**
292      * 实例化内部类
293      * <pre>
294      * 判断类中是否包含静态成员变量app_delegate并赋值
295      * 主要用于实例化Cross\MVC\Module, Cross\MVC\View命名空间下的派生类
296      * 不能实例化控制器, 实例化控制器请调用本类中的get()方法
297      * </pre>
298      *
299      * @param string $class 类名或命名空间
300      * @param array $args
301      * @return object|bool
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      * @param array $params
330      * @param array $annotate_params
331      * @param int $op_mode 处理参数的方式
332      * @return array
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      * @param string $stringParams
369      * @param string $separator
370      * @return array
371      */
372     public static function stringParamsToAssociativeArray($stringParams, $separator)
373     {
374         return self::oneDimensionalToAssociativeArray(explode($separator, $stringParams));
375     }
376 
377     /**
378      * 一维数组按顺序转换为关联数组
379      *
380      * @param array $oneDimensional
381      * @return array
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      * 解析router
395      * <pre>
396      * router类型为字符串时, 第二个参数生效
397      * 当router类型为数组或字符串时,dispatcher中不再调用initParams()
398      * </pre>
399      *
400      * @param RouterInterface|string $router
401      * @param array $params
402      * @param bool $init_params
403      * @return array
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      * @param string $controller_name
432      * @return string
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      * @param string $controller_name
443      * @return string
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      * @param string $controller 控制器
454      * @param string $action 动作
455      * @return ReflectionClass
456      * @throws CoreException
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                 //获取Action的注释配置
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      * @param array|string $url_params
506      * @param array $annotate_params
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      * <pre>
543      * request_cache_config 共接受3个参数
544      * 1 缓存开关
545      * 2 缓存配置数组
546      * 3 是否强制开启请求缓存(忽略HTTP请求类型检查)
547      *
548      * 请求类型验证优先级大于缓存开关
549      * 注册匿名函数cpCache可以更灵活的控制请求缓存
550      * </pre>
551      *
552      * @param array $request_cache_config
553      * @param array $annotate_params
554      * @return bool|FileCacheDriver|Memcache|RedisCache|RequestCacheInterface|object
555      * @throws CoreException
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      * 设置Response
638      *
639      * @param array $config
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      * @param Closure $closure
656      * @param $controller
657      */
658     private function callReliesControllerClosure(Closure $closure, $controller)
659     {
660         $closure($controller);
661     }
662 
663     /**
664      * 设置action注释
665      *
666      * @param array $annotate
667      * @param array $controller_annotate
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 
CrossPHP API documentation generated by ApiGen