From 165bb02a65646afcdfa595c16c601e250a8dc4f4 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 30 Apr 2012 11:20:53 -0400 Subject: [PATCH] ... --- framework/base/Action.php | 110 ++++++++ framework/base/ActionFilter.php | 75 ++++++ framework/base/Application.php | 276 ++----------------- framework/base/Controller.php | 490 ++++++++++++++++++++++++++++++++++ framework/base/ErrorHandler.php | 38 +-- framework/base/InlineAction.php | 53 ++++ framework/base/InlineActionFilter.php | 61 +++++ framework/base/Module.php | 5 +- 8 files changed, 829 insertions(+), 279 deletions(-) create mode 100644 framework/base/Action.php create mode 100644 framework/base/ActionFilter.php create mode 100644 framework/base/Controller.php create mode 100644 framework/base/InlineAction.php create mode 100644 framework/base/InlineActionFilter.php diff --git a/framework/base/Action.php b/framework/base/Action.php new file mode 100644 index 0000000..415c421 --- /dev/null +++ b/framework/base/Action.php @@ -0,0 +1,110 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CAction is the base class for all controller action classes. + * + * CAction provides a way to divide a complex controller into + * smaller actions in separate class files. + * + * Derived classes must implement {@link run()} which is invoked by + * controller when the action is requested. + * + * An action instance can access its controller via {@link getController controller} property. + * + * @property CController $controller The controller who owns this action. + * @property string $id Id of this action. + * + * @author Qiang Xue + * @version $Id$ + * @package system.web.actions + * @since 1.0 + */ +abstract class CAction extends CComponent implements IAction +{ + private $_id; + private $_controller; + + /** + * Constructor. + * @param CController $controller the controller who owns this action. + * @param string $id id of the action. + */ + public function __construct($controller,$id) + { + $this->_controller=$controller; + $this->_id=$id; + } + + /** + * @return CController the controller who owns this action. + */ + public function getController() + { + return $this->_controller; + } + + /** + * @return string id of this action + */ + public function getId() + { + return $this->_id; + } + + /** + * Runs the action with the supplied request parameters. + * This method is internally called by {@link CController::runAction()}. + * @param array $params the request parameters (name=>value) + * @return boolean whether the request parameters are valid + * @since 1.1.7 + */ + public function runWithParams($params) + { + $method=new ReflectionMethod($this, 'run'); + if($method->getNumberOfParameters()>0) + return $this->runWithParamsInternal($this, $method, $params); + else + return $this->run(); + } + + /** + * Executes a method of an object with the supplied named parameters. + * This method is internally used. + * @param mixed $object the object whose method is to be executed + * @param ReflectionMethod $method the method reflection + * @param array $params the named parameters + * @return boolean whether the named parameters are valid + * @since 1.1.7 + */ + protected function runWithParamsInternal($object, $method, $params) + { + $ps=array(); + foreach($method->getParameters() as $i=>$param) + { + $name=$param->getName(); + if(isset($params[$name])) + { + if($param->isArray()) + $ps[]=is_array($params[$name]) ? $params[$name] : array($params[$name]); + else if(!is_array($params[$name])) + $ps[]=$params[$name]; + else + return false; + } + else if($param->isDefaultValueAvailable()) + $ps[]=$param->getDefaultValue(); + else + return false; + } + $method->invokeArgs($object,$ps); + return true; + } +} diff --git a/framework/base/ActionFilter.php b/framework/base/ActionFilter.php new file mode 100644 index 0000000..efa3b51 --- /dev/null +++ b/framework/base/ActionFilter.php @@ -0,0 +1,75 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CFilter is the base class for all filters. + * + * A filter can be applied before and after an action is executed. + * It can modify the context that the action is to run or decorate the result that the + * action generates. + * + * Override {@link preFilter()} to specify the filtering logic that should be applied + * before the action, and {@link postFilter()} for filtering logic after the action. + * + * @author Qiang Xue + * @version $Id$ + * @package system.web.filters + * @since 1.0 + */ +class CFilter extends CComponent implements IFilter +{ + /** + * Performs the filtering. + * The default implementation is to invoke {@link preFilter} + * and {@link postFilter} which are meant to be overridden + * child classes. If a child class needs to override this method, + * make sure it calls $filterChain->run() + * if the action should be executed. + * @param CFilterChain $filterChain the filter chain that the filter is on. + */ + public function filter($filterChain) + { + if($this->preFilter($filterChain)) + { + $filterChain->run(); + $this->postFilter($filterChain); + } + } + + /** + * Initializes the filter. + * This method is invoked after the filter properties are initialized + * and before {@link preFilter} is called. + * You may override this method to include some initialization logic. + * @since 1.1.4 + */ + public function init() + { + } + + /** + * Performs the pre-action filtering. + * @param CFilterChain $filterChain the filter chain that the filter is on. + * @return boolean whether the filtering process should continue and the action + * should be executed. + */ + protected function preFilter($filterChain) + { + return true; + } + + /** + * Performs the post-action filtering. + * @param CFilterChain $filterChain the filter chain that the filter is on. + */ + protected function postFilter($filterChain) + { + } +} \ No newline at end of file diff --git a/framework/base/Application.php b/framework/base/Application.php index bb9be2f..5663ac0 100644 --- a/framework/base/Application.php +++ b/framework/base/Application.php @@ -73,7 +73,7 @@ use yii\base\Exception; * @author Qiang Xue * @since 2.0 */ -abstract class Application extends Module +class Application extends Module { /** * @var string the application name. Defaults to 'My Application'. @@ -91,25 +91,16 @@ abstract class Application extends Module public $sourceLanguage = 'en_us'; public $preload = array('errorHandler'); + public $localeDataPath = '@yii/i18n/data'; + private $_runtimePath; private $_ended = false; private $_language; /** - * Processes the request. - * This is the place where the actual request processing work is done. - * Derived classes should override this method. - */ - abstract public function processRequest(); - - /** * Constructor. - * @param mixed $config application configuration. - * If a string, it is treated as the path of the file that contains the configuration; - * If an array, it is the actual configuration information. - * Please make sure you specify the {@link getBasePath basePath} property in the configuration, - * which should point to the directory containing all application logic, template and data. - * If not, the directory will be defaulted to 'protected'. + * @param string $basePath the base path of this application. This should point to + * the directory containing all application logic, template and data. */ public function __construct($basePath) { @@ -169,6 +160,15 @@ abstract class Application extends Module } /** + * Processes the request. + * This is the place where the actual request processing work is done. + * Derived classes should override this method. + */ + public function processRequest() + { + } + + /** * Raises the [[afterRequest]] event right AFTER the application processes the request. */ public function afterRequest() @@ -306,24 +306,6 @@ abstract class Application extends Module } /** - * Returns the directory that contains the locale data. - * @return string the directory that contains the locale data. It defaults to 'framework/i18n/data'. - */ - public function getLocaleDataPath() - { - return CLocale::$dataPath === null ? \Yii::getPathOfAlias('system.i18n.data') : CLocale::$dataPath; - } - - /** - * Sets the directory that contains the locale data. - * @param string $value the directory that contains the locale data. - */ - public function setLocaleDataPath($value) - { - CLocale::$dataPath = $value; - } - - /** * @return CNumberFormatter the locale-dependent number formatter. * The current {@link getLocale application locale} will be used. */ @@ -415,232 +397,6 @@ abstract class Application extends Module } /** - * Handles uncaught PHP exceptions. - * - * This method is implemented as a PHP exception handler. It requires - * that constant YII_ENABLE_EXCEPTION_HANDLER be defined true. - * - * This method will first raise an `exception` event. - * If the exception is not handled by any event handler, it will call - * {@link getErrorHandler errorHandler} to process the exception. - * - * The application will be terminated by this method. - * - * @param Exception $exception exception that is not caught - */ - public function handleException($exception) - { - // disable error capturing to avoid recursive errors - restore_error_handler(); - restore_exception_handler(); - - $category = 'exception.' . get_class($exception); - if ($exception instanceof \yii\web\HttpException) { - $category .= '.' . $exception->statusCode; - } - // php <5.2 doesn't support string conversion auto-magically - $message = $exception->__toString(); - if (isset($_SERVER['REQUEST_URI'])) { - $message .= ' REQUEST_URI=' . $_SERVER['REQUEST_URI']; - } - \Yii::error($message, $category); - - try - { - // TODO: do we need separate exception class as it was in 1.1? - //$event = new CExceptionEvent($this, $exception); - $event = new Event($this, array('exception' => $exception)); - $this->onException($event); - if (!$event->handled) { - // try an error handler - if (($handler = $this->getErrorHandler()) !== null) { - $handler->handle($event); - } else - { - $this->displayException($exception); - } - } - } - catch (Exception $e) - { - $this->displayException($e); - } - - try - { - $this->end(1); - } - catch (Exception $e) - { - // use the most primitive way to log error - $msg = get_class($e) . ': ' . $e->getMessage() . ' (' . $e->getFile() . ':' . $e->getLine() . ")\n"; - $msg .= $e->getTraceAsString() . "\n"; - $msg .= "Previous exception:\n"; - $msg .= get_class($exception) . ': ' . $exception->getMessage() . ' (' . $exception->getFile() . ':' . $exception->getLine() . ")\n"; - $msg .= $exception->getTraceAsString() . "\n"; - $msg .= '$_SERVER=' . var_export($_SERVER, true); - error_log($msg); - exit(1); - } - } - - /** - * Handles PHP execution errors such as warnings, notices. - * - * This method is implemented as a PHP error handler. It requires - * that constant YII_ENABLE_ERROR_HANDLER be defined true. - * - * This method will first raise an `error` event. - * If the error is not handled by any event handler, it will call - * {@link getErrorHandler errorHandler} to process the error. - * - * The application will be terminated by this method. - * - * @param integer $code the level of the error raised - * @param string $message the error message - * @param string $file the filename that the error was raised in - * @param integer $line the line number the error was raised at - */ - public function handleError($code, $message, $file, $line) - { - if ($code & error_reporting()) { - // disable error capturing to avoid recursive errors - restore_error_handler(); - restore_exception_handler(); - - $log = "$message ($file:$line)\nStack trace:\n"; - $trace = debug_backtrace(); - // skip the first 3 stacks as they do not tell the error position - if (count($trace) > 3) { - $trace = array_slice($trace, 3); - } - foreach ($trace as $i => $t) - { - if (!isset($t['file'])) { - $t['file'] = 'unknown'; - } - if (!isset($t['line'])) { - $t['line'] = 0; - } - if (!isset($t['function'])) { - $t['function'] = 'unknown'; - } - $log .= "#$i {$t['file']}( {$t['line']}): "; - if (isset($t['object']) && is_object($t['object'])) { - $log .= get_class($t['object']) . '->'; - } - $log .= " {$t['function']}()\n"; - } - if (isset($_SERVER['REQUEST_URI'])) { - $log .= 'REQUEST_URI=' . $_SERVER['REQUEST_URI']; - } - \Yii::error($log, 'php'); - - try - { - \Yii::import('CErrorEvent', true); - $event = new CErrorEvent($this, $code, $message, $file, $line); - $this->onError($event); - if (!$event->handled) { - // try an error handler - if (($handler = $this->getErrorHandler()) !== null) { - $handler->handle($event); - } else - { - $this->displayError($code, $message, $file, $line); - } - } - } - catch (Exception $e) - { - $this->displayException($e); - } - - try - { - $this->end(1); - } - catch (Exception $e) - { - // use the most primitive way to log error - $msg = get_class($e) . ': ' . $e->getMessage() . ' (' . $e->getFile() . ':' . $e->getLine() . ")\n"; - $msg .= $e->getTraceAsString() . "\n"; - $msg .= "Previous error:\n"; - $msg .= $log . "\n"; - $msg .= '$_SERVER=' . var_export($_SERVER, true); - error_log($msg); - exit(1); - } - } - } - - /** - * Displays the captured PHP error. - * This method displays the error in HTML when there is - * no active error handler. - * @param integer $code error code - * @param string $message error message - * @param string $file error file - * @param string $line error line - */ - public function displayError($code, $message, $file, $line) - { - if (YII_DEBUG) { - echo "

PHP Error [$code]

\n"; - echo "

$message ($file:$line)

\n"; - echo '
';
-
-			$trace = debug_backtrace();
-			// skip the first 3 stacks as they do not tell the error position
-			if (count($trace) > 3) {
-				$trace = array_slice($trace, 3);
-			}
-			foreach ($trace as $i => $t)
-			{
-				if (!isset($t['file'])) {
-					$t['file'] = 'unknown';
-				}
-				if (!isset($t['line'])) {
-					$t['line'] = 0;
-				}
-				if (!isset($t['function'])) {
-					$t['function'] = 'unknown';
-				}
-				echo "#$i  {$t['file']}( {$t['line']}): ";
-				if (isset($t['object']) && is_object($t['object'])) {
-					echo get_class($t['object']) . '->';
-				}
-				echo " {$t['function']}()\n";
-			}
-
-			echo '
'; - } else - { - echo "

PHP Error [$code]

\n"; - echo "

$message

\n"; - } - } - - /** - * Displays the uncaught PHP exception. - * This method displays the exception in HTML when there is - * no active error handler. - * @param Exception $exception the uncaught exception - */ - public function displayException($exception) - { - if (YII_DEBUG) { - echo '

' . get_class($exception) . "

\n"; - echo '

' . $exception->getMessage() . ' (' . $exception->getFile() . ':' . $exception->getLine() . ')

'; - echo '
' . $exception->getTraceAsString() . '
'; - } else - { - echo '

' . get_class($exception) . "

\n"; - echo '

' . $exception->getMessage() . '

'; - } - } - - /** * Registers the core application components. * @see setComponents */ @@ -654,7 +410,7 @@ abstract class Application extends Module 'class' => 'yii\base\Request', ), 'response' => array( - 'class' => 'yii\base\Request', + 'class' => 'yii\base\Response', ), 'format' => array( 'class' => 'yii\base\Formatter', @@ -662,7 +418,7 @@ abstract class Application extends Module 'coreMessages' => array( 'class' => 'yii\i18n\PhpMessageSource', 'language' => 'en_us', - 'basePath' => YII_PATH . DIRECTORY_SEPARATOR . 'messages', + 'basePath' => '@yii/messages', ), 'messages' => array( 'class' => 'yii\i18n\PhpMessageSource', diff --git a/framework/base/Controller.php b/framework/base/Controller.php new file mode 100644 index 0000000..0852ace --- /dev/null +++ b/framework/base/Controller.php @@ -0,0 +1,490 @@ + + *
  • {@link CClipWidget Clips} : a clip is a piece of captured output that can be inserted elsewhere.
  • + *
  • {@link CWidget Widgets} : a widget is a self-contained sub-controller with its own view and model.
  • + *
  • {@link COutputCache Fragment cache} : fragment cache selectively caches a portion of the output.
  • + * + * + * To use a widget in a view, use the following in the view: + *
    + * $this->widget('path.to.widgetClass',array('property1'=>'value1',...));
    + * 
    + * or + *
    + * $this->beginWidget('path.to.widgetClass',array('property1'=>'value1',...));
    + * // ... display other contents here
    + * $this->endWidget();
    + * 
    + * + * To create a clip, use the following: + *
    + * $this->beginClip('clipID');
    + * // ... display the clip contents
    + * $this->endClip();
    + * 
    + * Then, in a different view or place, the captured clip can be inserted as: + *
    + * echo $this->clips['clipID'];
    + * 
    + * + * Note that $this in the code above refers to current controller so, for example, + * if you need to access clip from a widget where $this refers to widget itself + * you need to do it the following way: + * + *
    + * echo $this->getController()->clips['clipID'];
    + * 
    + * + * To use fragment cache, do as follows, + *
    + * if($this->beginCache('cacheID',array('property1'=>'value1',...))
    + * {
    + *	 // ... display the content to be cached here
    + *	$this->endCache();
    + * }
    + * 
    + * + * @author Qiang Xue + * @since 2.0 + */ +abstract class Controller extends Component implements Initable +{ + /** + * @var string the name of the default action. Defaults to 'index'. + */ + public $defaultAction = 'index'; + + private $_id; + private $_action; + private $_module; + + + /** + * @param string $id id of this controller + * @param CWebModule $module the module that this controller belongs to. + */ + public function __construct($id, $module = null) + { + $this->_id = $id; + $this->_module = $module; + } + + /** + * Initializes the controller. + * This method is called by the application before the controller starts to execute. + * You may override this method to perform the needed initialization for the controller. + */ + public function init() + { + } + + /** + * Returns the filter configurations. + * + * By overriding this method, child classes can specify filters to be applied to actions. + * + * This method returns an array of filter specifications. Each array element specify a single filter. + * + * For a method-based filter (called inline filter), it is specified as 'FilterName[ +|- Action1, Action2, ...]', + * where the '+' ('-') operators describe which actions should be (should not be) applied with the filter. + * + * For a class-based filter, it is specified as an array like the following: + *
    +	 * array(
    +	 *	 'FilterClass[ +|- Action1, Action2, ...]',
    +	 *	 'name1'=>'value1',
    +	 *	 'name2'=>'value2',
    +	 *	 ...
    +	 * )
    +	 * 
    + * where the name-value pairs will be used to initialize the properties of the filter. + * + * Note, in order to inherit filters defined in the parent class, a child class needs to + * merge the parent filters with child filters using functions like array_merge(). + * + * @return array a list of filter configurations. + * @see CFilter + */ + public function filters() + { + return array(); + } + + /** + * Returns a list of external action classes. + * Array keys are action IDs, and array values are the corresponding + * action class in dot syntax (e.g. 'edit'=>'application.controllers.article.EditArticle') + * or arrays representing the configuration of the actions, such as the following, + *
    +	 * return array(
    +	 *	 'action1'=>'path.to.Action1Class',
    +	 *	 'action2'=>array(
    +	 *		 'class'=>'path.to.Action2Class',
    +	 *		 'property1'=>'value1',
    +	 *		 'property2'=>'value2',
    +	 *	 ),
    +	 * );
    +	 * 
    + * Derived classes may override this method to declare external actions. + * + * Note, in order to inherit actions defined in the parent class, a child class needs to + * merge the parent actions with child actions using functions like array_merge(). + * + * You may import actions from an action provider + * (such as a widget, see {@link CWidget::actions}), like the following: + *
    +	 * return array(
    +	 *	 ...other actions...
    +	 *	 // import actions declared in ProviderClass::actions()
    +	 *	 // the action IDs will be prefixed with 'pro.'
    +	 *	 'pro.'=>'path.to.ProviderClass',
    +	 *	 // similar as above except that the imported actions are
    +	 *	 // configured with the specified initial property values
    +	 *	 'pro2.'=>array(
    +	 *		 'class'=>'path.to.ProviderClass',
    +	 *		 'action1'=>array(
    +	 *			 'property1'=>'value1',
    +	 *		 ),
    +	 *		 'action2'=>array(
    +	 *			 'property2'=>'value2',
    +	 *		 ),
    +	 *	 ),
    +	 * )
    +	 * 
    + * + * In the above, we differentiate action providers from other action + * declarations by the array keys. For action providers, the array keys + * must contain a dot. As a result, an action ID 'pro2.action1' will + * be resolved as the 'action1' action declared in the 'ProviderClass'. + * + * @return array list of external action classes + * @see createAction + */ + public function actions() + { + return array(); + } + + /** + * Returns the access rules for this controller. + * Override this method if you use the {@link filterAccessControl accessControl} filter. + * @return array list of access rules. See {@link CAccessControlFilter} for details about rule specification. + */ + public function accessRules() + { + return array(); + } + + /** + * Runs the named action. + * Filters specified via {@link filters()} will be applied. + * @param string $actionID action ID + * @throws CHttpException if the action does not exist or the action name is not proper. + * @see filters + * @see createAction + * @see runAction + */ + public function run($actionID) + { + if (($action = $this->createAction($actionID)) !== null) { + if (($parent = $this->getModule()) === null) { + $parent = Yii::app(); + } + if ($parent->beforeControllerAction($this, $action)) { + $this->runActionWithFilters($action, $this->filters()); + $parent->afterControllerAction($this, $action); + } + } + else + { + $this->missingAction($actionID); + } + } + + /** + * Runs an action with the specified filters. + * A filter chain will be created based on the specified filters + * and the action will be executed then. + * @param CAction $action the action to be executed. + * @param array $filters list of filters to be applied to the action. + * @see filters + * @see createAction + * @see runAction + */ + public function runActionWithFilters($action, $filters) + { + if (empty($filters)) { + $this->runAction($action); + } + else + { + $priorAction = $this->_action; + $this->_action = $action; + CFilterChain::create($this, $action, $filters)->run(); + $this->_action = $priorAction; + } + } + + /** + * Runs the action after passing through all filters. + * This method is invoked by {@link runActionWithFilters} after all possible filters have been executed + * and the action starts to run. + * @param CAction $action action to run + */ + public function runAction($action) + { + $priorAction = $this->_action; + $this->_action = $action; + if ($this->beforeAction($action)) { + if ($action->runWithParams($this->getActionParams()) === false) { + $this->invalidActionParams($action); + } + else + { + $this->afterAction($action); + } + } + $this->_action = $priorAction; + } + + /** + * Returns the request parameters that will be used for action parameter binding. + * By default, this method will return $_GET. You may override this method if you + * want to use other request parameters (e.g. $_GET+$_POST). + * @return array the request parameters to be used for action parameter binding + * @since 1.1.7 + */ + public function getActionParams() + { + return $_GET; + } + + /** + * This method is invoked when the request parameters do not satisfy the requirement of the specified action. + * The default implementation will throw a 400 HTTP exception. + * @param CAction $action the action being executed + * @since 1.1.7 + */ + public function invalidActionParams($action) + { + throw new CHttpException(400, Yii::t('yii', 'Your request is invalid.')); + } + + /** + * Creates the action instance based on the action name. + * The action can be either an inline action or an object. + * The latter is created by looking up the action map specified in {@link actions}. + * @param string $actionID ID of the action. If empty, the {@link defaultAction default action} will be used. + * @return CAction the action instance, null if the action does not exist. + * @see actions + */ + public function createAction($actionID) + { + if ($actionID === '') { + $actionID = $this->defaultAction; + } + if (method_exists($this, 'action' . $actionID) && strcasecmp($actionID, 's')) // we have actions method + { + return new CInlineAction($this, $actionID); + } + else + { + $action = $this->createActionFromMap($this->actions(), $actionID, $actionID); + if ($action !== null && !method_exists($action, 'run')) { + throw new CException(Yii::t('yii', 'Action class {class} must implement the "run" method.', array('{class}' => get_class($action)))); + } + return $action; + } + } + + /** + * Creates the action instance based on the action map. + * This method will check to see if the action ID appears in the given + * action map. If so, the corresponding configuration will be used to + * create the action instance. + * @param array $actionMap the action map + * @param string $actionID the action ID that has its prefix stripped off + * @param string $requestActionID the originally requested action ID + * @param array $config the action configuration that should be applied on top of the configuration specified in the map + * @return CAction the action instance, null if the action does not exist. + */ + protected function createActionFromMap($actionMap, $actionID, $requestActionID, $config = array()) + { + if (($pos = strpos($actionID, '.')) === false && isset($actionMap[$actionID])) { + $baseConfig = is_array($actionMap[$actionID]) ? $actionMap[$actionID] : array('class' => $actionMap[$actionID]); + return Yii::createComponent(empty($config) ? $baseConfig : array_merge($baseConfig, $config), $this, $requestActionID); + } + else { + if ($pos === false) { + return null; + } + } + + // the action is defined in a provider + $prefix = substr($actionID, 0, $pos + 1); + if (!isset($actionMap[$prefix])) { + return null; + } + $actionID = (string)substr($actionID, $pos + 1); + + $provider = $actionMap[$prefix]; + if (is_string($provider)) { + $providerType = $provider; + } + else { + if (is_array($provider) && isset($provider['class'])) { + $providerType = $provider['class']; + if (isset($provider[$actionID])) { + if (is_string($provider[$actionID])) { + $config = array_merge(array('class' => $provider[$actionID]), $config); + } + else + { + $config = array_merge($provider[$actionID], $config); + } + } + } + else + { + throw new CException(Yii::t('yii', 'Object configuration must be an array containing a "class" element.')); + } + } + + $class = Yii::import($providerType, true); + $map = call_user_func(array($class, 'actions')); + + return $this->createActionFromMap($map, $actionID, $requestActionID, $config); + } + + /** + * Handles the request whose action is not recognized. + * This method is invoked when the controller cannot find the requested action. + * The default implementation simply throws an exception. + * @param string $actionID the missing action name + * @throws CHttpException whenever this method is invoked + */ + public function missingAction($actionID) + { + throw new CHttpException(404, Yii::t('yii', 'The system is unable to find the requested action "{action}".', + array('{action}' => $actionID == '' ? $this->defaultAction : $actionID))); + } + + /** + * @return CAction the action currently being executed, null if no active action. + */ + public function getAction() + { + return $this->_action; + } + + /** + * @param CAction $value the action currently being executed. + */ + public function setAction($value) + { + $this->_action = $value; + } + + /** + * @return string ID of the controller + */ + public function getId() + { + return $this->_id; + } + + /** + * @return string the controller ID that is prefixed with the module ID (if any). + */ + public function getUniqueId() + { + return $this->_module ? $this->_module->getId() . '/' . $this->_id : $this->_id; + } + + /** + * @return string the route (module ID, controller ID and action ID) of the current request. + * @since 1.1.0 + */ + public function getRoute() + { + if (($action = $this->getAction()) !== null) { + return $this->getUniqueId() . '/' . $action->getId(); + } + else + { + return $this->getUniqueId(); + } + } + + /** + * @return CWebModule the module that this controller belongs to. It returns null + * if the controller does not belong to any module + */ + public function getModule() + { + return $this->_module; + } + + /** + * Processes the request using another controller action. + * This is like {@link redirect}, but the user browser's URL remains unchanged. + * In most cases, you should call {@link redirect} instead of this method. + * @param string $route the route of the new controller action. This can be an action ID, or a complete route + * with module ID (optional in the current module), controller ID and action ID. If the former, the action is assumed + * to be located within the current controller. + * @param boolean $exit whether to end the application after this call. Defaults to true. + * @since 1.1.0 + */ + public function forward($route, $exit = true) + { + if (strpos($route, '/') === false) { + $this->run($route); + } + else + { + if ($route[0] !== '/' && ($module = $this->getModule()) !== null) { + $route = $module->getId() . '/' . $route; + } + Yii::app()->runController($route); + } + if ($exit) { + Yii::app()->end(); + } + } + + /** + * This method is invoked right before an action is to be executed (after all possible filters.) + * You may override this method to do last-minute preparation for the action. + * @param CAction $action the action to be executed. + * @return boolean whether the action should be executed. + */ + protected function beforeAction($action) + { + return true; + } + + /** + * This method is invoked right after an action is executed. + * You may override this method to do some postprocessing for the action. + * @param CAction $action the action just executed. + */ + protected function afterAction($action) + { + } +} diff --git a/framework/base/ErrorHandler.php b/framework/base/ErrorHandler.php index 7abd43e..7bf81e1 100644 --- a/framework/base/ErrorHandler.php +++ b/framework/base/ErrorHandler.php @@ -12,9 +12,16 @@ namespace yii\base; /** * ErrorHandler handles uncaught PHP errors and exceptions. * - * It displays these errors using appropriate views based on the - * nature of the error and the mode the application runs at. - * It also chooses the most preferred language for displaying the error. + * ErrorHandler displays these errors using appropriate views based on the + * nature of the errors and the mode the application runs at. + * + * ErrorHandler does the following when the application encounters an error or exception: + * + * - If it is a PHP error, warning or notice, an ErrorException will be thrown which + * if not caught, will be handled in the next few steps + * - If it is an uncaught exception, it will invoke the action defined by [[errorAction]] + * to handle the exception; + * - If [[errorAction]] is not defined, it will * * ErrorHandler uses two sets of views: *
      @@ -58,7 +65,6 @@ class ErrorHandler extends ApplicationComponent * @var integer maximum number of source code lines to be displayed. Defaults to 25. */ public $maxSourceLines = 25; - /** * @var integer maximum number of trace source code lines to be displayed. Defaults to 10. */ @@ -90,7 +96,7 @@ class ErrorHandler extends ApplicationComponent /** * Handles PHP execution errors such as warnings, notices. * - * This method is implemented as a PHP error handler. It requires + * This method is used as a PHP error handler. It requires * that constant YII_ENABLE_ERROR_HANDLER be defined true. * * This method will first raise an `error` event. @@ -135,19 +141,17 @@ class ErrorHandler extends ApplicationComponent protected function render($exception) { - if (\Yii::$application instanceof \yii\web\Application) { - if ($this->errorAction !== null) { - \Yii::$application->runController($this->errorAction); + if ($this->errorAction !== null) { + \Yii::$application->runController($this->errorAction); + } elseif (\Yii::$application instanceof \yii\web\Application) { + if (!headers_sent()) { + $errorCode = $exception instanceof HttpException ? $exception->statusCode : 500; + header("HTTP/1.0 $errorCode " . get_class($exception)); + } + if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') { + $this->renderAsText($exception); } else { - if (!headers_sent()) { - $errorCode = $exception instanceof HttpException ? $exception->statusCode : 500; - header("HTTP/1.0 $errorCode " . get_class($exception)); - } - if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') { - $this->renderAsText($exception); - } else { - $this->renderAsHtml($exception); - } + $this->renderAsHtml($exception); } } else { $this->renderAsText($exception); diff --git a/framework/base/InlineAction.php b/framework/base/InlineAction.php new file mode 100644 index 0000000..83de4af --- /dev/null +++ b/framework/base/InlineAction.php @@ -0,0 +1,53 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + + +/** + * CInlineAction represents an action that is defined as a controller method. + * + * The method name is like 'actionXYZ' where 'XYZ' stands for the action name. + * + * @author Qiang Xue + * @version $Id$ + * @package system.web.actions + * @since 1.0 + */ +class CInlineAction extends CAction +{ + /** + * Runs the action. + * The action method defined in the controller is invoked. + * This method is required by {@link CAction}. + */ + public function run() + { + $method='action'.$this->getId(); + $this->getController()->$method(); + } + + /** + * Runs the action with the supplied request parameters. + * This method is internally called by {@link CController::runAction()}. + * @param array $params the request parameters (name=>value) + * @return boolean whether the request parameters are valid + * @since 1.1.7 + */ + public function runWithParams($params) + { + $methodName='action'.$this->getId(); + $controller=$this->getController(); + $method=new ReflectionMethod($controller, $methodName); + if($method->getNumberOfParameters()>0) + return $this->runWithParamsInternal($controller, $method, $params); + else + return $controller->$methodName(); + } + +} diff --git a/framework/base/InlineActionFilter.php b/framework/base/InlineActionFilter.php new file mode 100644 index 0000000..9d7b18a --- /dev/null +++ b/framework/base/InlineActionFilter.php @@ -0,0 +1,61 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CInlineFilter represents a filter defined as a controller method. + * + * CInlineFilter executes the 'filterXYZ($action)' method defined + * in the controller, where the name 'XYZ' can be retrieved from the {@link name} property. + * + * @author Qiang Xue + * @version $Id$ + * @package system.web.filters + * @since 1.0 + */ +class CInlineFilter extends CFilter +{ + /** + * @var string name of the filter. It stands for 'XYZ' in the filter method name 'filterXYZ'. + */ + public $name; + + /** + * Creates an inline filter instance. + * The creation is based on a string describing the inline method name + * and action names that the filter shall or shall not apply to. + * @param CController $controller the controller who hosts the filter methods + * @param string $filterName the filter name + * @return CInlineFilter the created instance + * @throws CException if the filter method does not exist + */ + public static function create($controller,$filterName) + { + if(method_exists($controller,'filter'.$filterName)) + { + $filter=new CInlineFilter; + $filter->name=$filterName; + return $filter; + } + else + throw new CException(Yii::t('yii','Filter "{filter}" is invalid. Controller "{class}" does not have the filter method "filter{filter}".', + array('{filter}'=>$filterName, '{class}'=>get_class($controller)))); + } + + /** + * Performs the filtering. + * This method calls the filter method defined in the controller class. + * @param CFilterChain $filterChain the filter chain that the filter is on. + */ + public function filter($filterChain) + { + $method='filter'.$this->name; + $filterChain->controller->$method($filterChain); + } +} diff --git a/framework/base/Module.php b/framework/base/Module.php index 4c2b34a..40c595f 100644 --- a/framework/base/Module.php +++ b/framework/base/Module.php @@ -136,10 +136,11 @@ abstract class Module extends Component implements Initable */ public function setBasePath($path) { - if (($p = realpath($path)) === false || !is_dir($p)) { + $p = \Yii::getAlias($path); + if ($p === false || !is_dir($p)) { throw new Exception('Invalid base path: ' . $path); } else { - $this->_basePath = $p; + $this->_basePath = realpath($p); } }