* @link http://www.yiiframework.com/ * @copyright 2008-2013 Yii Software LLC * @license http://www.yiiframework.com/license/ */ /** * CWebApplication extends CApplication by providing functionalities specific to Web requests. * * CWebApplication manages the controllers in MVC pattern, and provides the following additional * core application components: * * * User requests are resolved as controller-action pairs and additional parameters. * CWebApplication creates the requested controller instance and let it to handle * the actual user request. If the user does not specify controller ID, it will * assume {@link defaultController} is requested (which defaults to 'site'). * * Controller class files must reside under the directory {@link getControllerPath controllerPath} * (defaults to 'protected/controllers'). The file name and the class name must be * the same as the controller ID with the first letter in upper case and appended with 'Controller'. * For example, the controller 'article' is defined by the class 'ArticleController' * which is in the file 'protected/controllers/ArticleController.php'. * * @property IAuthManager $authManager The authorization manager component. * @property CAssetManager $assetManager The asset manager component. * @property CHttpSession $session The session component. * @property CWebUser $user The user session information. * @property IViewRenderer $viewRenderer The view renderer. * @property CClientScript $clientScript The client script manager. * @property IWidgetFactory $widgetFactory The widget factory. * @property CThemeManager $themeManager The theme manager. * @property CTheme $theme The theme used currently. Null if no theme is being used. * @property CController $controller The currently active controller. * @property string $controllerPath The directory that contains the controller classes. Defaults to 'protected/controllers'. * @property string $viewPath The root directory of view files. Defaults to 'protected/views'. * @property string $systemViewPath The root directory of system view files. Defaults to 'protected/views/system'. * @property string $layoutPath The root directory of layout files. Defaults to 'protected/views/layouts'. * * @author Qiang Xue * @package system.web * @since 1.0 */ class CWebApplication extends CApplication { /** * @return string the route of the default controller, action or module. Defaults to 'site'. */ public $defaultController='site'; /** * @var mixed the application-wide layout. Defaults to 'main' (relative to {@link getLayoutPath layoutPath}). * If this is false, then no layout will be used. */ public $layout='main'; /** * @var array mapping from controller ID to controller configurations. * Each name-value pair specifies the configuration for a single controller. * A controller configuration can be either a string or an array. * If the former, the string should be the class name or * {@link YiiBase::getPathOfAlias class path alias} of the controller. * If the latter, the array must contain a 'class' element which specifies * the controller's class name or {@link YiiBase::getPathOfAlias class path alias}. * The rest name-value pairs in the array are used to initialize * the corresponding controller properties. For example, *
	 * array(
	 *   'post'=>array(
	 *      'class'=>'path.to.PostController',
	 *      'pageTitle'=>'something new',
	 *   ),
	 *   'user'=>'path.to.UserController',
	 * )
	 * 
* * Note, when processing an incoming request, the controller map will first be * checked to see if the request can be handled by one of the controllers in the map. * If not, a controller will be searched for under the {@link getControllerPath default controller path}. */ public $controllerMap=array(); /** * @var array the configuration specifying a controller which should handle * all user requests. This is mainly used when the application is in maintenance mode * and we should use a controller to handle all incoming requests. * The configuration specifies the controller route (the first element) * and GET parameters (the rest name-value pairs). For example, *
	 * array(
	 *     'offline/notice',
	 *     'param1'=>'value1',
	 *     'param2'=>'value2',
	 * )
	 * 
* Defaults to null, meaning catch-all is not effective. */ public $catchAllRequest; /** * @var string Namespace that should be used when loading controllers. * Default is to use global namespace. * @since 1.1.11 */ public $controllerNamespace; private $_controllerPath; private $_viewPath; private $_systemViewPath; private $_layoutPath; private $_controller; private $_theme; /** * Processes the current request. * It first resolves the request into controller and action, * and then creates the controller to perform the action. */ public function processRequest() { if(is_array($this->catchAllRequest) && isset($this->catchAllRequest[0])) { $route=$this->catchAllRequest[0]; foreach(array_splice($this->catchAllRequest,1) as $name=>$value) $_GET[$name]=$value; } else $route=$this->getUrlManager()->parseUrl($this->getRequest()); $this->runController($route); } /** * Registers the core application components. * This method overrides the parent implementation by registering additional core components. * @see setComponents */ protected function registerCoreComponents() { parent::registerCoreComponents(); $components=array( 'session'=>array( 'class'=>'CHttpSession', ), 'assetManager'=>array( 'class'=>'CAssetManager', ), 'user'=>array( 'class'=>'CWebUser', ), 'themeManager'=>array( 'class'=>'CThemeManager', ), 'authManager'=>array( 'class'=>'CPhpAuthManager', ), 'clientScript'=>array( 'class'=>'CClientScript', ), 'widgetFactory'=>array( 'class'=>'CWidgetFactory', ), ); $this->setComponents($components); } /** * @return IAuthManager the authorization manager component */ public function getAuthManager() { return $this->getComponent('authManager'); } /** * @return CAssetManager the asset manager component */ public function getAssetManager() { return $this->getComponent('assetManager'); } /** * @return CHttpSession the session component */ public function getSession() { return $this->getComponent('session'); } /** * @return CWebUser the user session information */ public function getUser() { return $this->getComponent('user'); } /** * Returns the view renderer. * If this component is registered and enabled, the default * view rendering logic defined in {@link CBaseController} will * be replaced by this renderer. * @return IViewRenderer the view renderer. */ public function getViewRenderer() { return $this->getComponent('viewRenderer'); } /** * Returns the client script manager. * @return CClientScript the client script manager */ public function getClientScript() { return $this->getComponent('clientScript'); } /** * Returns the widget factory. * @return IWidgetFactory the widget factory * @since 1.1 */ public function getWidgetFactory() { return $this->getComponent('widgetFactory'); } /** * @return CThemeManager the theme manager. */ public function getThemeManager() { return $this->getComponent('themeManager'); } /** * @return CTheme the theme used currently. Null if no theme is being used. */ public function getTheme() { if(is_string($this->_theme)) $this->_theme=$this->getThemeManager()->getTheme($this->_theme); return $this->_theme; } /** * @param string $value the theme name */ public function setTheme($value) { $this->_theme=$value; } /** * Creates the controller and performs the specified action. * @param string $route the route of the current request. See {@link createController} for more details. * @throws CHttpException if the controller could not be created. */ public function runController($route) { if(($ca=$this->createController($route))!==null) { list($controller,$actionID)=$ca; $oldController=$this->_controller; $this->_controller=$controller; $controller->init(); $controller->run($actionID); $this->_controller=$oldController; } else throw new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".', array('{route}'=>$route===''?$this->defaultController:$route))); } /** * Creates a controller instance based on a route. * The route should contain the controller ID and the action ID. * It may also contain additional GET variables. All these must be concatenated together with slashes. * * This method will attempt to create a controller in the following order: *
    *
  1. If the first segment is found in {@link controllerMap}, the corresponding * controller configuration will be used to create the controller;
  2. *
  3. If the first segment is found to be a module ID, the corresponding module * will be used to create the controller;
  4. *
  5. Otherwise, it will search under the {@link controllerPath} to create * the corresponding controller. For example, if the route is "admin/user/create", * then the controller will be created using the class file "protected/controllers/admin/UserController.php".
  6. *
* @param string $route the route of the request. * @param CWebModule $owner the module that the new controller will belong to. Defaults to null, meaning the application * instance is the owner. * @return array the controller instance and the action ID. Null if the controller class does not exist or the route is invalid. */ public function createController($route,$owner=null) { if($owner===null) $owner=$this; if((array)$route===$route || ($route=trim($route,'/'))==='') $route=$owner->defaultController; $caseSensitive=$this->getUrlManager()->caseSensitive; $route.='/'; while(($pos=strpos($route,'/'))!==false) { $id=substr($route,0,$pos); if(!preg_match('/^\w+$/',$id)) return null; if(!$caseSensitive) $id=strtolower($id); $route=(string)substr($route,$pos+1); if(!isset($basePath)) // first segment { if(isset($owner->controllerMap[$id])) { return array( Yii::createComponent($owner->controllerMap[$id],$id,$owner===$this?null:$owner), $this->parseActionParams($route), ); } if(($module=$owner->getModule($id))!==null) return $this->createController($route,$module); $basePath=$owner->getControllerPath(); $controllerID=''; } else $controllerID.='/'; $className=ucfirst($id).'Controller'; $classFile=$basePath.DIRECTORY_SEPARATOR.$className.'.php'; if($owner->controllerNamespace!==null) $className=$owner->controllerNamespace.'\\'.str_replace('/','\\',$controllerID).$className; if(is_file($classFile)) { if(!class_exists($className,false)) require($classFile); if(class_exists($className,false) && is_subclass_of($className,'CController')) { $id[0]=strtolower($id[0]); return array( new $className($controllerID.$id,$owner===$this?null:$owner), $this->parseActionParams($route), ); } return null; } $controllerID.=$id; $basePath.=DIRECTORY_SEPARATOR.$id; } } /** * Parses a path info into an action ID and GET variables. * @param string $pathInfo path info * @return string action ID */ protected function parseActionParams($pathInfo) { if(($pos=strpos($pathInfo,'/'))!==false) { $manager=$this->getUrlManager(); $manager->parsePathInfo((string)substr($pathInfo,$pos+1)); $actionID=substr($pathInfo,0,$pos); return $manager->caseSensitive ? $actionID : strtolower($actionID); } else return $pathInfo; } /** * @return CController the currently active controller */ public function getController() { return $this->_controller; } /** * @param CController $value the currently active controller */ public function setController($value) { $this->_controller=$value; } /** * @return string the directory that contains the controller classes. Defaults to 'protected/controllers'. */ public function getControllerPath() { if($this->_controllerPath!==null) return $this->_controllerPath; else return $this->_controllerPath=$this->getBasePath().DIRECTORY_SEPARATOR.'controllers'; } /** * @param string $value the directory that contains the controller classes. * @throws CException if the directory is invalid */ public function setControllerPath($value) { if(($this->_controllerPath=realpath($value))===false || !is_dir($this->_controllerPath)) throw new CException(Yii::t('yii','The controller path "{path}" is not a valid directory.', array('{path}'=>$value))); } /** * @return string the root directory of view files. Defaults to 'protected/views'. */ public function getViewPath() { if($this->_viewPath!==null) return $this->_viewPath; else return $this->_viewPath=$this->getBasePath().DIRECTORY_SEPARATOR.'views'; } /** * @param string $path the root directory of view files. * @throws CException if the directory does not exist. */ public function setViewPath($path) { if(($this->_viewPath=realpath($path))===false || !is_dir($this->_viewPath)) throw new CException(Yii::t('yii','The view path "{path}" is not a valid directory.', array('{path}'=>$path))); } /** * @return string the root directory of system view files. Defaults to 'protected/views/system'. */ public function getSystemViewPath() { if($this->_systemViewPath!==null) return $this->_systemViewPath; else return $this->_systemViewPath=$this->getViewPath().DIRECTORY_SEPARATOR.'system'; } /** * @param string $path the root directory of system view files. * @throws CException if the directory does not exist. */ public function setSystemViewPath($path) { if(($this->_systemViewPath=realpath($path))===false || !is_dir($this->_systemViewPath)) throw new CException(Yii::t('yii','The system view path "{path}" is not a valid directory.', array('{path}'=>$path))); } /** * @return string the root directory of layout files. Defaults to 'protected/views/layouts'. */ public function getLayoutPath() { if($this->_layoutPath!==null) return $this->_layoutPath; else return $this->_layoutPath=$this->getViewPath().DIRECTORY_SEPARATOR.'layouts'; } /** * @param string $path the root directory of layout files. * @throws CException if the directory does not exist. */ public function setLayoutPath($path) { if(($this->_layoutPath=realpath($path))===false || !is_dir($this->_layoutPath)) throw new CException(Yii::t('yii','The layout path "{path}" is not a valid directory.', array('{path}'=>$path))); } /** * The pre-filter for controller actions. * This method is invoked before the currently requested controller action and all its filters * are executed. You may override this method with logic that needs to be done * before all controller actions. * @param CController $controller the controller * @param CAction $action the action * @return boolean whether the action should be executed. */ public function beforeControllerAction($controller,$action) { return true; } /** * The post-filter for controller actions. * This method is invoked after the currently requested controller action and all its filters * are executed. You may override this method with logic that needs to be done * after all controller actions. * @param CController $controller the controller * @param CAction $action the action */ public function afterControllerAction($controller,$action) { } /** * Do not call this method. This method is used internally to search for a module by its ID. * @param string $id module ID * @return CWebModule the module that has the specified ID. Null if no module is found. */ public function findModule($id) { if(($controller=$this->getController())!==null && ($module=$controller->getModule())!==null) { do { if(($m=$module->getModule($id))!==null) return $m; } while(($module=$module->getParentModule())!==null); } if(($m=$this->getModule($id))!==null) return $m; } /** * Initializes the application. * This method overrides the parent implementation by preloading the 'request' component. */ protected function init() { parent::init(); // preload 'request' so that it has chance to respond to onBeginRequest event. $this->getRequest(); } }