CWebApplication.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. <?php
  2. /**
  3. * CWebApplication class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright 2008-2013 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CWebApplication extends CApplication by providing functionalities specific to Web requests.
  12. *
  13. * CWebApplication manages the controllers in MVC pattern, and provides the following additional
  14. * core application components:
  15. * <ul>
  16. * <li>{@link urlManager}: provides URL parsing and constructing functionality;</li>
  17. * <li>{@link request}: encapsulates the Web request information;</li>
  18. * <li>{@link session}: provides the session-related functionalities;</li>
  19. * <li>{@link assetManager}: manages the publishing of private asset files.</li>
  20. * <li>{@link user}: represents the user session information.</li>
  21. * <li>{@link themeManager}: manages themes.</li>
  22. * <li>{@link authManager}: manages role-based access control (RBAC).</li>
  23. * <li>{@link clientScript}: manages client scripts (javascripts and CSS).</li>
  24. * <li>{@link widgetFactory}: creates widgets and supports widget skinning.</li>
  25. * </ul>
  26. *
  27. * User requests are resolved as controller-action pairs and additional parameters.
  28. * CWebApplication creates the requested controller instance and let it to handle
  29. * the actual user request. If the user does not specify controller ID, it will
  30. * assume {@link defaultController} is requested (which defaults to 'site').
  31. *
  32. * Controller class files must reside under the directory {@link getControllerPath controllerPath}
  33. * (defaults to 'protected/controllers'). The file name and the class name must be
  34. * the same as the controller ID with the first letter in upper case and appended with 'Controller'.
  35. * For example, the controller 'article' is defined by the class 'ArticleController'
  36. * which is in the file 'protected/controllers/ArticleController.php'.
  37. *
  38. * @property IAuthManager $authManager The authorization manager component.
  39. * @property CAssetManager $assetManager The asset manager component.
  40. * @property CHttpSession $session The session component.
  41. * @property CWebUser $user The user session information.
  42. * @property IViewRenderer $viewRenderer The view renderer.
  43. * @property CClientScript $clientScript The client script manager.
  44. * @property IWidgetFactory $widgetFactory The widget factory.
  45. * @property CThemeManager $themeManager The theme manager.
  46. * @property CTheme $theme The theme used currently. Null if no theme is being used.
  47. * @property CController $controller The currently active controller.
  48. * @property string $controllerPath The directory that contains the controller classes. Defaults to 'protected/controllers'.
  49. * @property string $viewPath The root directory of view files. Defaults to 'protected/views'.
  50. * @property string $systemViewPath The root directory of system view files. Defaults to 'protected/views/system'.
  51. * @property string $layoutPath The root directory of layout files. Defaults to 'protected/views/layouts'.
  52. *
  53. * @author Qiang Xue <qiang.xue@gmail.com>
  54. * @package system.web
  55. * @since 1.0
  56. */
  57. class CWebApplication extends CApplication
  58. {
  59. /**
  60. * @return string the route of the default controller, action or module. Defaults to 'site'.
  61. */
  62. public $defaultController='site';
  63. /**
  64. * @var mixed the application-wide layout. Defaults to 'main' (relative to {@link getLayoutPath layoutPath}).
  65. * If this is false, then no layout will be used.
  66. */
  67. public $layout='main';
  68. /**
  69. * @var array mapping from controller ID to controller configurations.
  70. * Each name-value pair specifies the configuration for a single controller.
  71. * A controller configuration can be either a string or an array.
  72. * If the former, the string should be the class name or
  73. * {@link YiiBase::getPathOfAlias class path alias} of the controller.
  74. * If the latter, the array must contain a 'class' element which specifies
  75. * the controller's class name or {@link YiiBase::getPathOfAlias class path alias}.
  76. * The rest name-value pairs in the array are used to initialize
  77. * the corresponding controller properties. For example,
  78. * <pre>
  79. * array(
  80. * 'post'=>array(
  81. * 'class'=>'path.to.PostController',
  82. * 'pageTitle'=>'something new',
  83. * ),
  84. * 'user'=>'path.to.UserController',
  85. * )
  86. * </pre>
  87. *
  88. * Note, when processing an incoming request, the controller map will first be
  89. * checked to see if the request can be handled by one of the controllers in the map.
  90. * If not, a controller will be searched for under the {@link getControllerPath default controller path}.
  91. */
  92. public $controllerMap=array();
  93. /**
  94. * @var array the configuration specifying a controller which should handle
  95. * all user requests. This is mainly used when the application is in maintenance mode
  96. * and we should use a controller to handle all incoming requests.
  97. * The configuration specifies the controller route (the first element)
  98. * and GET parameters (the rest name-value pairs). For example,
  99. * <pre>
  100. * array(
  101. * 'offline/notice',
  102. * 'param1'=>'value1',
  103. * 'param2'=>'value2',
  104. * )
  105. * </pre>
  106. * Defaults to null, meaning catch-all is not effective.
  107. */
  108. public $catchAllRequest;
  109. /**
  110. * @var string Namespace that should be used when loading controllers.
  111. * Default is to use global namespace.
  112. * @since 1.1.11
  113. */
  114. public $controllerNamespace;
  115. private $_controllerPath;
  116. private $_viewPath;
  117. private $_systemViewPath;
  118. private $_layoutPath;
  119. private $_controller;
  120. private $_theme;
  121. /**
  122. * Processes the current request.
  123. * It first resolves the request into controller and action,
  124. * and then creates the controller to perform the action.
  125. */
  126. public function processRequest()
  127. {
  128. if(is_array($this->catchAllRequest) && isset($this->catchAllRequest[0]))
  129. {
  130. $route=$this->catchAllRequest[0];
  131. foreach(array_splice($this->catchAllRequest,1) as $name=>$value)
  132. $_GET[$name]=$value;
  133. }
  134. else
  135. $route=$this->getUrlManager()->parseUrl($this->getRequest());
  136. $this->runController($route);
  137. }
  138. /**
  139. * Registers the core application components.
  140. * This method overrides the parent implementation by registering additional core components.
  141. * @see setComponents
  142. */
  143. protected function registerCoreComponents()
  144. {
  145. parent::registerCoreComponents();
  146. $components=array(
  147. 'session'=>array(
  148. 'class'=>'CHttpSession',
  149. ),
  150. 'assetManager'=>array(
  151. 'class'=>'CAssetManager',
  152. ),
  153. 'user'=>array(
  154. 'class'=>'CWebUser',
  155. ),
  156. 'themeManager'=>array(
  157. 'class'=>'CThemeManager',
  158. ),
  159. 'authManager'=>array(
  160. 'class'=>'CPhpAuthManager',
  161. ),
  162. 'clientScript'=>array(
  163. 'class'=>'CClientScript',
  164. ),
  165. 'widgetFactory'=>array(
  166. 'class'=>'CWidgetFactory',
  167. ),
  168. );
  169. $this->setComponents($components);
  170. }
  171. /**
  172. * @return IAuthManager the authorization manager component
  173. */
  174. public function getAuthManager()
  175. {
  176. return $this->getComponent('authManager');
  177. }
  178. /**
  179. * @return CAssetManager the asset manager component
  180. */
  181. public function getAssetManager()
  182. {
  183. return $this->getComponent('assetManager');
  184. }
  185. /**
  186. * @return CHttpSession the session component
  187. */
  188. public function getSession()
  189. {
  190. return $this->getComponent('session');
  191. }
  192. /**
  193. * @return CWebUser the user session information
  194. */
  195. public function getUser()
  196. {
  197. return $this->getComponent('user');
  198. }
  199. /**
  200. * Returns the view renderer.
  201. * If this component is registered and enabled, the default
  202. * view rendering logic defined in {@link CBaseController} will
  203. * be replaced by this renderer.
  204. * @return IViewRenderer the view renderer.
  205. */
  206. public function getViewRenderer()
  207. {
  208. return $this->getComponent('viewRenderer');
  209. }
  210. /**
  211. * Returns the client script manager.
  212. * @return CClientScript the client script manager
  213. */
  214. public function getClientScript()
  215. {
  216. return $this->getComponent('clientScript');
  217. }
  218. /**
  219. * Returns the widget factory.
  220. * @return IWidgetFactory the widget factory
  221. * @since 1.1
  222. */
  223. public function getWidgetFactory()
  224. {
  225. return $this->getComponent('widgetFactory');
  226. }
  227. /**
  228. * @return CThemeManager the theme manager.
  229. */
  230. public function getThemeManager()
  231. {
  232. return $this->getComponent('themeManager');
  233. }
  234. /**
  235. * @return CTheme the theme used currently. Null if no theme is being used.
  236. */
  237. public function getTheme()
  238. {
  239. if(is_string($this->_theme))
  240. $this->_theme=$this->getThemeManager()->getTheme($this->_theme);
  241. return $this->_theme;
  242. }
  243. /**
  244. * @param string $value the theme name
  245. */
  246. public function setTheme($value)
  247. {
  248. $this->_theme=$value;
  249. }
  250. /**
  251. * Creates the controller and performs the specified action.
  252. * @param string $route the route of the current request. See {@link createController} for more details.
  253. * @throws CHttpException if the controller could not be created.
  254. */
  255. public function runController($route)
  256. {
  257. if(($ca=$this->createController($route))!==null)
  258. {
  259. list($controller,$actionID)=$ca;
  260. $oldController=$this->_controller;
  261. $this->_controller=$controller;
  262. $controller->init();
  263. $controller->run($actionID);
  264. $this->_controller=$oldController;
  265. }
  266. else
  267. throw new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".',
  268. array('{route}'=>$route===''?$this->defaultController:$route)));
  269. }
  270. /**
  271. * Creates a controller instance based on a route.
  272. * The route should contain the controller ID and the action ID.
  273. * It may also contain additional GET variables. All these must be concatenated together with slashes.
  274. *
  275. * This method will attempt to create a controller in the following order:
  276. * <ol>
  277. * <li>If the first segment is found in {@link controllerMap}, the corresponding
  278. * controller configuration will be used to create the controller;</li>
  279. * <li>If the first segment is found to be a module ID, the corresponding module
  280. * will be used to create the controller;</li>
  281. * <li>Otherwise, it will search under the {@link controllerPath} to create
  282. * the corresponding controller. For example, if the route is "admin/user/create",
  283. * then the controller will be created using the class file "protected/controllers/admin/UserController.php".</li>
  284. * </ol>
  285. * @param string $route the route of the request.
  286. * @param CWebModule $owner the module that the new controller will belong to. Defaults to null, meaning the application
  287. * instance is the owner.
  288. * @return array the controller instance and the action ID. Null if the controller class does not exist or the route is invalid.
  289. */
  290. public function createController($route,$owner=null)
  291. {
  292. if($owner===null)
  293. $owner=$this;
  294. if(($route=trim($route,'/'))==='')
  295. $route=$owner->defaultController;
  296. $caseSensitive=$this->getUrlManager()->caseSensitive;
  297. $route.='/';
  298. while(($pos=strpos($route,'/'))!==false)
  299. {
  300. $id=substr($route,0,$pos);
  301. if(!preg_match('/^\w+$/',$id))
  302. return null;
  303. if(!$caseSensitive)
  304. $id=strtolower($id);
  305. $route=(string)substr($route,$pos+1);
  306. if(!isset($basePath)) // first segment
  307. {
  308. if(isset($owner->controllerMap[$id]))
  309. {
  310. return array(
  311. Yii::createComponent($owner->controllerMap[$id],$id,$owner===$this?null:$owner),
  312. $this->parseActionParams($route),
  313. );
  314. }
  315. if(($module=$owner->getModule($id))!==null)
  316. return $this->createController($route,$module);
  317. $basePath=$owner->getControllerPath();
  318. $controllerID='';
  319. }
  320. else
  321. $controllerID.='/';
  322. $className=ucfirst($id).'Controller';
  323. $classFile=$basePath.DIRECTORY_SEPARATOR.$className.'.php';
  324. if($owner->controllerNamespace!==null)
  325. $className=$owner->controllerNamespace.'\\'.str_replace('/','\\',$controllerID).$className;
  326. if(is_file($classFile))
  327. {
  328. if(!class_exists($className,false))
  329. require($classFile);
  330. if(class_exists($className,false) && is_subclass_of($className,'CController'))
  331. {
  332. $id[0]=strtolower($id[0]);
  333. return array(
  334. new $className($controllerID.$id,$owner===$this?null:$owner),
  335. $this->parseActionParams($route),
  336. );
  337. }
  338. return null;
  339. }
  340. $controllerID.=$id;
  341. $basePath.=DIRECTORY_SEPARATOR.$id;
  342. }
  343. }
  344. /**
  345. * Parses a path info into an action ID and GET variables.
  346. * @param string $pathInfo path info
  347. * @return string action ID
  348. */
  349. protected function parseActionParams($pathInfo)
  350. {
  351. if(($pos=strpos($pathInfo,'/'))!==false)
  352. {
  353. $manager=$this->getUrlManager();
  354. $manager->parsePathInfo((string)substr($pathInfo,$pos+1));
  355. $actionID=substr($pathInfo,0,$pos);
  356. return $manager->caseSensitive ? $actionID : strtolower($actionID);
  357. }
  358. else
  359. return $pathInfo;
  360. }
  361. /**
  362. * @return CController the currently active controller
  363. */
  364. public function getController()
  365. {
  366. return $this->_controller;
  367. }
  368. /**
  369. * @param CController $value the currently active controller
  370. */
  371. public function setController($value)
  372. {
  373. $this->_controller=$value;
  374. }
  375. /**
  376. * @return string the directory that contains the controller classes. Defaults to 'protected/controllers'.
  377. */
  378. public function getControllerPath()
  379. {
  380. if($this->_controllerPath!==null)
  381. return $this->_controllerPath;
  382. else
  383. return $this->_controllerPath=$this->getBasePath().DIRECTORY_SEPARATOR.'controllers';
  384. }
  385. /**
  386. * @param string $value the directory that contains the controller classes.
  387. * @throws CException if the directory is invalid
  388. */
  389. public function setControllerPath($value)
  390. {
  391. if(($this->_controllerPath=realpath($value))===false || !is_dir($this->_controllerPath))
  392. throw new CException(Yii::t('yii','The controller path "{path}" is not a valid directory.',
  393. array('{path}'=>$value)));
  394. }
  395. /**
  396. * @return string the root directory of view files. Defaults to 'protected/views'.
  397. */
  398. public function getViewPath()
  399. {
  400. if($this->_viewPath!==null)
  401. return $this->_viewPath;
  402. else
  403. return $this->_viewPath=$this->getBasePath().DIRECTORY_SEPARATOR.'views';
  404. }
  405. /**
  406. * @param string $path the root directory of view files.
  407. * @throws CException if the directory does not exist.
  408. */
  409. public function setViewPath($path)
  410. {
  411. if(($this->_viewPath=realpath($path))===false || !is_dir($this->_viewPath))
  412. throw new CException(Yii::t('yii','The view path "{path}" is not a valid directory.',
  413. array('{path}'=>$path)));
  414. }
  415. /**
  416. * @return string the root directory of system view files. Defaults to 'protected/views/system'.
  417. */
  418. public function getSystemViewPath()
  419. {
  420. if($this->_systemViewPath!==null)
  421. return $this->_systemViewPath;
  422. else
  423. return $this->_systemViewPath=$this->getViewPath().DIRECTORY_SEPARATOR.'system';
  424. }
  425. /**
  426. * @param string $path the root directory of system view files.
  427. * @throws CException if the directory does not exist.
  428. */
  429. public function setSystemViewPath($path)
  430. {
  431. if(($this->_systemViewPath=realpath($path))===false || !is_dir($this->_systemViewPath))
  432. throw new CException(Yii::t('yii','The system view path "{path}" is not a valid directory.',
  433. array('{path}'=>$path)));
  434. }
  435. /**
  436. * @return string the root directory of layout files. Defaults to 'protected/views/layouts'.
  437. */
  438. public function getLayoutPath()
  439. {
  440. if($this->_layoutPath!==null)
  441. return $this->_layoutPath;
  442. else
  443. return $this->_layoutPath=$this->getViewPath().DIRECTORY_SEPARATOR.'layouts';
  444. }
  445. /**
  446. * @param string $path the root directory of layout files.
  447. * @throws CException if the directory does not exist.
  448. */
  449. public function setLayoutPath($path)
  450. {
  451. if(($this->_layoutPath=realpath($path))===false || !is_dir($this->_layoutPath))
  452. throw new CException(Yii::t('yii','The layout path "{path}" is not a valid directory.',
  453. array('{path}'=>$path)));
  454. }
  455. /**
  456. * The pre-filter for controller actions.
  457. * This method is invoked before the currently requested controller action and all its filters
  458. * are executed. You may override this method with logic that needs to be done
  459. * before all controller actions.
  460. * @param CController $controller the controller
  461. * @param CAction $action the action
  462. * @return boolean whether the action should be executed.
  463. */
  464. public function beforeControllerAction($controller,$action)
  465. {
  466. return true;
  467. }
  468. /**
  469. * The post-filter for controller actions.
  470. * This method is invoked after the currently requested controller action and all its filters
  471. * are executed. You may override this method with logic that needs to be done
  472. * after all controller actions.
  473. * @param CController $controller the controller
  474. * @param CAction $action the action
  475. */
  476. public function afterControllerAction($controller,$action)
  477. {
  478. }
  479. /**
  480. * Do not call this method. This method is used internally to search for a module by its ID.
  481. * @param string $id module ID
  482. * @return CWebModule the module that has the specified ID. Null if no module is found.
  483. */
  484. public function findModule($id)
  485. {
  486. if(($controller=$this->getController())!==null && ($module=$controller->getModule())!==null)
  487. {
  488. do
  489. {
  490. if(($m=$module->getModule($id))!==null)
  491. return $m;
  492. } while(($module=$module->getParentModule())!==null);
  493. }
  494. if(($m=$this->getModule($id))!==null)
  495. return $m;
  496. }
  497. /**
  498. * Initializes the application.
  499. * This method overrides the parent implementation by preloading the 'request' component.
  500. */
  501. protected function init()
  502. {
  503. parent::init();
  504. // preload 'request' so that it has chance to respond to onBeginRequest event.
  505. $this->getRequest();
  506. }
  507. }