diff --git a/framework/YiiBase.php b/framework/YiiBase.php index 9d3698b..1230053 100644 --- a/framework/YiiBase.php +++ b/framework/YiiBase.php @@ -121,8 +121,8 @@ class YiiBase * * To import a class or a directory, one can use either path alias or class name (can be namespaced): * - * - `@app/components/GoogleMap`: importing the `GoogleMap` class with a path alias; - * - `@app/components/*`: importing the whole `components` directory with a path alias; + * - `@application/components/GoogleMap`: importing the `GoogleMap` class with a path alias; + * - `@application/components/*`: importing the whole `components` directory with a path alias; * - `GoogleMap`: importing the `GoogleMap` class with a class name. [[autoload()]] will be used * when this class is used for the first time. * @@ -322,12 +322,12 @@ class YiiBase * the class. For example, * * - `\app\components\GoogleMap`: fully-qualified namespaced class. - * - `@app/components/GoogleMap`: an alias + * - `@application/components/GoogleMap`: an alias * * Below are some usage examples: * * ~~~ - * $object = \Yii::createObject('@app/components/GoogleMap'); + * $object = \Yii::createObject('@application/components/GoogleMap'); * $object = \Yii::createObject(array( * 'class' => '\app\components\GoogleMap', * 'apiKey' => 'xyz', diff --git a/framework/base/Application.php b/framework/base/Application.php index c618faa..6203d11 100644 --- a/framework/base/Application.php +++ b/framework/base/Application.php @@ -10,8 +10,8 @@ namespace yii\base; use Yii; +use yii\util\FileHelper; use yii\base\InvalidCallException; -use yii\util\StringHelper; /** * Application is the base class for all application classes. @@ -37,7 +37,7 @@ use yii\util\StringHelper; * Yii framework messages. This application component is dynamically loaded when needed. * * - * Application will undergo the following lifecycles when processing a user request: + * Application will undergo the following life cycles when processing a user request: *
    *
  1. load application configuration;
  2. *
  3. set up class autoloader and error handling;
  4. @@ -50,28 +50,6 @@ use yii\util\StringHelper; * Starting from lifecycle 3, if a PHP error or an uncaught exception occurs, * the application will switch to its error handling logic and jump to step 6 afterwards. * - * @property string $basePath Returns the root path of the application. - * @property CCache $cache Returns the cache component. - * @property CPhpMessageSource $coreMessages Returns the core message translations. - * @property CDateFormatter $dateFormatter Returns the locale-dependent date formatter. - * @property \yii\db\Connection $db Returns the database connection component. - * @property CErrorHandler $errorHandler Returns the error handler component. - * @property string $extensionPath Returns the root directory that holds all third-party extensions. - * @property string $id Returns the unique identifier for the application. - * @property string $language Returns the language that the user is using and the application should be targeted to. - * @property CLocale $locale Returns the locale instance. - * @property string $localeDataPath Returns the directory that contains the locale data. - * @property CMessageSource $messages Returns the application message translations component. - * @property CNumberFormatter $numberFormatter The locale-dependent number formatter. - * @property CHttpRequest $request Returns the request component. - * @property string $runtimePath Returns the directory that stores runtime files. - * @property CSecurityManager $securityManager Returns the security manager component. - * @property CStatePersister $statePersister Returns the state persister component. - * @property string $timeZone Returns the time zone used by this application. - * @property UrlManager $urlManager Returns the URL manager component. - * @property string $baseUrl Returns the relative URL for the application - * @property string $homeUrl the homepage URL - * * @author Qiang Xue * @since 2.0 */ @@ -134,7 +112,7 @@ class Application extends Module $this->setBasePath($basePath); $this->registerDefaultAliases(); $this->registerCoreComponents(); - parent::__construct($id, $this, $config); + Component::__construct($config); } /** @@ -204,27 +182,6 @@ class Application extends Module } /** - * Runs a controller with the given route and parameters. - * @param string $route the route (e.g. `post/create`) - * @param array $params the parameters to be passed to the controller action - * @return integer the exit status (0 means normal, non-zero values mean abnormal) - * @throws InvalidRequestException if the route cannot be resolved into a controller - */ - public function runController($route, $params = array()) - { - $result = $this->createController($route); - if ($result === false) { - throw new InvalidRequestException(Yii::t('yii', 'Unable to resolve the request.')); - } - - $priorController = $this->controller; - $this->controller = $controllerObject; - $status = $controllerObject->run($action, $params); - $this->controller = $priorController; - return $status; - } - - /** * Returns the directory that stores runtime files. * @return string the directory that stores runtime files. Defaults to 'protected/runtime'. */ @@ -239,15 +196,15 @@ class Application extends Module /** * Sets the directory that stores runtime files. * @param string $path the directory that stores runtime files. - * @throws InvalidCallException if the directory does not exist or is not writable + * @throws InvalidConfigException if the directory does not exist or is not writable */ public function setRuntimePath($path) { - $p = Yii::getAlias($path); - if ($p === false || !is_dir($p) || !is_writable($path)) { - throw new InvalidCallException("Application runtime path \"$path\" is invalid. Please make sure it is a directory writable by the Web server process."); - } else { + $p = FileHelper::ensureDirectory($path); + if (is_writable($p)) { $this->_runtimePath = $p; + } else { + throw new InvalidConfigException("Runtime path must be writable by the Web server process: $path"); } } @@ -296,34 +253,61 @@ class Application extends Module date_default_timezone_set($value); } - /** - * Returns the locale instance. - * @param string $localeID the locale ID (e.g. en_US). If null, the {@link getLanguage application language ID} will be used. - * @return CLocale the locale instance - */ - public function getLocale($localeID = null) - { - return CLocale::getInstance($localeID === null ? $this->getLanguage() : $localeID); - } - - /** - * @return CNumberFormatter the locale-dependent number formatter. - * The current {@link getLocale application locale} will be used. - */ - public function getNumberFormatter() - { - return $this->getLocale()->getNumberFormatter(); - } - - /** - * Returns the locale-dependent date formatter. - * @return CDateFormatter the locale-dependent date formatter. - * The current {@link getLocale application locale} will be used. - */ - public function getDateFormatter() - { - return $this->getLocale()->getDateFormatter(); - } +// /** +// * Returns the security manager component. +// * @return SecurityManager the security manager application component. +// */ +// public function getSecurityManager() +// { +// return $this->getComponent('securityManager'); +// } +// +// /** +// * Returns the locale instance. +// * @param string $localeID the locale ID (e.g. en_US). If null, the {@link getLanguage application language ID} will be used. +// * @return CLocale the locale instance +// */ +// public function getLocale($localeID = null) +// { +// return CLocale::getInstance($localeID === null ? $this->getLanguage() : $localeID); +// } +// +// /** +// * @return CNumberFormatter the locale-dependent number formatter. +// * The current {@link getLocale application locale} will be used. +// */ +// public function getNumberFormatter() +// { +// return $this->getLocale()->getNumberFormatter(); +// } +// +// /** +// * Returns the locale-dependent date formatter. +// * @return CDateFormatter the locale-dependent date formatter. +// * The current {@link getLocale application locale} will be used. +// */ +// public function getDateFormatter() +// { +// return $this->getLocale()->getDateFormatter(); +// } +// +// /** +// * Returns the core message translations component. +// * @return \yii\i18n\MessageSource the core message translations +// */ +// public function getCoreMessages() +// { +// return $this->getComponent('coreMessages'); +// } +// +// /** +// * Returns the application message translations component. +// * @return \yii\i18n\MessageSource the application message translations +// */ +// public function getMessages() +// { +// return $this->getComponent('messages'); +// } /** * Returns the database connection component. @@ -353,15 +337,6 @@ class Application extends Module } /** - * Returns the security manager component. - * @return SecurityManager the security manager application component. - */ - public function getSecurityManager() - { - return $this->getComponent('securityManager'); - } - - /** * Returns the cache component. * @return \yii\caching\Cache the cache application component. Null if the component is not enabled. */ @@ -371,24 +346,6 @@ class Application extends Module } /** - * Returns the core message translations component. - * @return \yii\i18n\MessageSource the core message translations - */ - public function getCoreMessages() - { - return $this->getComponent('coreMessages'); - } - - /** - * Returns the application message translations component. - * @return \yii\i18n\MessageSource the application message translations - */ - public function getMessages() - { - return $this->getComponent('messages'); - } - - /** * Returns the request component. * @return Request the request component */ @@ -417,15 +374,6 @@ class Application extends Module 'errorHandler' => array( 'class' => 'yii\base\ErrorHandler', ), - 'request' => array( - 'class' => 'yii\base\Request', - ), - 'response' => array( - 'class' => 'yii\base\Response', - ), - 'format' => array( - 'class' => 'yii\base\Formatter', - ), 'coreMessages' => array( 'class' => 'yii\i18n\PhpMessageSource', 'language' => 'en_us', @@ -442,117 +390,4 @@ class Application extends Module ), )); } - - /** - * Performs a controller action specified by a route. - * This method parses the specified route and creates the corresponding controller and action - * instances under the context of the specified module. It then runs the created action - * with the given parameters. - * @param string $route the route that specifies the action. - * @param array $params the parameters to be passed to the action - * @param Module $module the module which serves as the context of the route - * @return integer the action - * @throws InvalidConfigException if the module's defaultRoute is empty or the controller's defaultAction is empty - * @throws InvalidRequestException if the requested route cannot be resolved into an action successfully - */ - public function runAction($route, $params = array(), $module = null) - { - if ($module === null) { - $module = $this; - } - $route = trim($route, '/'); - if ($route === '') { - $route = trim($module->defaultRoute, '/'); - if ($route == '') { - throw new InvalidConfigException(get_class($module) . '::defaultRoute cannot be empty.'); - } - } - if (($pos = strpos($route, '/')) !== false) { - $id = substr($route, 0, $pos); - $route = substr($route, $pos + 1); - } else { - $id = $route; - $route = ''; - } - - $childModule = $module->getModule($id); - if ($childModule !== null) { - return $this->runAction($route, $params, $childModule); - } - - $controller = $this->createController($id, $module); - if ($controller !== null) { - if ($route === '') { - $route = $controller->defaultAction; - if ($route == '') { - throw new InvalidConfigException(get_class($controller) . '::defaultAction cannot be empty.'); - } - } - - $action = $this->createAction($route, $controller); - if ($action !== null) { - return $action->runWithParams($params); - } - } - - throw new InvalidRequestException("Unable to resolve the request: $route"); - } - - - /** - * Creates a controller instance based on the controller ID. - * - * The controller is created within the given module. The method first attempts to - * create the controller based on the [[controllerMap]] of the module. If not available, - * it will look for the controller class under the [[controllerPath]] and create an - * instance of it. - * - * @param string $id the controller ID - * @param Module $module the module that owns the controller - * @return Controller the newly created controller instance - */ - public function createController($id, $module) - { - if (isset($module->controllerMap[$id])) { - return Yii::createObject($module->controllerMap[$id], $id, $module); - } elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) { - $className = StringHelper::id2camel($id) . 'Controller'; - $classFile = $module->controllerPath . DIRECTORY_SEPARATOR . $className . '.php'; - if (is_file($classFile)) { - $className = $module->controllerNamespace . '\\' . $className; - if (!class_exists($className, false)) { - require($classFile); - } - if (class_exists($className, false) && is_subclass_of($className, '\yii\base\Controller')) { - return new $className($id, $module); - } - } - } - return null; - } - - /** - * Creates an action based on the given action ID. - * The action is created within the given controller. The method first attempts to - * create the action based on [[Controller::actions()]]. If not available, - * it will look for the inline action method within the controller. - * @param string $id the action ID - * @param Controller $controller the controller that owns the action - * @return Action the newly created action instance - */ - public function createAction($id, $controller) - { - if (isset($controller->actionMap[$id])) { - return Yii::createObject($controller->actionMap[$id], $id, $controller); - } elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) { - $methodName = 'action' . StringHelper::id2camel($id); - if (method_exists($controller, $methodName)) { - $method = new \ReflectionMethod($controller, $methodName); - if ($method->getName() === $methodName) { - return new InlineAction($id, $controller); - } - } - } - return null; - } } diff --git a/framework/base/Controller.php b/framework/base/Controller.php index d62cc35..79ad574 100644 --- a/framework/base/Controller.php +++ b/framework/base/Controller.php @@ -9,6 +9,9 @@ namespace yii\base; +use Yii; +use yii\util\StringHelper; + /** * Controller is the base class for classes containing controller logic. * @@ -27,6 +30,10 @@ namespace yii\base; */ class Controller extends Component { + const EVENT_AUTHORIZE = 'authorize'; + const EVENT_BEFORE_ACTION = 'beforeAction'; + const EVENT_AFTER_ACTION = 'afterAction'; + /** * @var string the ID of this controller */ @@ -91,65 +98,138 @@ class Controller extends Component } /** - * Runs the controller with the specified action and parameters. - * @param Action|string $action the action to be executed. This can be either an action object - * or the ID of the action. + * Runs an action with the specified action ID and parameters. + * If the action ID is empty, the method will use [[defaultAction]]. + * @param string $id the ID of the action to be executed. * @param array $params the parameters (name-value pairs) to be passed to the action. - * If null, the result of [[getActionParams()]] will be used as action parameters. - * @return integer the exit status of the action. 0 means normal, other values mean abnormal. - * @see missingAction + * @return integer the status of the action execution. 0 means normal, other values mean abnormal. + * @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully. * @see createAction */ - public function run($action, $params = null) + public function runAction($id, $params = array()) { - if (is_string($action)) { - if (($a = $this->createAction($action)) !== null) { - $action = $a; - } else { - $this->missingAction($action); - return 1; - } + if ($id === '') { + $id = $this->defaultAction; } - $priorAction = $this->action; - $this->action = $action; + $action = $this->createAction($id); + if ($action !== null) { + $oldAction = $this->action; + $this->action = $action; - if ($this->authorize($action) && $this->beforeAction($action)) { - if ($params === null) { - $params = $this->getActionParams(); + if ($this->authorize($action) && $this->beforeAction($action)) { + $status = $action->runWithParams($params); + $this->afterAction($action); + } else { + $status = 1; } - $status = $action->runWithParams($params); - $this->afterAction($action); + + $this->action = $oldAction; + + return $status; } else { - $status = 1; + throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id); } + } - $this->action = $priorAction; + /** + * Runs a request specified in terms of a route. + * The route can be either an ID of an action within this controller or a complete route consisting + * of module IDs, controller ID and action ID. If the route starts with a slash '/', the parsing of + * the route will start from the application; otherwise, it will start from the parent module of this controller. + * @param string $route the route to be handled, e.g., 'view', 'comment/view', '/admin/comment/view'. + * @param array $params the parameters to be passed to the action. + * @return integer the status code returned by the action execution. 0 means normal, and other values mean abnormal. + * @see runAction + * @see forward + */ + public function run($route, $params = array()) + { + $pos = strpos($route, '/'); + if ($pos === false) { + return $this->runAction($route, $params); + } elseif ($pos > 0) { + return $this->module->runAction($route, $params); + } else { + return \Yii::$application->runAction($route, $params); + } + } - return $status; + /** + * Forwards the current execution flow to handle a new request specified by a route. + * The only difference between this method and [[run()]] is that after calling this method, + * the application will exit. + * @param string $route the route to be handled, e.g., 'view', 'comment/view', '/admin/comment/view'. + * @param array $params the parameters to be passed to the action. + * @return integer the status code returned by the action execution. 0 means normal, and other values mean abnormal. + * @see run + */ + public function forward($route, $params = array()) + { + $status = $this->run($route, $params); + exit($status); } /** - * Creates the action instance based on the action ID. - * The action can be either an inline action or an object. - * The latter is created by looking up the action map specified in [[actions]]. - * @param string $actionID ID of the action. If empty, it will take the value of [[defaultAction]]. - * @return Action the action instance, null if the action does not exist. - * @see actions + * Creates an action based on the given action ID. + * The method first checks if the action ID has been declared in [[actions()]]. If so, + * it will use the configuration declared there to create the action object. + * If not, it will look for a controller method whose name is in the format of `actionXyz` + * where `Xyz` stands for the action ID. If found, an [[InlineAction]] representing that + * method will be created and returned. + * @param string $id the action ID + * @return Action the newly created action instance. Null if the ID doesn't resolve into any action. */ - public function createAction($actionID) + public function createAction($id) { - if ($actionID === '') { - $actionID = $this->defaultAction; - } - $actions = $this->actions(); - if (isset($actions[$actionID])) { - return \Yii::createObject($actions[$actionID], $actionID, $this); - } elseif (method_exists($this, 'action' . $actionID)) { - return new InlineAction($actionID, $this); - } else { - return null; + $actionMap = $this->actions(); + if (isset($actionMap[$id])) { + return Yii::createObject($actionMap[$id], $id, $this); + } elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) { + $methodName = 'action' . StringHelper::id2camel($id); + if (method_exists($this, $methodName)) { + $method = new \ReflectionMethod($this, $methodName); + if ($method->getName() === $methodName) { + return new InlineAction($id, $this); + } + } } + return null; + } + + /** + * This method is invoked when checking the access for the action to be executed. + * @param Action $action the action to be executed. + * @return boolean whether the action is allowed to be executed. + */ + public function authorize($action) + { + $event = new ActionEvent($action); + $this->trigger(self::EVENT_AUTHORIZE, $event); + return $event->isValid; + } + + /** + * 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 Action $action the action to be executed. + * @return boolean whether the action should continue to be executed. + */ + public function beforeAction($action) + { + $event = new ActionEvent($action); + $this->trigger(self::EVENT_BEFORE_ACTION, $event); + return $event->isValid; + } + + /** + * This method is invoked right after an action is executed. + * You may override this method to do some postprocessing for the action. + * @param Action $action the action just executed. + */ + public function afterAction($action) + { + $this->trigger(self::EVENT_AFTER_ACTION, new ActionEvent($action)); } /** @@ -217,67 +297,6 @@ class Controller extends Component return $this->action !== null ? $this->getUniqueId() . '/' . $this->action->id : $this->getUniqueId(); } - /** - * Processes the request using another controller action. - * @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 array $params the parameters to be passed to the action. - * If null, the result of [[getActionParams()]] will be used as action parameters. - * Note that the parameters must be name-value pairs with the names corresponding to - * the parameter names as declared by the action. - * @param boolean $exit whether to end the application after this call. Defaults to true. - */ - public function forward($route, $params = array(), $exit = true) - { - if (strpos($route, '/') === false) { - $status = $this->run($route, $params); - } else { - if ($route[0] !== '/' && !$this->module instanceof Application) { - $route = '/' . $this->module->getUniqueId() . '/' . $route; - } - $status = \Yii::$application->runController($route, $params); - } - if ($exit) { - \Yii::$application->end($status); - } - } - - /** - * This method is invoked when checking the access for the action to be executed. - * @param Action $action the action to be executed. - * @return boolean whether the action is allowed to be executed. - */ - public function authorize($action) - { - $event = new ActionEvent($action); - $this->trigger(__METHOD__, $event); - return $event->isValid; - } - - /** - * 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 Action $action the action to be executed. - * @return boolean whether the action should continue to be executed. - */ - public function beforeAction($action) - { - $event = new ActionEvent($action); - $this->trigger(__METHOD__, $event); - return $event->isValid; - } - - /** - * This method is invoked right after an action is executed. - * You may override this method to do some postprocessing for the action. - * @param Action $action the action just executed. - */ - public function afterAction($action) - { - $this->trigger(__METHOD__, new ActionEvent($action)); - } - public function render($view, $params = array()) { return $this->createView()->render($view, $params); diff --git a/framework/base/InvalidRouteException.php b/framework/base/InvalidRouteException.php new file mode 100644 index 0000000..8e82b40 --- /dev/null +++ b/framework/base/InvalidRouteException.php @@ -0,0 +1,21 @@ + + * @since 2.0 + */ +class InvalidRouteException extends \Exception +{ +} + diff --git a/framework/base/Module.php b/framework/base/Module.php index 0539cdc..23bd577 100644 --- a/framework/base/Module.php +++ b/framework/base/Module.php @@ -16,13 +16,19 @@ use yii\util\FileHelper; /** * Module is the base class for module and application classes. * - * Module mainly manages application components and sub-modules that belongs to a module. + * A module represents a sub-application which contains MVC elements by itself, such as + * models, views, controllers, etc. + * + * A module may consist of [[modules|sub-modules]]. + * + * [[components|Components]] may be registered with the module so that they are globally + * accessible within the module. * * @property string $uniqueId An ID that uniquely identifies this module among all modules within * the current application. * @property string $basePath The root directory of the module. Defaults to the directory containing the module class. * @property array $modules The configuration of the currently installed modules (module ID => configuration). - * @property array $components The application components (indexed by their IDs). + * @property array $components The components (indexed by their IDs) registered within this module. * @property array $import List of aliases to be imported. This property is write-only. * @property array $aliases List of aliases to be defined. This property is write-only. * @@ -36,7 +42,7 @@ abstract class Module extends Component */ public $params = array(); /** - * @var array the IDs of the application components that should be preloaded when this module is created. + * @var array the IDs of the components that should be preloaded when this module is created. */ public $preload = array(); /** @@ -88,27 +94,27 @@ abstract class Module extends Component /** * @var string the root directory of the module. */ - protected $_basePath; + private $_basePath; /** * @var string the root directory that contains view files for this module */ - protected $_viewPath; + private $_viewPath; /** * @var string the root directory that contains layout view files for this module. */ - protected $_layoutPath; + private $_layoutPath; /** * @var string the directory containing controller classes in the module. */ - protected $_controllerPath; + private $_controllerPath; /** * @var array child modules of this module */ - protected $_modules = array(); + private $_modules = array(); /** - * @var array application components of this module + * @var array components registered under this module */ - protected $_components = array(); + private $_components = array(); /** * Constructor. @@ -125,9 +131,9 @@ abstract class Module extends Component /** * Getter magic method. - * This method is overridden to support accessing application components + * This method is overridden to support accessing components * like reading module properties. - * @param string $name application component or property name + * @param string $name component or property name * @return mixed the named property value */ public function __get($name) @@ -142,7 +148,7 @@ abstract class Module extends Component /** * Checks if a property value is null. * This method overrides the parent implementation by checking - * if the named application component is loaded. + * if the named component is loaded. * @param string $name the property name or the event name * @return boolean whether the property value is null */ @@ -163,7 +169,7 @@ abstract class Module extends Component */ public function init() { - \Yii::setAlias('@' . $this->id, $this->getBasePath()); + Yii::setAlias('@' . $this->id, $this->getBasePath()); $this->preloadComponents(); } @@ -282,19 +288,19 @@ abstract class Module extends Component /** * Imports the specified path aliases. * This method is provided so that you can import a set of path aliases when configuring a module. - * The path aliases will be imported by calling [[\Yii::import()]]. + * The path aliases will be imported by calling [[Yii::import()]]. * @param array $aliases list of path aliases to be imported */ public function setImport($aliases) { foreach ($aliases as $alias) { - \Yii::import($alias); + Yii::import($alias); } } /** * Defines path aliases. - * This method calls [[\Yii::setAlias()]] to register the path aliases. + * This method calls [[Yii::setAlias()]] to register the path aliases. * This method is provided so that you can define path aliases when configuring a module. * @param array $aliases list of path aliases to be defined. The array keys are alias names * (must start with '@') and the array values are the corresponding paths or aliases. @@ -302,7 +308,7 @@ abstract class Module extends Component * * ~~~ * array( - * '@models' => '@app/models', // an existing alias + * '@models' => '@application/models', // an existing alias * '@backend' => __DIR__ . '/../backend', // a directory * ) * ~~~ @@ -310,7 +316,7 @@ abstract class Module extends Component public function setAliases($aliases) { foreach ($aliases as $name => $alias) { - \Yii::setAlias($name, $alias); + Yii::setAlias($name, $alias); } } @@ -339,8 +345,8 @@ abstract class Module extends Component if ($this->_modules[$id] instanceof Module) { return $this->_modules[$id]; } elseif ($load) { - \Yii::trace("Loading \"$id\" module", __CLASS__); - return $this->_modules[$id] = \Yii::createObject($this->_modules[$id], $id, $this); + Yii::trace("Loading module: $id", __CLASS__); + return $this->_modules[$id] = Yii::createObject($this->_modules[$id], $id, $this); } } return null; @@ -393,7 +399,7 @@ abstract class Module extends Component * * Each sub-module should be specified as a name-value pair, where * name refers to the ID of the module and value the module or a configuration - * array that can be used to create the module. In the latter case, [[\Yii::createObject()]] + * array that can be used to create the module. In the latter case, [[Yii::createObject()]] * will be used to create the module. * * If a new sub-module has the same ID as an existing one, the existing one will be overwritten silently. @@ -423,8 +429,8 @@ abstract class Module extends Component /** * Checks whether the named component exists. - * @param string $id application component ID - * @return boolean whether the named application component exists. Both loaded and unloaded components + * @param string $id component ID + * @return boolean whether the named component exists. Both loaded and unloaded components * are considered. */ public function hasComponent($id) @@ -433,11 +439,10 @@ abstract class Module extends Component } /** - * Retrieves the named application component. - * @param string $id application component ID (case-sensitive) + * Retrieves the named component. + * @param string $id component ID (case-sensitive) * @param boolean $load whether to load the component if it is not yet loaded. - * @return Component|null the application component instance, null if the application component - * does not exist. + * @return Component|null the component instance, null if the component does not exist. * @see hasComponent() */ public function getComponent($id, $load = true) @@ -446,22 +451,22 @@ abstract class Module extends Component if ($this->_components[$id] instanceof Component) { return $this->_components[$id]; } elseif ($load) { - \Yii::trace("Loading \"$id\" application component", __CLASS__); - return $this->_components[$id] = \Yii::createObject($this->_components[$id]); + Yii::trace("Loading component: $id", __CLASS__); + return $this->_components[$id] = Yii::createObject($this->_components[$id]); } } return null; } /** - * Registers an application component in this module. + * Registers a component with this module. * @param string $id component ID - * @param Component|array|null $component the component to be added to the module. This can + * @param Component|array|null $component the component to be registered with the module. This can * be one of the followings: * * - a [[Component]] object * - a configuration array: when [[getComponent()]] is called initially for this component, the array - * will be used to instantiate the component + * will be used to instantiate the component via [[Yii::createObject()]]. * - null: the named component will be removed from the module */ public function setComponent($id, $component) @@ -474,11 +479,11 @@ abstract class Module extends Component } /** - * Returns the application components. + * Returns the registered components. * @param boolean $loadedOnly whether to return the loaded components only. If this is set false, * then all components specified in the configuration will be returned, whether they are loaded or not. * Loaded components will be returned as objects, while unloaded components as configuration arrays. - * @return array the application components (indexed by their IDs) + * @return array the components (indexed by their IDs) */ public function getComponents($loadedOnly = false) { @@ -496,11 +501,11 @@ abstract class Module extends Component } /** - * Registers a set of application components in this module. + * Registers a set of components in this module. * - * Each application component should be specified as a name-value pair, where + * Each component should be specified as a name-value pair, where * name refers to the ID of the component and value the component or a configuration - * array that can be used to create the component. In the latter case, [[\Yii::createObject()]] + * array that can be used to create the component. In the latter case, [[Yii::createObject()]] * will be used to create the component. * * If a new component has the same ID as an existing one, the existing one will be overwritten silently. @@ -520,7 +525,7 @@ abstract class Module extends Component * ) * ~~~ * - * @param array $components application components (id => component configuration or instance) + * @param array $components components (id => component configuration or instance) */ public function setComponents($components) { @@ -530,7 +535,7 @@ abstract class Module extends Component } /** - * Loads application components that are declared in [[preload]]. + * Loads components that are declared in [[preload]]. */ public function preloadComponents() { @@ -540,54 +545,47 @@ abstract class Module extends Component } /** - * Performs a controller action specified by a route. - * This method parses the specified route and creates the corresponding controller and action - * instances under the context of the specified module. It then runs the created action - * with the given parameters. + * Runs a controller action specified by a route. + * This method parses the specified route and creates the corresponding child module(s), controller and action + * instances. It then calls [[Controller::runAction()]] to run the action with the given parameters. + * If the route is empty, the method will use [[defaultRoute]]. * @param string $route the route that specifies the action. * @param array $params the parameters to be passed to the action - * @return integer the action - * @throws InvalidConfigException if the module's defaultRoute is empty or the controller's defaultAction is empty - * @throws InvalidRequestException if the requested route cannot be resolved into an action successfully + * @return integer the status code returned by the action execution. 0 means normal, and other values mean abnormal. + * @throws InvalidRouteException if the requested route cannot be resolved into an action successfully */ public function runAction($route, $params = array()) { $route = trim($route, '/'); if ($route === '') { $route = trim($this->defaultRoute, '/'); - if ($route == '') { - throw new InvalidConfigException(get_class($this) . '::defaultRoute cannot be empty.'); - } } if (($pos = strpos($route, '/')) !== false) { $id = substr($route, 0, $pos); - $route = substr($route, $pos + 1); + $route2 = substr($route, $pos + 1); } else { $id = $route; - $route = ''; + $route2 = ''; } $module = $this->getModule($id); if ($module !== null) { - return $module->runAction($route, $params); + return $module->runAction($route2, $params); } $controller = $this->createController($id); if ($controller !== null) { - if ($route === '') { - $route = $controller->defaultAction; - if ($route == '') { - throw new InvalidConfigException(get_class($controller) . '::defaultAction cannot be empty.'); - } - } + $oldController = Yii::$application->controller; + Yii::$application->controller = $controller; - $action = $controller->createAction($route); - if ($action !== null) { - return $action->runWithParams($params); - } - } + $status = $controller->runAction($route2, $params); - throw new InvalidRequestException('Unable to resolve the request: ' . ltrim($this->getUniqueId() . '/' . $route, '/')); + Yii::$application->controller = $oldController; + + return $status; + } else { + throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $route); + } } /** diff --git a/framework/base/View.php b/framework/base/View.php index db0741a..9657025 100644 --- a/framework/base/View.php +++ b/framework/base/View.php @@ -97,7 +97,7 @@ class View extends Component * To determine which view file should be rendered, the method calls [[findViewFile()]] which * will search in the directories as specified by [[basePath]]. * - * View name can be a path alias representing an absolute file path (e.g. `@app/views/layout/index`), + * View name can be a path alias representing an absolute file path (e.g. `@application/views/layout/index`), * or a path relative to [[basePath]]. The file suffix is optional and defaults to `.php` if not given * in the view name. * diff --git a/framework/base/Widget.php b/framework/base/Widget.php index 3608205..686e58e 100644 --- a/framework/base/Widget.php +++ b/framework/base/Widget.php @@ -80,7 +80,7 @@ class Widget extends Component * To determine which view file should be rendered, the method calls [[findViewFile()]] which * will search in the directories as specified by [[basePath]]. * - * View name can be a path alias representing an absolute file path (e.g. `@app/views/layout/index`), + * View name can be a path alias representing an absolute file path (e.g. `@application/views/layout/index`), * or a path relative to [[basePath]]. The file suffix is optional and defaults to `.php` if not given * in the view name. * diff --git a/framework/logging/DbTarget.php b/framework/logging/DbTarget.php index 004bf21..129e4d4 100644 --- a/framework/logging/DbTarget.php +++ b/framework/logging/DbTarget.php @@ -89,16 +89,17 @@ class DbTarget extends Target } /** - * Stores log [[messages]] to DB. - * @param boolean $final whether this method is called at the end of the current application + * Stores log messages to DB. + * @param array $messages the messages to be exported. See [[Logger::messages]] for the structure + * of each message. */ - public function exportMessages($final) + public function export($messages) { $db = $this->getDb(); $tableName = $db->quoteTableName($this->tableName); $sql = "INSERT INTO $tableName (level, category, log_time, message) VALUES (:level, :category, :log_time, :message)"; $command = $db->createCommand($sql); - foreach ($this->messages as $message) { + foreach ($messages as $message) { $command->bindValues(array( ':level' => $message[1], ':category' => $message[2], diff --git a/framework/logging/EmailTarget.php b/framework/logging/EmailTarget.php index 73fd3bb..e02e4da 100644 --- a/framework/logging/EmailTarget.php +++ b/framework/logging/EmailTarget.php @@ -39,13 +39,14 @@ class EmailTarget extends Target public $headers = array(); /** - * Sends log [[messages]] to specified email addresses. - * @param boolean $final whether this method is called at the end of the current application + * Sends log messages to specified email addresses. + * @param array $messages the messages to be exported. See [[Logger::messages]] for the structure + * of each message. */ - public function exportMessages($final) + public function export($messages) { $body = ''; - foreach ($this->messages as $message) { + foreach ($messages as $message) { $body .= $this->formatMessage($message); } $body = wordwrap($body, 70); diff --git a/framework/logging/FileTarget.php b/framework/logging/FileTarget.php index f4ddf44..0eb897e 100644 --- a/framework/logging/FileTarget.php +++ b/framework/logging/FileTarget.php @@ -65,19 +65,28 @@ class FileTarget extends Target } /** - * Sends log [[messages]] to specified email addresses. - * @param boolean $final whether this method is called at the end of the current application + * Sends log messages to specified email addresses. + * @param array $messages the messages to be exported. See [[Logger::messages]] for the structure + * of each message. */ - public function exportMessages($final) + public function export($messages) { + $text = ''; + foreach ($messages as $message) { + $text .= $this->formatMessage($message); + } + $fp = @fopen($this->logFile, 'a'); + @flock($fp, LOCK_EX); if (@filesize($this->logFile) > $this->maxFileSize * 1024) { $this->rotateFiles(); + @flock($fp,LOCK_UN); + @fclose($fp); + @file_put_contents($this->logFile, $text, FILE_APPEND | LOCK_EX); + } else { + @fwrite($fp, $text); + @flock($fp,LOCK_UN); + @fclose($fp); } - $messages = array(); - foreach ($this->messages as $message) { - $messages[] = $this->formatMessage($message); - } - @file_put_contents($this->logFile, implode('', $messages), FILE_APPEND | LOCK_EX); } /** diff --git a/framework/logging/Logger.php b/framework/logging/Logger.php index c139193..a8ffb5e 100644 --- a/framework/logging/Logger.php +++ b/framework/logging/Logger.php @@ -8,16 +8,13 @@ */ namespace yii\logging; - -use yii\base\Event; -use yii\base\Exception; +use yii\base\InvalidConfigException; /** * Logger records logged messages in memory. * - * When [[flushInterval()]] is reached or when application terminates, it will - * call [[flush()]] to send logged messages to different log targets, such as - * file, email, Web. + * When the application ends or [[flushInterval]] is reached, Logger will call [[flush()]] + * to send logged messages to different log targets, such as file, email, Web. * * @author Qiang Xue * @since 2.0 @@ -25,15 +22,6 @@ use yii\base\Exception; class Logger extends \yii\base\Component { /** - * @event Event an event that is triggered when [[flush()]] is called. - */ - const EVENT_FLUSH = 'flush'; - /** - * @event Event an event that is triggered when [[flush()]] is called at the end of application. - */ - const EVENT_FINAL_FLUSH = 'finalFlush'; - - /** * Error message level. An error message is one that indicates the abnormal termination of the * application and may require developer's handling. */ @@ -82,7 +70,7 @@ class Logger extends \yii\base\Component * * ~~~ * array( - * [0] => message (mixed) + * [0] => message (mixed, can be a string or some complex data, such as an exception object) * [1] => level (integer) * [2] => category (string) * [3] => timestamp (float, obtained by microtime(true)) @@ -90,6 +78,10 @@ class Logger extends \yii\base\Component * ~~~ */ public $messages = array(); + /** + * @var Router the log target router registered with this logger. + */ + public $router; /** * Initializes the logger by registering [[flush()]] as a shutdown function. @@ -138,7 +130,9 @@ class Logger extends \yii\base\Component */ public function flush($final = false) { - $this->trigger($final ? self::EVENT_FINAL_FLUSH : self::EVENT_FLUSH); + if ($this->router) { + $this->router->dispatch($this->messages, $final); + } $this->messages = array(); } @@ -149,7 +143,7 @@ class Logger extends \yii\base\Component * of [[YiiBase]] class file. * @return float the total elapsed time in seconds for current request. */ - public function getExecutionTime() + public function getElapsedTime() { return microtime(true) - YII_BEGIN_TIME; } @@ -218,7 +212,7 @@ class Logger extends \yii\base\Component if (($last = array_pop($stack)) !== null && $last[0] === $token) { $timings[] = array($token, $category, $timestamp - $last[3]); } else { - throw new Exception("Unmatched profiling block: $token"); + throw new InvalidConfigException("Unmatched profiling block: $token"); } } } @@ -231,5 +225,4 @@ class Logger extends \yii\base\Component return $timings; } - } diff --git a/framework/logging/Router.php b/framework/logging/Router.php index 75fbbc0..2e6a8dd 100644 --- a/framework/logging/Router.php +++ b/framework/logging/Router.php @@ -81,26 +81,21 @@ class Router extends Component $this->targets[$name] = Yii::createObject($target); } } - - Yii::getLogger()->on(Logger::EVENT_FLUSH, array($this, 'processMessages')); - Yii::getLogger()->on(Logger::EVENT_FINAL_FLUSH, array($this, 'processMessages')); + Yii::getLogger()->router = $this; } /** - * Retrieves and processes log messages from the system logger. - * This method mainly serves the event handler to the [[Logger::EVENT_FLUSH]] event - * and the [[Logger::EVENT_FINAL_FLUSH]] event. - * It will retrieve the available log messages from the [[Yii::getLogger()|system logger]] - * and invoke the registered [[targets|log targets]] to do the actual processing. - * @param \yii\base\Event $event event parameter + * Dispatches log messages to [[targets]]. + * This method is called by [[Logger]] when its [[Logger::flush()]] method is called. + * It will forward the messages to each log target registered in [[targets]]. + * @param array $messages the messages to be processed + * @param boolean $final whether this is the final call during a request cycle */ - public function processMessages($event) + public function dispatch($messages, $final = false) { - $messages = Yii::getLogger()->messages; - $final = $event->name === Logger::EVENT_FINAL_FLUSH; foreach ($this->targets as $target) { if ($target->enabled) { - $target->processMessages($messages, $final); + $target->collect($messages, $final); } } } diff --git a/framework/logging/Target.php b/framework/logging/Target.php index a4e7714..c9e175a 100644 --- a/framework/logging/Target.php +++ b/framework/logging/Target.php @@ -50,15 +50,6 @@ abstract class Target extends \yii\base\Component */ public $except = array(); /** - * @var boolean whether to prefix each log message with the current session ID. Defaults to false. - */ - public $prefixSession = false; - /** - * @var boolean whether to prefix each log message with the current user name and ID. Defaults to false. - * @see \yii\web\User - */ - public $prefixUser = false; - /** * @var boolean whether to log a message containing the current user name and ID. Defaults to false. * @see \yii\web\User */ @@ -77,19 +68,18 @@ abstract class Target extends \yii\base\Component public $exportInterval = 1000; /** * @var array the messages that are retrieved from the logger so far by this log target. - * @see autoExport */ - public $messages = array(); + private $_messages = array(); private $_levels = 0; /** * Exports log messages to a specific destination. - * Child classes must implement this method. Note that you may need - * to clean up [[messages]] in this method to avoid re-exporting messages. - * @param boolean $final whether this method is called at the end of the current application + * Child classes must implement this method. + * @param array $messages the messages to be exported. See [[Logger::messages]] for the structure + * of each message. */ - abstract public function exportMessages($final); + abstract public function export($messages); /** * Processes the given log messages. @@ -99,45 +89,16 @@ abstract class Target extends \yii\base\Component * of each message. * @param boolean $final whether this method is called at the end of the current application */ - public function processMessages($messages, $final) + public function collect($messages, $final) { - $messages = $this->filterMessages($messages); - $this->messages = array_merge($this->messages, $messages); - - $count = count($this->messages); + $this->_messages = array($this->_messages, $this->filterMessages($messages)); + $count = count($this->_messages); if ($count > 0 && ($final || $this->exportInterval > 0 && $count >= $this->exportInterval)) { - $this->prepareExport($final); - $this->exportMessages($final); - $this->messages = array(); - } - } - - /** - * Prepares the [[messages]] for exporting. - * This method will modify each message by prepending extra information - * if [[prefixSession]] and/or [[prefixUser]] are set true. - * It will also add an additional message showing context information if - * [[logUser]] and/or [[logVars]] are set. - * @param boolean $final whether this method is called at the end of the current application - */ - protected function prepareExport($final) - { - $prefix = array(); - if ($this->prefixSession && ($id = session_id()) !== '') { - $prefix[] = "[$id]"; - } - if ($this->prefixUser && ($user = \Yii::$application->getComponent('user', false)) !== null) { - $prefix[] = '[' . $user->getName() . ']'; - $prefix[] = '[' . $user->getId() . ']'; - } - if ($prefix !== array()) { - $prefix = implode(' ', $prefix); - foreach ($this->messages as $i => $message) { - $this->messages[$i][0] = $prefix . ' ' . $this->messages[$i][0]; + if (($context = $this->getContextMessage()) !== '') { + $this->_messages[] = array($context, Logger::LEVEL_INFO, 'application', YII_BEGIN_TIME); } - } - if ($final && ($context = $this->getContextMessage()) !== '') { - $this->messages[] = array($context, Logger::LEVEL_INFO, 'application', YII_BEGIN_TIME); + $this->export($this->_messages); + $this->_messages = array(); } } @@ -164,7 +125,7 @@ abstract class Target extends \yii\base\Component /** * @return integer the message levels that this target is interested in. This is a bitmap of - * level values. Defaults to 0, meaning all available levels. + * level values. Defaults to 0, meaning all available levels. */ public function getLevels() { diff --git a/framework/util/FileHelper.php b/framework/util/FileHelper.php index b0b0611..d340338 100644 --- a/framework/util/FileHelper.php +++ b/framework/util/FileHelper.php @@ -10,6 +10,7 @@ namespace yii\util; use yii\base\Exception; +use yii\base\InvalidConfigException; /** * Filesystem helper @@ -37,7 +38,7 @@ class FileHelper * If the given path does not refer to an existing directory, an exception will be thrown. * @param string $path the given path. This can also be a path alias. * @return string the normalized path - * @throws Exception if the path does not refer to an existing directory. + * @throws InvalidConfigException if the path does not refer to an existing directory. */ public static function ensureDirectory($path) { @@ -45,7 +46,7 @@ class FileHelper if ($p !== false && ($p = realpath($p)) !== false && is_dir($p)) { return $p; } else { - throw new Exception('Directory does not exist: ' . $path); + throw new InvalidConfigException('Directory does not exist: ' . $path); } }