diff --git a/framework/YiiBase.php b/framework/YiiBase.php
index 9d3698b..d01648c 100644
--- a/framework/YiiBase.php
+++ b/framework/YiiBase.php
@@ -8,9 +8,8 @@
  */
 
 use yii\base\Exception;
-use yii\logging\Logger;
-use yii\base\InvalidCallException;
 use yii\base\InvalidConfigException;
+use yii\logging\Logger;
 
 /**
  * Gets the application start timestamp.
@@ -30,6 +29,11 @@ defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 0);
  * This constant defines the framework installation directory.
  */
 defined('YII_PATH') or define('YII_PATH', __DIR__);
+/**
+ * This constant defines whether error handling should be enabled. Defaults to true.
+ */
+defined('YII_ENABLE_ERROR_HANDLER') or define('YII_ENABLE_ERROR_HANDLER', true);
+
 
 /**
  * YiiBase is the core helper class for the Yii framework.
@@ -121,8 +125,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.
 	 *
@@ -189,14 +193,14 @@ class YiiBase
 	 *
 	 * Note, this method does not ensure the existence of the resulting path.
 	 * @param string $alias alias
-	 * @param boolean $throwException whether to throw exception if the alias is invalid.
 	 * @return string|boolean path corresponding to the alias, false if the root alias is not previously registered.
-	 * @throws Exception if the alias is invalid and $throwException is true.
 	 * @see setAlias
 	 */
-	public static function getAlias($alias, $throwException = false)
+	public static function getAlias($alias)
 	{
-		if (isset(self::$aliases[$alias])) {
+		if (!is_string($alias)) {
+			return false;
+		} elseif (isset(self::$aliases[$alias])) {
 			return self::$aliases[$alias];
 		} elseif ($alias === '' || $alias[0] !== '@') { // not an alias
 			return $alias;
@@ -206,11 +210,7 @@ class YiiBase
 				return self::$aliases[$alias] = self::$aliases[$rootAlias] . substr($alias, $pos);
 			}
 		}
-		if ($throwException) {
-			throw new Exception("Invalid path alias: $alias");
-		} else {
-			return false;
-		}
+		return false;
 	}
 
 	/**
@@ -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',
@@ -361,7 +361,7 @@ class YiiBase
 			$class = $config['class'];
 			unset($config['class']);
 		} else {
-			throw new InvalidCallException('Object configuration must be an array containing a "class" element.');
+			throw new InvalidConfigException('Object configuration must be an array containing a "class" element.');
 		}
 
 		if (!class_exists($class, false)) {
diff --git a/framework/base/Action.php b/framework/base/Action.php
index 1948c9d..8d4ec5a 100644
--- a/framework/base/Action.php
+++ b/framework/base/Action.php
@@ -9,8 +9,6 @@
 
 namespace yii\base;
 
-use yii\util\ReflectionHelper;
-
 /**
  * Action is the base class for all controller action classes.
  *
@@ -21,6 +19,14 @@ use yii\util\ReflectionHelper;
  * will be invoked by the controller when the action is requested.
  * The `run()` method can have parameters which will be filled up
  * with user input values automatically according to their names.
+ * For example, if the `run()` method is declared as follows:
+ *
+ * ~~~
+ * public function run($id, $type = 'book') { ... }
+ * ~~~
+ *
+ * And the parameters provided for the action are: `array('id' => 1)`.
+ * Then the `run()` method will be invoked as `run(1)` automatically.
  *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
@@ -37,6 +43,7 @@ class Action extends Component
 	public $controller;
 
 	/**
+	 * Constructor.
 	 * @param string $id the ID of this action
 	 * @param Controller $controller the controller that owns this action
 	 * @param array $config name-value pairs that will be used to initialize the object properties
@@ -51,20 +58,45 @@ class Action extends Component
 	/**
 	 * Runs this action with the specified parameters.
 	 * This method is mainly invoked by the controller.
-	 * @param array $params action parameters
+	 * @param array $params the parameters to be bound to the action's run() method.
 	 * @return integer the exit status (0 means normal, non-zero means abnormal).
+	 * @throws InvalidConfigException if the action class does not have a run() method
 	 */
 	public function runWithParams($params)
 	{
-		try {
-			$ps = ReflectionHelper::extractMethodParams($this, 'run', $params);
-		} catch (Exception $e) {
-			$this->controller->invalidActionParams($this, $e);
-			return 1;
+		if (!method_exists($this, 'run')) {
+			throw new InvalidConfigException(get_class($this) . ' must define a "run()" method.');
 		}
-		if ($params !== $ps) {
-			$this->controller->extraActionParams($this, $ps, $params);
+		$method = new \ReflectionMethod($this, 'run');
+		$args = $this->bindActionParams($method, $params);
+		return (int)$method->invokeArgs($this, $args);
+	}
+
+	/**
+	 * Binds the given parameters to the action method.
+	 * The returned array contains the parameters that need to be passed to the action method.
+	 * This method calls [[Controller::validateActionParams()]] to check if any exception
+	 * should be raised if there are missing or unknown parameters.
+	 * @param \ReflectionMethod $method the action method reflection object
+	 * @param array $params the supplied parameters
+	 * @return array the parameters that can be passed to the action method
+	 */
+	protected function bindActionParams($method, $params)
+	{
+		$args = array();
+		$missing = array();
+		foreach ($method->getParameters() as $param) {
+			$name = $param->getName();
+			if (array_key_exists($name, $params)) {
+				$args[] = $params[$name];
+				unset($params[$name]);
+			} elseif ($param->isDefaultValueAvailable()) {
+				$args[] = $param->getDefaultValue();
+			} else {
+				$missing[] = $name;
+			}
 		}
-		return (int)call_user_func_array(array($this, 'run'), $ps);
+		$this->controller->validateActionParams($this, $missing, $params);
+		return $args;
 	}
 }
diff --git a/framework/base/ActionEvent.php b/framework/base/ActionEvent.php
index 1b3e07d..ee945a8 100644
--- a/framework/base/ActionEvent.php
+++ b/framework/base/ActionEvent.php
@@ -12,8 +12,7 @@ namespace yii\base;
 /**
  * ActionEvent represents the event parameter used for an action event.
  *
- * By setting the [[isValid]] property, one may control whether to continue the life cycle of
- * the action currently being executed.
+ * By setting the [[isValid]] property, one may control whether to continue running the action.
  *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
@@ -25,7 +24,7 @@ class ActionEvent extends Event
 	 */
 	public $action;
 	/**
-	 * @var boolean whether the action is in valid state and its life cycle should proceed.
+	 * @var boolean whether to continue running the action.
 	 */
 	public $isValid = true;
 
@@ -34,7 +33,7 @@ class ActionEvent extends Event
 	 * @param Action $action the action associated with this action event.
 	 * @param array $config name-value pairs that will be used to initialize the object properties
 	 */
-	public function __construct(Action $action, $config = array())
+	public function __construct($action, $config = array())
 	{
 		$this->action = $action;
 		parent::__construct($config);
diff --git a/framework/base/ActionFilter.php b/framework/base/ActionFilter.php
deleted file mode 100644
index 9cb5331..0000000
--- a/framework/base/ActionFilter.php
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-/**
- * ActionFilter class file.
- *
- * @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
-
-namespace yii\base;
-
-/**
- * ActionFilter is the base class for all action filters.
- *
- * A filter can be applied to a controller action at different stages of its life cycle. In particular,
- * it responds to the following events that are raised when an action is being executed:
- *
- * 1. authorize
- * 2. beforeAction
- * 3. beforeRender
- * 4. afterRender
- * 5. afterAction
- *
- * Derived classes may respond to these events by overriding the corresponding methods in this class.
- * For example, to create an access control filter, one may override the [[authorize()]] method.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @since 2.0
- */
-class ActionFilter extends Behavior
-{
-	/**
-	 * @var Controller the owner of this behavior. For action filters, this should be a controller object.
-	 */
-	public $owner;
-	/**
-	 * @var array IDs of actions that this filter applies to.
-	 * If this property is empty or not set, it means this filter applies to all actions.
-	 * Note that if an action appears in [[except]], the filter will not apply to this action, even
-	 * if the action also appears in [[only]].
-	 * @see exception
-	 */
-	public $only;
-	/**
-	 * @var array IDs of actions that this filter does NOT apply to.
-	 */
-	public $except;
-
-	public function init()
-	{
-		$this->owner->on('authorize', array($this, 'handleEvent'));
-		$this->owner->on('beforeAction', array($this, 'handleEvent'));
-		$this->owner->on('beforeRender', array($this, 'handleEvent'));
-		$this->owner->getEventHandlers('afterRender')->insertAt(0, array($this, 'handleEvent'));
-		$this->owner->getEventHandlers('afterAction')->insertAt(0, array($this, 'handleEvent'));
-	}
-
-	public function authorize($event)
-	{
-	}
-
-	public function beforeAction($event)
-	{
-	}
-
-	public function beforeRender($event)
-	{
-	}
-
-	public function afterRender($event)
-	{
-	}
-
-	public function afterAction($event)
-	{
-	}
-
-	public function handleEvent($event)
-	{
-		if ($this->applyTo($event->action)) {
-			$this->{$event->name}($event);
-		}
-	}
-
-	public function applyTo(Action $action)
-	{
-		return (empty($this->only) || in_array($action->id, $this->only, false) !== false)
-			&& (empty($this->except) || in_array($action->id, $this->except, false) === false);
-	}
-}
\ No newline at end of file
diff --git a/framework/base/Application.php b/framework/base/Application.php
index 2e92aab..f64e352 100644
--- a/framework/base/Application.php
+++ b/framework/base/Application.php
@@ -9,8 +9,8 @@
 
 namespace yii\base;
 
-use yii\base\InvalidCallException;
-use yii\util\StringHelper;
+use Yii;
+use yii\util\FileHelper;
 
 /**
  * Application is the base class for all application classes.
@@ -36,7 +36,7 @@ use yii\util\StringHelper;
  *   Yii framework messages. This application component is dynamically loaded when needed.</li>
  * </ul>
  *
- * Application will undergo the following lifecycles when processing a user request:
+ * Application will undergo the following life cycles when processing a user request:
  * <ol>
  * <li>load application configuration;</li>
  * <li>set up class autoloader and error handling;</li>
@@ -49,28 +49,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 <qiang.xue@gmail.com>
  * @since 2.0
  */
@@ -97,11 +75,9 @@ class Application extends Module
 	 */
 	public $sourceLanguage = 'en_us';
 	/**
-	 * @var array IDs of application components that need to be loaded when the application starts.
-	 * The default value is `array('errorHandler')`, which loads the [[errorHandler]] component
-	 * to ensure errors and exceptions can be handled nicely.
+	 * @var array IDs of the components that need to be loaded when the application starts.
 	 */
-	public $preload = array('errorHandler');
+	public $preload = array();
 	/**
 	 * @var Controller the currently active controller instance
 	 */
@@ -128,12 +104,19 @@ class Application extends Module
 	 */
 	public function __construct($id, $basePath, $config = array())
 	{
-		\Yii::$application = $this;
+		Yii::$application = $this;
 		$this->id = $id;
 		$this->setBasePath($basePath);
+
+		if (YII_ENABLE_ERROR_HANDLER) {
+			set_exception_handler(array($this, 'handleException'));
+			set_error_handler(array($this, 'handleError'), error_reporting());
+		}
+
 		$this->registerDefaultAliases();
 		$this->registerCoreComponents();
-		parent::__construct($id, $this, $config);
+
+		Component::__construct($config);
 	}
 
 	/**
@@ -203,28 +186,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.'));
-		}
-		/** @var $controller Controller */
-		list($controller, $action) = $result;
-		$priorController = $this->controller;
-		$this->controller = $controller;
-		$status = $controller->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 +200,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 +257,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 +341,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 +350,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
 	 */
@@ -402,9 +363,9 @@ class Application extends Module
 	 */
 	public function registerDefaultAliases()
 	{
-		\Yii::$aliases['@application'] = $this->getBasePath();
-		\Yii::$aliases['@entry'] = dirname($_SERVER['SCRIPT_FILENAME']);
-		\Yii::$aliases['@www'] = '';
+		Yii::$aliases['@application'] = $this->getBasePath();
+		Yii::$aliases['@entry'] = dirname($_SERVER['SCRIPT_FILENAME']);
+		Yii::$aliases['@www'] = '';
 	}
 
 	/**
@@ -417,15 +378,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',
@@ -444,124 +396,88 @@ 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
+	 * Handles PHP execution errors such as warnings, notices.
+	 *
+	 * This method is used as a PHP error handler. It will simply raise an `ErrorException`.
+	 *
+	 * @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
+	 * @throws \ErrorException the error exception
 	 */
-	public function runAction($route, $params = array(), $module = null)
+	public function handleError($code, $message, $file, $line)
 	{
-		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);
-		}
-
-		/** @var $controller Controller */
-		if (isset($module->controllerMap[$id])) {
-			$controller = \Yii::createObject($module->controllerMap[$id], $id, $module);
-		} else {
-			$controller = $this->createController($id, $module);
-			if ($controller === null) {
-				throw new InvalidRequestException("Unable to resolve the request: $route");
-			}
-		}
-
-		if (isset($controller)) {
-			$action = $this->createAction($route, $controller);
-			if ($action !== null) {
-				return $action->runWithParams($params);
-			}
+		if (error_reporting() !== 0) {
+			throw new \ErrorException($message, 0, $code, $file, $line);
 		}
-
-		throw new InvalidRequestException("Unable to resolve the request: $route");
 	}
 
-
 	/**
-	 * Creates a controller instance based on the controller ID.
+	 * Handles uncaught PHP exceptions.
 	 *
-	 * 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.
+	 * This method is implemented as a PHP exception handler. It requires
+	 * that constant YII_ENABLE_ERROR_HANDLER be defined true.
 	 *
-	 * @param string $id the controller ID
-	 * @param Module $module the module that owns the controller
-	 * @return Controller the newly created controller instance
+	 * @param \Exception $exception exception that is not caught
 	 */
-	public function createController($id, $module)
+	public function handleException($exception)
 	{
-		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);
-				}
+		// disable error capturing to avoid recursive errors while handling exceptions
+		restore_error_handler();
+		restore_exception_handler();
+
+		try {
+			$this->logException($exception);
+
+			if (($handler = $this->getErrorHandler()) !== null) {
+				$handler->handle($exception);
+			} else {
+				$this->renderException($exception);
 			}
+
+			$this->end(1);
+
+		} catch(\Exception $e) {
+			// exception could be thrown in end() or ErrorHandler::handle()
+			$msg = (string)$e;
+			$msg .= "\nPrevious exception:\n";
+			$msg .= (string)$exception;
+			$msg .= "\n\$_SERVER = " . var_export($_SERVER, true);
+			error_log($msg);
+			exit(1);
 		}
-		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
-	 * @throws InvalidConfigException if [[Controller::defaultAction]] is empty.
+	 * Renders an exception without using rich format.
+	 * @param \Exception $exception the exception to be rendered.
 	 */
-	public function createAction($id, $controller)
+	public function renderException($exception)
 	{
-		if ($id === '') {
-			$id = $controller->defaultAction;
-			if ($id == '') {
-				throw new InvalidConfigException(get_class($controller) . '::defaultAction cannot be empty.');
-			}
+		if ($exception instanceof Exception && ($exception->causedByUser || !YII_DEBUG)) {
+			$message = $exception->getName() . ': ' . $exception->getMessage();
+		} else {
+			$message = YII_DEBUG ? (string)$exception : 'Error: ' . $exception->getMessage();
 		}
-		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);
-				}
-			}
+		if (PHP_SAPI) {
+			echo $message . "\n";
+		} else {
+			echo '<pre>' . htmlspecialchars($message, ENT_QUOTES, $this->charset) . '</pre>';
+		}
+	}
+
+	// todo: to be polished
+	protected function logException($exception)
+	{
+		$category = get_class($exception);
+		if ($exception instanceof HttpException) {
+			/** @var $exception HttpException */
+			$category .= '\\' . $exception->statusCode;
+		} elseif ($exception instanceof \ErrorException) {
+			/** @var $exception \ErrorException */
+			$category .= '\\' . $exception->getSeverity();
 		}
-		return null;
+		Yii::error((string)$exception, $category);
 	}
 }
diff --git a/framework/base/Controller.php b/framework/base/Controller.php
index 0df287a..804b339 100644
--- a/framework/base/Controller.php
+++ b/framework/base/Controller.php
@@ -9,16 +9,12 @@
 
 namespace yii\base;
 
+use Yii;
+use yii\util\StringHelper;
+
 /**
  * Controller is the base class for classes containing controller logic.
  *
- * Controller implements the action life cycles, which consist of the following steps:
- *
- * 1. [[authorize]]
- * 2. [[beforeAction]]
- * 3. [[afterAction]]
- *
- * @property array $actionParams the request parameters (name-value pairs) to be used for action parameter binding
  * @property string $route the route (module ID, controller ID and action ID) of the current request.
  * @property string $uniqueId the controller ID that is prefixed with the module ID (if any).
  *
@@ -27,6 +23,9 @@ namespace yii\base;
  */
 class Controller extends Component
 {
+	const EVENT_BEFORE_ACTION = 'beforeAction';
+	const EVENT_AFTER_ACTION = 'afterAction';
+
 	/**
 	 * @var string the ID of this controller
 	 */
@@ -91,200 +90,184 @@ 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;
+		$action = $this->createAction($id);
+		if ($action !== null) {
+			$oldAction = $this->action;
+			$this->action = $action;
+
+			if ($this->beforeAction($action)) {
+				$status = $action->runWithParams($params);
+				$this->afterAction($action);
 			} else {
-				$this->missingAction($action);
-				return 1;
+				$status = 1;
 			}
-		}
 
-		$priorAction = $this->action;
-		$this->action = $action;
+			$this->action = $oldAction;
 
-		if ($this->authorize($action) && $this->beforeAction($action)) {
-			if ($params === null) {
-				$params = $this->getActionParams();
-			}
-			$status = $action->runWithParams($params);
-			$this->afterAction($action);
+			return $status;
 		} else {
-			$status = 1;
+			throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id);
 		}
-
-		$this->action = $priorAction;
-
-		return $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
+	 * 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 createAction($actionID)
+	public function run($route, $params = array())
 	{
-		if ($actionID === '') {
-			$actionID = $this->defaultAction;
-		}
-		if (isset($this->actionMap[$actionID])) {
-			return \Yii::createObject($this->actionMap[$actionID], $actionID, $this);
-		} elseif (method_exists($this, 'action' . $actionID)) {
-			return new InlineAction($actionID, $this);
+		$pos = strpos($route, '/');
+		if ($pos === false) {
+			return $this->runAction($route, $params);
+		} elseif ($pos > 0) {
+			return $this->module->runAction($route, $params);
 		} else {
-			return null;
+			return \Yii::$application->runAction(ltrim($route, '/'), $params);
 		}
 	}
 
 	/**
-	 * Returns the request parameters that will be used for action parameter binding.
-	 * Default implementation simply returns an empty array.
-	 * Child classes may override this method to customize the parameters to be provided
-	 * for action parameter binding (e.g. `$_GET`).
-	 * @return array the request parameters (name-value pairs) to be used for action parameter binding
+	 * 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 getActionParams()
+	public function forward($route, $params = array())
 	{
-		return array();
+		$status = $this->run($route, $params);
+		exit($status);
 	}
 
 	/**
-	 * This method is invoked when the request parameters do not satisfy the requirement of the specified action.
-	 * The default implementation will throw an exception.
-	 * @param Action $action the action being executed
-	 * @param Exception $exception the exception about the invalid parameters
-	 * @throws Exception whenever this method is invoked
+	 * 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 invalidActionParams($action, $exception)
+	public function createAction($id)
 	{
-		throw $exception;
-	}
+		if ($id === '') {
+			$id = $this->defaultAction;
+		}
 
-	/**
-	 * This method is invoked when extra parameters are provided to an action when it is executed.
-	 * The default implementation does nothing.
-	 * @param Action $action the action being executed
-	 * @param array $expected the expected action parameters (name => value)
-	 * @param array $actual the actual action parameters (name => value)
-	 */
-	public function extraActionParams($action, $expected, $actual)
-	{
+		$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, $methodName);
+				}
+			}
+		}
+		return null;
 	}
 
 	/**
-	 * 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 InvalidRequestException whenever this method is invoked
+	 * 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 missingAction($actionID)
+	public function beforeAction($action)
 	{
-		throw new InvalidRequestException(\Yii::t('yii', 'The system is unable to find the requested action "{action}".',
-			array('{action}' => $actionID == '' ? $this->defaultAction : $actionID)));
+		$event = new ActionEvent($action);
+		$this->trigger(self::EVENT_BEFORE_ACTION, $event);
+		return $event->isValid;
 	}
 
 	/**
-	 * @return string the controller ID that is prefixed with the module ID (if any).
+	 * 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 getUniqueId()
+	public function afterAction($action)
 	{
-		return $this->module instanceof Application ? $this->id : $this->module->getUniqueId() . '/' . $this->id;
+		$this->trigger(self::EVENT_AFTER_ACTION, new ActionEvent($action));
 	}
 
 	/**
-	 * Returns the route of the current request.
-	 * @return string the route (module ID, controller ID and action ID) of the current request.
+	 * Returns the request parameters that will be used for action parameter binding.
+	 * Default implementation simply returns an empty array.
+	 * Child classes may override this method to customize the parameters to be provided
+	 * for action parameter binding (e.g. `$_GET`).
+	 * @return array the request parameters (name-value pairs) to be used for action parameter binding
 	 */
-	public function getRoute()
+	public function getActionParams()
 	{
-		return $this->action !== null ? $this->getUniqueId() . '/' . $this->action->id : $this->getUniqueId();
+		return array();
 	}
 
 	/**
-	 * 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.
+	 * Validates the parameter being bound to actions.
+	 * This method is invoked when parameters are being bound to the currently requested action.
+	 * Child classes may override this method to throw exceptions when there are missing and/or unknown parameters.
+	 * @param Action $action the currently requested action
+	 * @param array $missingParams the names of the missing parameters
+	 * @param array $unknownParams the unknown parameters (name=>value)
 	 */
-	public function forward($route, $params = array(), $exit = true)
+	public function validateActionParams($action, $missingParams, $unknownParams)
 	{
-		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.
+	 * @return string the controller ID that is prefixed with the module ID (if any).
 	 */
-	public function authorize($action)
+	public function getUniqueId()
 	{
-		$event = new ActionEvent($action);
-		$this->trigger(__METHOD__, $event);
-		return $event->isValid;
+		return $this->module instanceof Application ? $this->id : $this->module->getUniqueId() . '/' . $this->id;
 	}
 
 	/**
-	 * 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.
+	 * Returns the route of the current request.
+	 * @return string the route (module ID, controller ID and action ID) of the current request.
 	 */
-	public function beforeAction($action)
+	public function getRoute()
 	{
-		$event = new ActionEvent($action);
-		$this->trigger(__METHOD__, $event);
-		return $event->isValid;
+		return $this->action !== null ? $this->getUniqueId() . '/' . $this->action->id : $this->getUniqueId();
 	}
 
 	/**
-	 * 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.
+	 * Renders a view and applies layout if available.
+	 *
+	 * @param $view
+	 * @param array $params
+	 * @return string
 	 */
-	public function afterAction($action)
-	{
-		$this->trigger(__METHOD__, new ActionEvent($action));
-	}
-
 	public function render($view, $params = array())
 	{
 		return $this->createView()->render($view, $params);
 	}
 
-	public function renderText($text)
+	public function renderContent($content)
 	{
-		return $this->createView()->renderText($text);
+		return $this->createView()->renderContent($content);
 	}
 
 	public function renderPartial($view, $params = array())
@@ -296,4 +279,15 @@ class Controller extends Component
 	{
 		return new View($this);
 	}
+
+	/**
+	 * Returns the directory containing view files for this controller.
+	 * The default implementation returns the directory named as controller [[id]] under the [[module]]'s
+	 * [[viewPath]] directory.
+	 * @return string the directory containing the view files for this controller.
+	 */
+	public function getViewPath()
+	{
+		return $this->module->getViewPath() . DIRECTORY_SEPARATOR . $this->id;
+	}
 }
diff --git a/framework/base/ErrorHandler.php b/framework/base/ErrorHandler.php
index 9f1621a..0b6bf97 100644
--- a/framework/base/ErrorHandler.php
+++ b/framework/base/ErrorHandler.php
@@ -52,90 +52,46 @@ class ErrorHandler extends Component
 	 * @var \Exception the exception that is being handled currently
 	 */
 	public $exception;
-	/**
-	 * @var boolean whether to log errors also using error_log(). Defaults to true.
-	 * Note that errors captured by the error handler are always logged by [[\Yii::error()]].
-	 */
-	public $logErrors = true;
-
-	public function init()
-	{
-		set_exception_handler(array($this, 'handleException'));
-		set_error_handler(array($this, 'handleError'), error_reporting());
-	}
 
-	/**
-	 * Handles PHP execution errors such as warnings, notices.
-	 *
-	 * This method is used as a PHP error handler. It will simply raise an `ErrorException`.
-	 *
-	 * @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
-	 * @throws \ErrorException the error exception
-	 */
-	public function handleError($code, $message, $file, $line)
-	{
-		if(error_reporting()!==0) {
-			throw new \ErrorException($message, 0, $code, $file, $line);
-		}
-	}
 
 	/**
 	 * @param \Exception $exception
 	 */
-	public function handleException($exception)
+	public function handle($exception)
 	{
-		// disable error capturing to avoid recursive errors while handling exceptions
-		restore_error_handler();
-		restore_exception_handler();
-
 		$this->exception = $exception;
-		$this->logException($exception);
 
 		if ($this->discardExistingOutput) {
 			$this->clearOutput();
 		}
 
-		try {
-			$this->render($exception);
-		} catch (\Exception $e) {
-			// use the most primitive way to display exception thrown in the error view
-			$this->renderAsText($e);
-		}
-
-
-		try {
-			\Yii::$application->end(1);
-		} catch (Exception $e2) {
-			// use the most primitive way to log error occurred in end()
-			$msg = get_class($e2) . ': ' . $e2->getMessage() . ' (' . $e2->getFile() . ':' . $e2->getLine() . ")\n";
-			$msg .= $e2->getTraceAsString() . "\n";
-			$msg .= "Previous error:\n";
-			$msg .= $e2->getTraceAsString() . "\n";
-			$msg .= '$_SERVER=' . var_export($_SERVER, true);
-			error_log($msg);
-			exit(1);
-		}
+		$this->render($exception);
 	}
 
 	protected function render($exception)
 	{
 		if ($this->errorAction !== null) {
-			\Yii::$application->runController($this->errorAction);
+			\Yii::$application->runAction($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);
+				\Yii::$application->renderException($exception);
 			} else {
-				$this->renderAsHtml($exception);
+				$view = new View($this);
+				if (!YII_DEBUG || $exception instanceof Exception && $exception->causedByUser) {
+					$viewName = $this->errorView;
+				} else {
+					$viewName = $this->exceptionView;
+				}
+				echo $view->render($viewName, array(
+					'exception' => $exception,
+				));
 			}
 		} else {
-			$this->renderAsText($exception);
+			\Yii::$application->renderException($exception);
 		}
 	}
 
@@ -286,22 +242,6 @@ class ErrorHandler extends Component
 		return htmlspecialchars($text, ENT_QUOTES, \Yii::$application->charset);
 	}
 
-	public function logException($exception)
-	{
-		$category = get_class($exception);
-		if ($exception instanceof HttpException) {
-			/** @var $exception HttpException */
-			$category .= '\\' . $exception->statusCode;
-		} elseif ($exception instanceof \ErrorException) {
-			/** @var $exception \ErrorException */
-			$category .= '\\' . $exception->getSeverity();
-		}
-		\Yii::error((string)$exception, $category);
-		if ($this->logErrors) {
-			error_log($exception);
-		}
-	}
-
 	public function clearOutput()
 	{
 		// the following manual level counting is to deal with zlib.output_compression set to On
@@ -313,22 +253,14 @@ class ErrorHandler extends Component
 	/**
 	 * @param \Exception $exception
 	 */
-	public function renderAsText($exception)
+	public function renderAsHtml($exception)
 	{
-		if (YII_DEBUG) {
-			echo $exception;
+		$view = new View($this);
+		if (!YII_DEBUG || $exception instanceof Exception && $exception->causedByUser) {
+			$viewName = $this->errorView;
 		} else {
-			echo get_class($exception) . ': ' . $exception->getMessage();
+			$viewName = $this->exceptionView;
 		}
-	}
-
-	/**
-	 * @param \Exception $exception
-	 */
-	public function renderAsHtml($exception)
-	{
-		$view = new View;
-		$view->context = $this;
 		$name = !YII_DEBUG || $exception instanceof HttpException ? $this->errorView : $this->exceptionView;
 		echo $view->render($name, array(
 			'exception' => $exception,
diff --git a/framework/base/Exception.php b/framework/base/Exception.php
index a740a35..ab681e2 100644
--- a/framework/base/Exception.php
+++ b/framework/base/Exception.php
@@ -17,5 +17,17 @@ namespace yii\base;
  */
 class Exception extends \Exception
 {
+	/**
+	 * @var boolean whether this exception is caused by end user's mistake (e.g. wrong URL)
+	 */
+	public $causedByUser = false;
+
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return \Yii::t('yii', 'Exception');
+	}
 }
 
diff --git a/framework/base/HttpException.php b/framework/base/HttpException.php
index 52ac690..d2de5bc 100644
--- a/framework/base/HttpException.php
+++ b/framework/base/HttpException.php
@@ -25,6 +25,10 @@ class HttpException extends Exception
 	 * @var integer HTTP status code, such as 403, 404, 500, etc.
 	 */
 	public $statusCode;
+	/**
+	 * @var boolean whether this exception is caused by end user's mistake (e.g. wrong URL)
+	 */
+	public $causedByUser = true;
 
 	/**
 	 * Constructor.
diff --git a/framework/base/InlineAction.php b/framework/base/InlineAction.php
index 8764ac2..4cd5413 100644
--- a/framework/base/InlineAction.php
+++ b/framework/base/InlineAction.php
@@ -9,13 +9,11 @@
 
 namespace yii\base;
 
-use yii\util\ReflectionHelper;
-
 /**
  * InlineAction represents an action that is defined as a controller method.
  *
- * The name of the controller method should be in the format of `actionXyz`
- * where `Xyz` stands for the action ID (e.g. `actionIndex`).
+ * The name of the controller method is available via [[actionMethod]] which
+ * is set by the [[controller]] who creates this action.
  *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
@@ -23,6 +21,23 @@ use yii\util\ReflectionHelper;
 class InlineAction extends Action
 {
 	/**
+	 * @var string the controller method that  this inline action is associated with
+	 */
+	public $actionMethod;
+
+	/**
+	 * @param string $id the ID of this action
+	 * @param Controller $controller the controller that owns this action
+	 * @param string $actionMethod the controller method that  this inline action is associated with
+	 * @param array $config name-value pairs that will be used to initialize the object properties
+	 */
+	public function __construct($id, $controller, $actionMethod, $config = array())
+	{
+		$this->actionMethod = $actionMethod;
+		parent::__construct($id, $controller, $config);
+	}
+
+	/**
 	 * Runs this action with the specified parameters.
 	 * This method is mainly invoked by the controller.
 	 * @param array $params action parameters
@@ -30,16 +45,8 @@ class InlineAction extends Action
 	 */
 	public function runWithParams($params)
 	{
-		try {
-			$method = 'action' . $this->id;
-			$ps = ReflectionHelper::extractMethodParams($this->controller, $method, $params);
-		} catch (Exception $e) {
-			$this->controller->invalidActionParams($this, $e);
-			return 1;
-		}
-		if ($params !== $ps) {
-			$this->controller->extraActionParams($this, $ps, $params);
-		}
-		return (int)call_user_func_array(array($this->controller, $method), $ps);
+		$method = new \ReflectionMethod($this->controller, $this->actionMethod);
+		$args = $this->bindActionParams($method, $params);
+		return (int)$method->invokeArgs($this->controller, $args);
 	}
 }
diff --git a/framework/base/InvalidCallException.php b/framework/base/InvalidCallException.php
index 24e7b6e..a1df021 100644
--- a/framework/base/InvalidCallException.php
+++ b/framework/base/InvalidCallException.php
@@ -17,5 +17,12 @@ namespace yii\base;
  */
 class InvalidCallException extends \Exception
 {
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return \Yii::t('yii', 'Invalid Call');
+	}
 }
 
diff --git a/framework/base/InvalidConfigException.php b/framework/base/InvalidConfigException.php
index 5256d7e..3c100d1 100644
--- a/framework/base/InvalidConfigException.php
+++ b/framework/base/InvalidConfigException.php
@@ -17,5 +17,12 @@ namespace yii\base;
  */
 class InvalidConfigException extends \Exception
 {
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return \Yii::t('yii', 'Invalid Configuration');
+	}
 }
 
diff --git a/framework/base/InvalidRequestException.php b/framework/base/InvalidRequestException.php
index 2e2a04a..fd468a1 100644
--- a/framework/base/InvalidRequestException.php
+++ b/framework/base/InvalidRequestException.php
@@ -17,5 +17,17 @@ namespace yii\base;
  */
 class InvalidRequestException extends \Exception
 {
+	/**
+	 * @var boolean whether this exception is caused by end user's mistake (e.g. wrong URL)
+	 */
+	public $causedByUser = true;
+
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return \Yii::t('yii', 'Invalid Request');
+	}
 }
 
diff --git a/framework/base/InvalidRouteException.php b/framework/base/InvalidRouteException.php
new file mode 100644
index 0000000..e20b2b7
--- /dev/null
+++ b/framework/base/InvalidRouteException.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * InvalidRouteException class file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+/**
+ * InvalidRouteException represents an exception caused by an invalid route.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class InvalidRouteException extends \Exception
+{
+	/**
+	 * @var boolean whether this exception is caused by end user's mistake (e.g. wrong URL)
+	 */
+	public $causedByUser = true;
+
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return \Yii::t('yii', 'Invalid Route');
+	}
+}
+
diff --git a/framework/base/Module.php b/framework/base/Module.php
index 6337a9b..dcb468c 100644
--- a/framework/base/Module.php
+++ b/framework/base/Module.php
@@ -9,18 +9,29 @@
 
 namespace yii\base;
 
+use Yii;
+use yii\util\StringHelper;
 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 string $controllerPath The directory containing the controller classes. Defaults to "[[basePath]]/controllers".
+ * @property string $viewPath The directory containing the view files within this module. Defaults to "[[basePath]]/views".
+ * @property string $layoutPath The directory containing the layout view files within this module. Defaults to "[[viewPath]]/layouts".
  * @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.
  *
@@ -34,7 +45,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();
 	/**
@@ -86,27 +97,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.
@@ -123,9 +134,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)
@@ -140,7 +151,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
 	 */
@@ -161,18 +172,21 @@ abstract class Module extends Component
 	 */
 	public function init()
 	{
-		\Yii::setAlias('@' . $this->id, $this->getBasePath());
+		Yii::setAlias('@' . $this->id, $this->getBasePath());
 		$this->preloadComponents();
 	}
 
 	/**
 	 * Returns an ID that uniquely identifies this module among all modules within the current application.
+	 * Note that if the module is an application, an empty string will be returned.
 	 * @return string the unique ID of the module.
 	 */
 	public function getUniqueId()
 	{
-		if ($this->module && !$this->module instanceof Application) {
-			return $this->module->getUniqueId() . "/{$this->id}";
+		if ($this instanceof Application) {
+			return '';
+		} elseif ($this->module) {
+			return $this->module->getUniqueId() . '/' . $this->id;
 		} else {
 			return $this->id;
 		}
@@ -229,8 +243,8 @@ abstract class Module extends Component
 	}
 
 	/**
-	 * @return string the root directory of view files. Defaults to 'moduleDir/views' where
-	 * moduleDir is the directory containing the module class.
+	 * Returns the directory that contains the view files for this module.
+	 * @return string the root directory of view files. Defaults to "[[basePath]]/view".
 	 */
 	public function getViewPath()
 	{
@@ -252,8 +266,8 @@ abstract class Module extends Component
 	}
 
 	/**
-	 * @return string the root directory of layout files. Defaults to 'moduleDir/views/layouts' where
-	 * moduleDir is the directory containing the module class.
+	 * Returns the directory that contains layout view files for this module.
+	 * @return string the root directory of layout files. Defaults to "[[viewPath]]/layouts".
 	 */
 	public function getLayoutPath()
 	{
@@ -277,19 +291,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.
@@ -297,7 +311,7 @@ abstract class Module extends Component
 	 *
 	 * ~~~
 	 * array(
-	 *	'@models' => '@app/models', // an existing alias
+	 *	'@models' => '@application/models', // an existing alias
 	 *	'@backend' => __DIR__ . '/../backend',  // a directory
 	 * )
 	 * ~~~
@@ -305,7 +319,7 @@ abstract class Module extends Component
 	public function setAliases($aliases)
 	{
 		foreach ($aliases as $name => $alias) {
-			\Yii::setAlias($name, $alias);
+			Yii::setAlias($name, $alias);
 		}
 	}
 
@@ -334,8 +348,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;
@@ -388,7 +402,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.
@@ -418,8 +432,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)
@@ -428,11 +442,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)
@@ -441,22 +454,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)
@@ -469,11 +482,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)
 	{
@@ -491,11 +504,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.
@@ -515,7 +528,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)
 	{
@@ -525,7 +538,7 @@ abstract class Module extends Component
 	}
 
 	/**
-	 * Loads application components that are declared in [[preload]].
+	 * Loads components that are declared in [[preload]].
 	 */
 	public function preloadComponents()
 	{
@@ -533,4 +546,79 @@ abstract class Module extends Component
 			$this->getComponent($id);
 		}
 	}
+
+	/**
+	 * 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 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())
+	{
+		$result = $this->createController($route);
+		if (is_array($result)) {
+			/** @var $controller Controller */
+			list($controller, $actionID) = $result;
+			$oldController = Yii::$application->controller;
+			Yii::$application->controller = $controller;
+			$status = $controller->runAction($actionID, $params);
+			Yii::$application->controller = $oldController;
+			return $status;
+		} else {
+			throw new InvalidRouteException('Unable to resolve the request: ' . trim($this->getUniqueId() . '/' . $route, '/'));
+		}
+	}
+
+	/**
+	 * Creates a controller instance based on the controller ID.
+	 *
+	 * The controller is created within this 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 $route the route consisting of module, controller and action IDs.
+	 * @return array|boolean if the controller is created successfully, it will be returned together
+	 * with the remainder of the route which represents the action ID. Otherwise false will be returned.
+	 */
+	public function createController($route)
+	{
+		if ($route === '') {
+			$route = $this->defaultRoute;
+		}
+		if (($pos = strpos($route, '/')) !== false) {
+			$id = substr($route, 0, $pos);
+			$route = substr($route, $pos + 1);
+		} else {
+			$id = $route;
+			$route = '';
+		}
+
+		$module = $this->getModule($id);
+		if ($module !== null) {
+			return $module->createController($route);
+		}
+
+		if (isset($this->controllerMap[$id])) {
+			$controller = Yii::createObject($this->controllerMap[$id], $id, $this);
+		} elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) {
+			$className = StringHelper::id2camel($id) . 'Controller';
+			$classFile = $this->controllerPath . DIRECTORY_SEPARATOR . $className . '.php';
+			if (is_file($classFile)) {
+				$className = $this->controllerNamespace . '\\' . $className;
+				if (!class_exists($className, false)) {
+					require($classFile);
+				}
+				if (class_exists($className, false) && is_subclass_of($className, '\yii\base\Controller')) {
+					$controller = new $className($id, $this);
+				}
+			}
+		}
+
+		return isset($controller) ? array($controller, $route) : false;
+	}
 }
diff --git a/framework/base/NotSupportedException.php b/framework/base/NotSupportedException.php
index aa2badb..56e7e36 100644
--- a/framework/base/NotSupportedException.php
+++ b/framework/base/NotSupportedException.php
@@ -17,5 +17,12 @@ namespace yii\base;
  */
 class NotSupportedException extends \Exception
 {
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return \Yii::t('yii', 'Not Supported');
+	}
 }
 
diff --git a/framework/base/Theme.php b/framework/base/Theme.php
index c1fc94a..03f8f55 100644
--- a/framework/base/Theme.php
+++ b/framework/base/Theme.php
@@ -9,55 +9,114 @@
 
 namespace yii\base;
 
+use Yii;
 use yii\base\InvalidConfigException;
+use yii\util\FileHelper;
 
 /**
  * Theme represents an application theme.
  *
+ * A theme is directory consisting of view and layout files which are meant to replace their
+ * non-themed counterparts.
+ *
+ * Theme uses [[pathMap]] to achieve the file replacement. A view or layout file will be replaced
+ * with its themed version if part of its path matches one of the keys in [[pathMap]].
+ * Then the matched part will be replaced with the corresponding array value.
+ *
+ * For example, if [[pathMap]] is `array('/www/views' => '/www/themes/basic')`,
+ * then the themed version for a view file `/www/views/site/index.php` will be
+ * `/www/themes/basic/site/index.php`.
+ *
+ * @property string $baseUrl the base URL for this theme. This is mainly used by [[getUrl()]].
+ *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
  */
 class Theme extends Component
 {
+	/**
+	 * @var string the root path of this theme.
+	 * @see pathMap
+	 */
 	public $basePath;
-	public $baseUrl;
+	/**
+	 * @var array the mapping between view directories and their corresponding themed versions.
+	 * If not set, it will be initialized as a mapping from [[Application::basePath]] to [[basePath]].
+	 * This property is used by [[apply()]] when a view is trying to apply the theme.
+	 */
+	public $pathMap;
+
+	private $_baseUrl;
 
+	/**
+	 * Initializes the theme.
+	 * @throws InvalidConfigException if [[basePath]] is not set.
+	 */
 	public function init()
 	{
-		if ($this->basePath !== null) {
-			$this->basePath = \Yii::getAlias($this->basePath, true);
-		} else {
-			throw new InvalidConfigException("Theme.basePath must be set.");
+	 	parent::init();
+		if (empty($this->pathMap)) {
+			if ($this->basePath !== null) {
+				$this->basePath = FileHelper::ensureDirectory($this->basePath);
+				$this->pathMap = array(Yii::$application->getBasePath() => $this->basePath);
+			} else {
+				throw new InvalidConfigException("Theme::basePath must be set.");
+			}
 		}
-		if ($this->baseUrl !== null) {
-			$this->baseUrl = \Yii::getAlias($this->baseUrl, true);
-		} else {
-			throw new InvalidConfigException("Theme.baseUrl must be set.");
+		$paths = array();
+		foreach ($this->pathMap as $from => $to) {
+			$paths[FileHelper::normalizePath($from) . DIRECTORY_SEPARATOR] = FileHelper::normalizePath($to) . DIRECTORY_SEPARATOR;
 		}
+		$this->pathMap = $paths;
+	}
+
+	/**
+	 * Returns the base URL for this theme.
+	 * The method [[getUrl()]] will prefix this to the given URL.
+	 * @return string the base URL for this theme.
+	 */
+	public function getBaseUrl()
+	{
+		return $this->_baseUrl;
+	}
+
+	/**
+	 * Sets the base URL for this theme.
+	 * @param string $value the base URL for this theme.
+	 */
+	public function setBaseUrl($value)
+	{
+		$this->_baseUrl = rtrim(Yii::getAlias($value), '/');
 	}
 
 	/**
-	 * @param Application|Module|Controller|Object $context
-	 * @return string
+	 * Converts a file to a themed file if possible.
+	 * If there is no corresponding themed file, the original file will be returned.
+	 * @param string $path the file to be themed
+	 * @return string the themed file, or the original file if the themed version is not available.
 	 */
-	public function getViewPath($context = null)
+	public function apply($path)
 	{
-		$viewPath = $this->basePath . DIRECTORY_SEPARATOR . 'views';
-		if ($context === null || $context instanceof Application) {
-			return $viewPath;
-		} elseif ($context instanceof Controller || $context instanceof Module) {
-			return $viewPath . DIRECTORY_SEPARATOR . $context->getUniqueId();
-		} else {
-			return $viewPath . DIRECTORY_SEPARATOR . str_replace('\\', '_', get_class($context));
+		$path = FileHelper::normalizePath($path);
+		foreach ($this->pathMap as $from => $to) {
+			if (strpos($path, $from) === 0) {
+				$n = strlen($from);
+				$file = $to . substr($path, $n);
+				if (is_file($file)) {
+					return $file;
+				}
+			}
 		}
+		return $path;
 	}
 
 	/**
-	 * @param Module $module
-	 * @return string
+	 * Converts a relative URL into an absolute URL using [[basePath]].
+	 * @param string $url the relative URL to be converted.
+	 * @return string the absolute URL
 	 */
-	public function getLayoutPath($module = null)
+	public function getUrl($url)
 	{
-		return $this->getViewPath($module) . DIRECTORY_SEPARATOR . 'layouts';
+		return $this->baseUrl . '/' . ltrim($url, '/');
 	}
 }
diff --git a/framework/base/UnknownMethodException.php b/framework/base/UnknownMethodException.php
index 8667d24..459f791 100644
--- a/framework/base/UnknownMethodException.php
+++ b/framework/base/UnknownMethodException.php
@@ -17,5 +17,12 @@ namespace yii\base;
  */
 class UnknownMethodException extends \Exception
 {
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return \Yii::t('yii', 'Unknown Method');
+	}
 }
 
diff --git a/framework/base/UnknownPropertyException.php b/framework/base/UnknownPropertyException.php
index 69581f9..de8de1c 100644
--- a/framework/base/UnknownPropertyException.php
+++ b/framework/base/UnknownPropertyException.php
@@ -17,5 +17,12 @@ namespace yii\base;
  */
 class UnknownPropertyException extends \Exception
 {
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return \Yii::t('yii', 'Unknown Property');
+	}
 }
 
diff --git a/framework/base/View.php b/framework/base/View.php
index db0741a..dccfb26 100644
--- a/framework/base/View.php
+++ b/framework/base/View.php
@@ -9,27 +9,29 @@
 
 namespace yii\base;
 
+use Yii;
 use yii\util\FileHelper;
 use yii\base\Application;
 
 /**
+ * View represents a view object in the MVC pattern.
+ * 
+ * View provides a set of methods (e.g. [[render()]]) for rendering purpose.
+ * 
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
  */
 class View extends Component
 {
 	/**
-	 * @var Controller|Widget|Object the context under which this view is being rendered
+	 * @var object the object that owns this view. This can be a controller, a widget, or any other object.
 	 */
-	public $context;
+	public $owner;
 	/**
-	 * @var string|array the directories where the view file should be looked for when a *relative* view name is given.
-	 * This can be either a string representing a single directory, or an array representing multiple directories.
-	 * If the latter, the view file will be looked for in the directories in the order they are specified.
-	 * Path aliases can be used. If this property is not set, relative view names should be treated as absolute ones.
-	 * @see roothPath
+	 * @var string the layout to be applied when [[render()]] or [[renderContent()]] is called.
+	 * If not set, it will use the [[Module::layout]] of the currently active module.
 	 */
-	public $basePath;
+	public $layout;
 	/**
 	 * @var string the language that the view should be rendered in. If not set, it will use
 	 * the value of [[Application::language]].
@@ -45,45 +47,67 @@ class View extends Component
 	 * Note that when this is true, if a localized view cannot be found, the original view will be rendered.
 	 * No error will be reported.
 	 */
-	public $localizeView = true;
+	public $enableI18N = true;
 	/**
 	 * @var boolean whether to theme the view when possible. Defaults to true.
-	 * Note that theming will be disabled if [[Application::theme]] is null.
+	 * Note that theming will be disabled if [[Application::theme]] is not set.
 	 */
-	public $themeView = true;
+	public $enableTheme = true;
 	/**
 	 * @var mixed custom parameters that are available in the view template
 	 */
 	public $params;
+
 	/**
 	 * @var Widget[] the widgets that are currently not ended
 	 */
-	protected  $widgetStack = array();
+	private  $_widgetStack = array();
 
 	/**
 	 * Constructor.
-	 * @param Controller|Widget|Object $context the context under which this view is being rendered (e.g. controller, widget)
+	 * @param object $owner the owner of this view. This usually is a controller or a widget.
 	 * @param array $config name-value pairs that will be used to initialize the object properties
 	 */
-	public function __construct($context = null, $config = array())
+	public function __construct($owner, $config = array())
 	{
-		$this->context = $context;
+		$this->owner = $owner;
 		parent::__construct($config);
 	}
 
+	/**
+	 * Renders a view within a layout.
+	 * This method is similar to [[renderPartial()]] except that if a layout is available,
+	 * this method will embed the view result into the layout and then return it.
+	 * @param string $view the view to be rendered. Please refer to [[findViewFile()]] on possible formats of the view name.
+	 * @param array $params the parameters that should be made available in the view. The PHP function `extract()`
+	 * will be called on this variable to extract the variables from this parameter.
+	 * @return string the rendering result
+	 * @throws InvalidConfigException if the view file or layout file cannot be found
+	 * @see findViewFile()
+	 * @see findLayoutFile()
+	 */
 	public function render($view, $params = array())
 	{
 		$content = $this->renderPartial($view, $params);
-		return $this->renderText($content);
+		return $this->renderContent($content);
 	}
 
-	public function renderText($text)
+	/**
+	 * Renders a text content within a layout.
+	 * The layout being used is resolved by [[findLayout()]].
+	 * If no layout is available, the content will be returned back.
+	 * @param string $content the content to be rendered
+	 * @return string the rendering result
+	 * @throws InvalidConfigException if the layout file cannot be found
+	 * @see findLayoutFile()
+	 */
+	public function renderContent($content)
 	{
 		$layoutFile = $this->findLayoutFile();
 		if ($layoutFile !== false) {
-			return $this->renderFile($layoutFile, array('content' => $text));
+			return $this->renderFile($layoutFile, array('content' => $content));
 		} else {
-			return $text;
+			return $content;
 		}
 	}
 
@@ -94,18 +118,12 @@ class View extends Component
 	 * It then calls [[renderFile()]] to render the view file. The rendering result is returned
 	 * as a string. If the view file does not exist, an exception will be thrown.
 	 *
-	 * 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`),
-	 * or a path relative to [[basePath]]. The file suffix is optional and defaults to `.php` if not given
-	 * in the view name.
-	 *
-	 * @param string $view the view to be rendered. This can be either a path alias or a path relative to [[basePath]].
+	 * @param string $view the view to be rendered. Please refer to [[findViewFile()]] on possible formats of the view name.
 	 * @param array $params the parameters that should be made available in the view. The PHP function `extract()`
 	 * will be called on this variable to extract the variables from this parameter.
 	 * @return string the rendering result
 	 * @throws InvalidCallException if the view file cannot be found
+	 * @see findViewFile()
 	 */
 	public function renderPartial($view, $params = array())
 	{
@@ -119,21 +137,44 @@ class View extends Component
 
 	/**
 	 * Renders a view file.
-	 * @param string $file the view file path
-	 * @param array $params the parameters to be extracted and made available in the view file
+	 * This method will extract the given parameters and include the view file.
+	 * It captures the output of the included view file and returns it as a string.
+	 * @param string $_file_ the view file.
+	 * @param array $_params_ the parameters (name-value pairs) that will be extracted and made available in the view file.
 	 * @return string the rendering result
 	 */
-	public function renderFile($file, $params = array())
+	public function renderFile($_file_, $_params_ = array())
 	{
-		return $this->renderFileInternal($file, $params);
+		ob_start();
+		ob_implicit_flush(false);
+		extract($_params_, EXTR_OVERWRITE);
+		require($_file_);
+		return ob_get_clean();
 	}
 
+	/**
+	 * Creates a widget.
+	 * This method will use [[Yii::createObject()]] to create the widget.
+	 * @param string $class the widget class name or path alias
+	 * @param array $properties the initial property values of the widget.
+	 * @return Widget the newly created widget instance
+	 */
 	public function createWidget($class, $properties = array())
 	{
 		$properties['class'] = $class;
-		return \Yii::createObject($properties, $this->context);
+		return Yii::createObject($properties, $this->owner);
 	}
 
+	/**
+	 * Creates and runs a widget.
+	 * Compared with [[createWidget()]], this method does one more thing: it will
+	 * run the widget after it is created.
+	 * @param string $class the widget class name or path alias
+	 * @param array $properties the initial property values of the widget.
+	 * @param boolean $captureOutput whether to capture the output of the widget and return it as a string
+	 * @return string|Widget if $captureOutput is true, the output of the widget will be returned;
+	 * otherwise the widget object will be returned.
+	 */
 	public function widget($class, $properties = array(), $captureOutput = false)
 	{
 		if ($captureOutput) {
@@ -151,14 +192,16 @@ class View extends Component
 
 	/**
 	 * Begins a widget.
-	 * @param string $class the widget class
-	 * @param array $properties the initial property values of the widget
+	 * This method is similar to [[createWidget()]] except that it will expect a matching
+	 * [[endWidget()]] call after this.
+	 * @param string $class the widget class name or path alias
+	 * @param array $properties the initial property values of the widget.
 	 * @return Widget the widget instance
 	 */
 	public function beginWidget($class, $properties = array())
 	{
 		$widget = $this->createWidget($class, $properties);
-		$this->widgetStack[] = $widget;
+		$this->_widgetStack[] = $widget;
 		return $widget;
 	}
 
@@ -172,293 +215,256 @@ class View extends Component
 	 */
 	public function endWidget()
 	{
-		/** @var $widget Widget */
-		if (($widget = array_pop($this->widgetStack)) !== null) {
+		$widget = array_pop($this->_widgetStack);
+		if ($widget instanceof Widget) {
 			$widget->run();
 			return $widget;
 		} else {
 			throw new Exception("Unmatched beginWidget() and endWidget() calls.");
 		}
 	}
+//
+//	/**
+//	 * Begins recording a clip.
+//	 * This method is a shortcut to beginning [[yii\widgets\Clip]]
+//	 * @param string $id the clip ID.
+//	 * @param array $properties initial property values for [[yii\widgets\Clip]]
+//	 */
+//	public function beginClip($id, $properties = array())
+//	{
+//		$properties['id'] = $id;
+//		$this->beginWidget('yii\widgets\Clip', $properties);
+//	}
+//
+//	/**
+//	 * Ends recording a clip.
+//	 */
+//	public function endClip()
+//	{
+//		$this->endWidget();
+//	}
+//
+//	/**
+//	 * Begins fragment caching.
+//	 * This method will display cached content if it is available.
+//	 * If not, it will start caching and would expect an [[endCache()]]
+//	 * call to end the cache and save the content into cache.
+//	 * A typical usage of fragment caching is as follows,
+//	 *
+//	 * ~~~
+//	 * if($this->beginCache($id)) {
+//	 *     // ...generate content here
+//	 *     $this->endCache();
+//	 * }
+//	 * ~~~
+//	 *
+//	 * @param string $id a unique ID identifying the fragment to be cached.
+//	 * @param array $properties initial property values for [[yii\widgets\OutputCache]]
+//	 * @return boolean whether we need to generate content for caching. False if cached version is available.
+//	 * @see endCache
+//	 */
+//	public function beginCache($id, $properties = array())
+//	{
+//		$properties['id'] = $id;
+//		$cache = $this->beginWidget('yii\widgets\OutputCache', $properties);
+//		if ($cache->getIsContentCached()) {
+//			$this->endCache();
+//			return false;
+//		} else {
+//			return true;
+//		}
+//	}
+//
+//	/**
+//	 * Ends fragment caching.
+//	 * This is an alias to [[endWidget()]]
+//	 * @see beginCache
+//	 */
+//	public function endCache()
+//	{
+//		$this->endWidget();
+//	}
+//
+//	/**
+//	 * Begins the rendering of content that is to be decorated by the specified view.
+//	 * @param mixed $view the name of the view that will be used to decorate the content. The actual view script
+//	 * is resolved via {@link getViewFile}. If this parameter is null (default),
+//	 * the default layout will be used as the decorative view.
+//	 * Note that if the current controller does not belong to
+//	 * any module, the default layout refers to the application's {@link CWebApplication::layout default layout};
+//	 * If the controller belongs to a module, the default layout refers to the module's
+//	 * {@link CWebModule::layout default layout}.
+//	 * @param array $params the variables (name=>value) to be extracted and made available in the decorative view.
+//	 * @see endContent
+//	 * @see yii\widgets\ContentDecorator
+//	 */
+//	public function beginContent($view, $params = array())
+//	{
+//		$this->beginWidget('yii\widgets\ContentDecorator', array(
+//			'view' => $view,
+//			'params' => $params,
+//		));
+//	}
+//
+//	/**
+//	 * Ends the rendering of content.
+//	 * @see beginContent
+//	 */
+//	public function endContent()
+//	{
+//		$this->endWidget();
+//	}
 
 	/**
-	 * Begins recording a clip.
-	 * This method is a shortcut to beginning [[yii\widgets\Clip]]
-	 * @param string $id the clip ID.
-	 * @param array $properties initial property values for [[yii\widgets\Clip]]
-	 */
-	public function beginClip($id, $properties = array())
-	{
-		$properties['id'] = $id;
-		$this->beginWidget('yii\widgets\Clip', $properties);
-	}
-
-	/**
-	 * Ends recording a clip.
-	 */
-	public function endClip()
-	{
-		$this->endWidget();
-	}
-
-	/**
-	 * Begins fragment caching.
-	 * This method will display cached content if it is available.
-	 * If not, it will start caching and would expect an [[endCache()]]
-	 * call to end the cache and save the content into cache.
-	 * A typical usage of fragment caching is as follows,
+	 * Finds the view file based on the given view name.
 	 *
-	 * ~~~
-	 * if($this->beginCache($id)) {
-	 *     // ...generate content here
-	 *     $this->endCache();
-	 * }
-	 * ~~~
+	 * A view name can be specified in one of the following formats:
+	 *
+	 * - path alias (e.g. "@application/views/site/index");
+	 * - absolute path within application (e.g. "//site/index"): the view name starts with double slashes.
+	 *   The actual view file will be looked for under the [[Application::viewPath|view path]] of the application.
+	 * - absolute path within module (e.g. "/site/index"): the view name starts with a single slash.
+	 *   The actual view file will be looked for under the [[Module::viewPath|view path]] of the currently
+	 *   active module.
+	 * - relative path (e.g. "index"): the actual view file will be looked for under the [[owner]]'s view path.
+	 *   If [[owner]] is a widget or a controller, its view path is given by their `viewPath` property.
+	 *   If [[owner]] is an object of any other type, its view path is the `view` sub-directory of the directory
+	 *   containing the owner class file.
+	 *
+	 * If the view name does not contain a file extension, it will default to `.php`.
+	 *
+	 * If [[enableTheme]] is true and there is an active application them, the method will also
+	 * attempt to use a themed version of the view file, when available.
+	 *
+	 * And if [[enableI18N]] is true, the method will attempt to use a translated version of the view file,
+	 * when available.
 	 *
-	 * @param string $id a unique ID identifying the fragment to be cached.
-	 * @param array $properties initial property values for [[yii\widgets\OutputCache]]
-	 * @return boolean whether we need to generate content for caching. False if cached version is available.
-	 * @see endCache
-	 */
-	public function beginCache($id, $properties = array())
-	{
-		$properties['id'] = $id;
-		$cache = $this->beginWidget('yii\widgets\OutputCache', $properties);
-		if ($cache->getIsContentCached()) {
-			$this->endCache();
-			return false;
-		} else {
-			return true;
-		}
-	}
-
-	/**
-	 * Ends fragment caching.
-	 * This is an alias to [[endWidget()]]
-	 * @see beginCache
-	 */
-	public function endCache()
-	{
-		$this->endWidget();
-	}
-
-	/**
-	 * Begins the rendering of content that is to be decorated by the specified view.
-	 * @param mixed $view the name of the view that will be used to decorate the content. The actual view script
-	 * is resolved via {@link getViewFile}. If this parameter is null (default),
-	 * the default layout will be used as the decorative view.
-	 * Note that if the current controller does not belong to
-	 * any module, the default layout refers to the application's {@link CWebApplication::layout default layout};
-	 * If the controller belongs to a module, the default layout refers to the module's
-	 * {@link CWebModule::layout default layout}.
-	 * @param array $params the variables (name=>value) to be extracted and made available in the decorative view.
-	 * @see endContent
-	 * @see yii\widgets\ContentDecorator
-	 */
-	public function beginContent($view, $params = array())
-	{
-		$this->beginWidget('yii\widgets\ContentDecorator', array(
-			'view' => $view,
-			'params' => $params,
-		));
-	}
-
-	/**
-	 * Ends the rendering of content.
-	 * @see beginContent
-	 */
-	public function endContent()
-	{
-		$this->endWidget();
-	}
-
-	/**
-	 * Renders a view file.
-	 * This method will extract the given parameters and include the view file.
-	 * It captures the output of the included view file and returns it as a string.
-	 * @param string $_file_ the view file.
-	 * @param array $_params_ the parameters (name-value pairs) that will be extracted and made available in the view file.
-	 * @return string the rendering result
-	 */
-	protected function renderFileInternal($_file_, $_params_ = array())
-	{
-		ob_start();
-		ob_implicit_flush(false);
-		extract($_params_, EXTR_OVERWRITE);
-		require($_file_);
-		return ob_get_clean();
-	}
-
-	/**
-	 * Finds the view file based on the given view name.
 	 * @param string $view the view name or path alias. If the view name does not specify
 	 * the view file extension name, it will use `.php` as the extension name.
-	 * @return string|boolean the view file if it exists. False if the view file cannot be found.
+	 * @return string the view file path if it exists. False if the view file cannot be found.
+	 * @throws InvalidConfigException if the view file does not exist
 	 */
 	public function findViewFile($view)
 	{
-		if (($extension = FileHelper::getExtension($view)) === '') {
+		if (FileHelper::getExtension($view) === '') {
 			$view .= '.php';
 		}
 		if (strncmp($view, '@', 1) === 0) {
-			$file = \Yii::getAlias($view);
+			// e.g. "@application/views/common"
+			if (($file = Yii::getAlias($view)) === false) {
+				throw new InvalidConfigException("Invalid path alias: $view");
+			}
 		} elseif (strncmp($view, '/', 1) !== 0) {
-			$file = $this->findRelativeViewFile($view);
-		} else {
-			$file = $this->findAbsoluteViewFile($view);
-		}
-
-		if ($file === false || !is_file($file)) {
-			return false;
-		} elseif ($this->localizeView) {
-			return FileHelper::localize($file, $this->language, $this->sourceLanguage);
+			// e.g. "index"
+			if ($this->owner instanceof Controller || $this->owner instanceof Widget) {
+				$file = $this->owner->getViewPath() . DIRECTORY_SEPARATOR . $view;
+			} elseif ($this->owner !== null) {
+				$class = new \ReflectionClass($this->owner);
+				$file = dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $view;
+			} else {
+				$file = Yii::$application->getViewPath() . DIRECTORY_SEPARATOR . $view;
+			}
+		} elseif (strncmp($view, '//', 2) !== 0 && Yii::$application->controller !== null) {
+			// e.g. "/site/index"
+			$file = Yii::$application->controller->module->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
 		} else {
-			return $file;
+			// e.g. "//layouts/main"
+			$file = Yii::$application->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
 		}
-	}
 
-	/**
-	 * Finds the view file corresponding to the given relative view name.
-	 * The method will look for the view file under a set of directories returned by [[resolveBasePath()]].
-	 * If no base path is given, the view will be treated as an absolute view and the result of
-	 * [[findAbsoluteViewFile()]] will be returned.
-	 * @param string $view the relative view name
-	 * @return string|boolean the view file path, or false if the view file cannot be found
-	 */
-	protected function findRelativeViewFile($view)
-	{
-		$paths = $this->resolveBasePath();
-		if ($paths === array()) {
-			return $this->findAbsoluteViewFile($view);
-		}
-		if ($this->themeView && $this->context !== null && ($theme = \Yii::$application->getTheme()) !== null) {
-			array_unshift($paths, $theme->getViewPath($this->context));
-		}
-		foreach ($paths as $path) {
-			$file = \Yii::getAlias($path . '/' . $view);
-			if ($file !== false && is_file($file)) {
-				return $file;
+		if (is_file($file)) {
+			if ($this->enableTheme && ($theme = Yii::$application->getTheme()) !== null) {
+				$file = $theme->apply($file);
 			}
-		}
-		return $paths === array() ? $this->findAbsoluteViewFile($view) : false;
-	}
-
-	/**
-	 * Finds the view file corresponding to the given absolute view name.
-	 * If the view name starts with double slashes `//`, the method will look for the view file
-	 * under [[Application::getViewPath()]]. Otherwise, it will look for the view file under the
-	 * view path of the currently active module.
-	 * @param string $view the absolute view name
-	 * @return string|boolean the view file path, or false if the view file cannot be found
-	 */
-	protected function findAbsoluteViewFile($view)
-	{
-		$app = \Yii::$application;
-		if (strncmp($view, '//', 2) !== 0 && $app->controller !== null) {
-			$module = $app->controller->module;
+			return $this->enableI18N ? FileHelper::localize($file, $this->language, $this->sourceLanguage) : $file;
 		} else {
-			$module = $app;
+			throw new InvalidConfigException("View file for view '$view' does not exist: $file");
 		}
-		if ($this->themeView && ($theme = $app->getTheme()) !== null) {
-			$paths[] = $theme->getViewPath($module);
-		}
-		$paths[] = $module->getViewPath();
-		$view = ltrim($view, '/');
-		foreach ($paths as $path) {
-			$file = \Yii::getAlias($path . '/' . $view);
-			if ($file !== false && is_file($file)) {
-				return $file;
-			}
-		}
-		return false;
 	}
 
 	/**
-	 * Resolves the base paths that will be used to determine view files for relative view names.
-	 * The method resolves the base path using the following algorithm:
+	 * Finds the layout file that can be applied to the view.
 	 *
-	 * - If [[basePath]] is not empty, it is returned;
-	 * - If [[context]] is a controller, it will return the subdirectory named as
-	 *   [[Controller::uniqueId]] under the controller's module view path;
-	 * - If [[context]] is an object, it will return the `views` subdirectory under
-	 *   the directory containing the object class file.
-	 * - Otherwise, it will return false.
-	 * @return array the base paths
-	 */
-	protected function resolveBasePath()
-	{
-		if (!empty($this->basePath)) {
-			return (array)$this->basePath;
-		} elseif ($this->context instanceof Controller) {
-			return array($this->context->module->getViewPath() . '/' . $this->context->getUniqueId());
-		} elseif ($this->context !== null) {
-			$class = new \ReflectionClass($this->context);
-			return array(dirname($class->getFileName()) . '/views');
-		} else {
-			return array();
-		}
-	}
-
-	/**
-	 * Finds the layout file for the current [[context]].
-	 * The method will return false if [[context]] is not a controller.
-	 * When [[context]] is a controller, the following algorithm is used to determine the layout file:
+	 * The applicable layout is resolved according to the following rules:
+	 *
+	 * - If [[layout]] is specified as a string, use it as the layout name and search for the layout file
+	 *   under the layout path of the currently active module;
+	 * - If [[layout]] is null and [[owner]] is a controller:
+	 *      * If the controller's [[Controller::layout|layout]] is a string, use it as the layout name
+	 *        and search for the layout file under the layout path of the parent module of the controller;
+	 *      * If the controller's [[Controller::layout|layout]] is null, look through its ancestor modules
+	 *        and find the first one whose [[Module::layout|layout]] is not null. Use the layout specified
+	 *        by that module;
+	 * - Returns false for all other cases.
+	 *
+	 * Like view names, a layout name can take several formats:
 	 *
-	 * - If `context.layout` is false, it will return false;
-	 * - If `context.layout` is a string, it will look for the layout file under the [[Module::layoutPath|layout path]]
-	 *   of the controller's parent module;
-	 * - If `context.layout` is null, the following steps are taken to resolve the actual layout to be returned:
-	 *      * Check the `layout` property of the parent module. If it is null, check the grand parent module and so on
-	 *        until a non-null layout is encountered. Let's call this module the *effective module*.
-	 *      * If the layout is null or false, it will return false;
-	 *      * Otherwise, it will look for the layout file under the layout path of the effective module.
+	 * - path alias (e.g. "@application/views/layouts/main");
+	 * - absolute path (e.g. "/main"): the layout name starts with a slash. The actual layout file will be
+	 *   looked for under the [[Application::layoutPath|layout path]] of the application;
+	 * - relative path (e.g. "main"): the actual layout layout file will be looked for under the
+	 *   [[Module::viewPath|view path]] of the context module determined by the above layout resolution process.
 	 *
-	 * The themed layout file will be returned if theme is enabled and the theme contains such a layout file.
+	 * If the layout name does not contain a file extension, it will default to `.php`.
 	 *
-	 * @return string|boolean the layout file path, or false if the context does not need layout.
-	 * @throws InvalidCallException if the layout file cannot be found
+	 * If [[enableTheme]] is true and there is an active application them, the method will also
+	 * attempt to use a themed version of the layout file, when available.
+	 *
+	 * And if [[enableI18N]] is true, the method will attempt to use a translated version of the layout file,
+	 * when available.
+	 *
+	 * @return string|boolean the layout file path, or false if layout is not needed.
+	 * @throws InvalidConfigException if the layout file cannot be found
 	 */
 	public function findLayoutFile()
 	{
-		if (!$this->context instanceof Controller || $this->context->layout === false) {
-			return false;
-		}
-		$module = $this->context->module;
-		while ($module !== null && $module->layout === null) {
-			$module = $module->module;
+		/** @var $module Module */
+		if (is_string($this->layout)) {
+			if (Yii::$application->controller) {
+				$module = Yii::$application->controller->module;
+			} else {
+				$module = Yii::$application;
+			}
+			$view = $this->layout;
+		} elseif ($this->owner instanceof Controller) {
+			if (is_string($this->owner->layout)) {
+				$module = $this->owner->module;
+				$view = $this->owner->layout;
+			} elseif ($this->owner->layout === null) {
+				$module = $this->owner->module;
+				while ($module !== null && $module->layout === null) {
+					$module = $module->module;
+				}
+				if ($module !== null && is_string($module->layout)) {
+					$view = $module->layout;
+				}
+			}
 		}
-		if ($module === null || $module->layout === null || $module->layout === false) {
+
+		if (!isset($view)) {
 			return false;
 		}
 
-		$view = $module->layout;
-		if (($extension = FileHelper::getExtension($view)) === '') {
+		if (FileHelper::getExtension($view) === '') {
 			$view .= '.php';
 		}
 		if (strncmp($view, '@', 1) === 0) {
-			$file = \Yii::getAlias($view);
+			if (($file = Yii::getAlias($view)) === false) {
+				throw new InvalidConfigException("Invalid path alias: $view");
+			}
 		} elseif (strncmp($view, '/', 1) === 0) {
-			$file = $this->findAbsoluteViewFile($view);
+			$file = Yii::$application->getLayoutPath() . DIRECTORY_SEPARATOR . $view;
 		} else {
-			if ($this->themeView && ($theme = \Yii::$application->getTheme()) !== null) {
-				$paths[] = $theme->getLayoutPath($module);
-			}
-			$paths[] = $module->getLayoutPath();
-			$file = false;
-			foreach ($paths as $path) {
-				$f = \Yii::getAlias($path . '/' . $view);
-				if ($f !== false && is_file($f)) {
-					$file = $f;
-					break;
-				}
-			}
+			$file = $module->getLayoutPath() . DIRECTORY_SEPARATOR . $view;
 		}
-		if ($file === false || !is_file($file)) {
-			throw new InvalidCallException("Unable to find the layout file for layout '{$module->layout}' (specified by " . get_class($module) . ")");
-		} elseif ($this->localizeView) {
-			return FileHelper::localize($file, $this->language, $this->sourceLanguage);
+
+		if (is_file($file)) {
+			if ($this->enableTheme && ($theme = Yii::$application->getTheme()) !== null) {
+				$file = $theme->apply($file);
+			}
+			return $this->enableI18N ? FileHelper::localize($file, $this->language, $this->sourceLanguage) : $file;
 		} else {
-			return $file;
+			throw new InvalidConfigException("Layout file for layout '$view' does not exist: $file");
 		}
 	}
 }
\ No newline at end of file
diff --git a/framework/base/Widget.php b/framework/base/Widget.php
index 3608205..bdec634 100644
--- a/framework/base/Widget.php
+++ b/framework/base/Widget.php
@@ -49,7 +49,7 @@ class Widget extends Component
 	public function getId($autoGenerate = true)
 	{
 		if ($autoGenerate && $this->_id === null) {
-			$this->_id = 'yw' . self::$_counter++;
+			$this->_id = 'w' . self::$_counter++;
 		}
 		return $this->_id;
 	}
@@ -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.
 	 *
@@ -102,4 +102,16 @@ class Widget extends Component
 	{
 		return new View($this);
 	}
+
+	/**
+	 * Returns the directory containing the view files for this widget.
+	 * The default implementation returns the 'views' subdirectory under the directory containing the widget class file.
+	 * @return string the directory containing the view files for this widget.
+	 */
+	public function getViewPath()
+	{
+		$className = get_class($this);
+		$class = new \ReflectionClass($className);
+		return dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views';
+	}
 }
\ No newline at end of file
diff --git a/framework/console/Application.php b/framework/console/Application.php
index 23d80e0..237be05 100644
--- a/framework/console/Application.php
+++ b/framework/console/Application.php
@@ -10,7 +10,7 @@
 namespace yii\console;
 
 use yii\base\Exception;
-use yii\util\ReflectionHelper;
+use yii\base\InvalidRouteException;
 
 /**
  * Application represents a console application.
@@ -85,40 +85,36 @@ class Application extends \yii\base\Application
 	 * Processes the request.
 	 * The request is represented in terms of a controller route and action parameters.
 	 * @return integer the exit status of the controller action (0 means normal, non-zero values mean abnormal)
-	 * @throws Exception if the route cannot be resolved into a controller
 	 */
 	public function processRequest()
 	{
 		/** @var $request Request */
 		$request = $this->getRequest();
 		if ($request->getIsConsoleRequest()) {
-			return $this->runController($request->route, $request->params);
+			return $this->runAction($request->route, $request->params);
 		} else {
-			die('This script must be run from the command line.');
+			echo "Error: this script must be run from the command line.";
+			return 1;
 		}
 	}
 
 	/**
-	 * 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 Exception if the route cannot be resolved into a controller
+	 * 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 status code returned by the action execution. 0 means normal, and other values mean abnormal.
 	 */
-	public function runController($route, $params = array())
+	public function runAction($route, $params = array())
 	{
-		$result = $this->createController($route);
-		if ($result === false) {
-			throw new Exception(\Yii::t('yii', 'Unable to resolve the request.'));
+		try {
+			return parent::runAction($route, $params);
+		} catch (InvalidRouteException $e) {
+			echo "Error: unknown command \"$route\".\n";
+			return 1;
 		}
-		/** @var $controller \yii\console\Controller */
-		list($controller, $action) = $result;
-		$priorController = $this->controller;
-		$this->controller = $controller;
-		$params = ReflectionHelper::initObjectWithParams($controller, $params);
-		$status = $controller->run($action, $params);
-		$this->controller = $priorController;
-		return $status;
 	}
 
 	/**
@@ -152,4 +148,9 @@ class Application extends \yii\base\Application
 			),
 		));
 	}
+
+	public function usageError($message)
+	{
+
+	}
 }
diff --git a/framework/console/Controller.php b/framework/console/Controller.php
index 250cefe..16968f2 100644
--- a/framework/console/Controller.php
+++ b/framework/console/Controller.php
@@ -9,8 +9,10 @@
 
 namespace yii\console;
 
+use Yii;
 use yii\base\Action;
-use yii\base\Exception;
+use yii\base\InvalidRequestException;
+use yii\base\InvalidRouteException;
 
 /**
  * Controller is the base class of console command classes.
@@ -30,72 +32,56 @@ use yii\base\Exception;
 class Controller extends \yii\base\Controller
 {
 	/**
-	 * This method is invoked when the request parameters do not satisfy the requirement of the specified action.
-	 * The default implementation will throw an exception.
-	 * @param Action $action the action being executed
-	 * @param Exception $exception the exception about the invalid parameters
+	 * @var boolean whether the call of [[confirm()]] requires a user input.
+	 * If false, [[confirm()]] will always return true no matter what user enters or not.
 	 */
-	public function invalidActionParams($action, $exception)
-	{
-		echo \Yii::t('yii', 'Error: {message}', array(
-			'{message}' => $exception->getMessage(),
-		));
-		\Yii::$application->end(1);
-	}
+	public $interactive = true;
 
 	/**
-	 * This method is invoked when extra parameters are provided to an action while it is executed.
-	 * @param Action $action the action being executed
-	 * @param array $expected the expected action parameters (name => value)
-	 * @param array $actual the actual action parameters (name => value)
+	 * 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.
+	 * @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 extraActionParams($action, $expected, $actual)
+	public function runAction($id, $params = array())
 	{
-		unset($expected['args'], $actual['args']);
-
-		$keys = array_diff(array_keys($actual), array_keys($expected));
-		if (!empty($keys)) {
-			echo \Yii::t('yii', 'Error: Unknown parameter(s): {params}', array(
-				'{params}' => implode(', ', $keys),
-			)) . "\n";
-			\Yii::$application->end(1);
+		if ($params !== array()) {
+			$class = new \ReflectionClass($this);
+			foreach ($params as $name => $value) {
+				if ($class->hasProperty($name)) {
+					$property = $class->getProperty($name);
+					if ($property->isPublic() && !$property->isStatic() && $property->getDeclaringClass()->getName() === get_class($this)) {
+						$this->$name = $value;
+						unset($params[$name]);
+					}
+				}
+			}
 		}
+		return parent::runAction($id, $params);
 	}
 
 	/**
-	 * Reads input via the readline PHP extension if that's available, or fgets() if readline is not installed.
-	 *
-	 * @param string $message to echo out before waiting for user input
-	 * @param string $default the default string to be returned when user does not write anything.
-	 * Defaults to null, means that default string is disabled.
-	 * @return mixed line read as a string, or false if input has been closed
+	 * Validates the parameter being bound to actions.
+	 * This method is invoked when parameters are being bound to the currently requested action.
+	 * Child classes may override this method to throw exceptions when there are missing and/or unknown parameters.
+	 * @param Action $action the currently requested action
+	 * @param array $missingParams the names of the missing parameters
+	 * @param array $unknownParams the unknown parameters (name=>value)
+	 * @throws InvalidRequestException if there are missing or unknown parameters
 	 */
-	public function prompt($message, $default = null)
+	public function validateActionParams($action, $missingParams, $unknownParams)
 	{
-		if($default !== null) {
-			$message .= " [$default] ";
-		}
-		else {
-			$message .= ' ';
-		}
-
-		if(extension_loaded('readline')) {
-			$input = readline($message);
-			if($input !== false) {
-				readline_add_history($input);
-			}
-		}
-		else {
-			echo $message;
-			$input = fgets(STDIN);
-		}
-
-		if($input === false) {
-			return false;
-		}
-		else {
-			$input = trim($input);
-			return ($input === '' && $default !== null) ? $default : $input;
+		if (!empty($missingParams)) {
+			throw new InvalidRequestException(Yii::t('yii', 'Missing required options: {params}', array(
+				'{params}' => implode(', ', $missingParams),
+			)));
+		} elseif (!empty($unknownParams)) {
+			throw new InvalidRequestException(Yii::t('yii', 'Unknown options: {params}', array(
+				'{params}' => implode(', ', $unknownParams),
+			)));
 		}
 	}
 
@@ -108,9 +94,23 @@ class Controller extends \yii\base\Controller
 	 */
 	public function confirm($message, $default = false)
 	{
-		echo $message . ' (yes|no) [' . ($default ? 'yes' : 'no') . ']:';
+		if ($this->interactive) {
+			echo $message . ' (yes|no) [' . ($default ? 'yes' : 'no') . ']:';
+			$input = trim(fgets(STDIN));
+			return empty($input) ? $default : !strncasecmp($input, 'y', 1);
+		} else {
+			return true;
+		}
+	}
+
+	public function usageError($message)
+	{
+		echo "\nError: $message\n";
+		Yii::$application->end(1);
+	}
 
-		$input = trim(fgets(STDIN));
-		return empty($input) ? $default : !strncasecmp($input, 'y', 1);
+	public function globalOptions()
+	{
+		return array();
 	}
 }
\ No newline at end of file
diff --git a/framework/console/controllers/CreateController.php b/framework/console/controllers/CreateController.php
index a513e40..7bd7fd0 100644
--- a/framework/console/controllers/CreateController.php
+++ b/framework/console/controllers/CreateController.php
@@ -165,8 +165,8 @@ class CreateController extends Controller
 	}
 
 	/**
-	 * @param string $path1 abosolute path
-	 * @param string $path2 abosolute path
+	 * @param string $path1 absolute path
+	 * @param string $path2 absolute path
 	 *
 	 * @return string relative path
 	 */
diff --git a/framework/console/controllers/HelpController.php b/framework/console/controllers/HelpController.php
index f4d1eb8..6e4b397 100644
--- a/framework/console/controllers/HelpController.php
+++ b/framework/console/controllers/HelpController.php
@@ -12,6 +12,7 @@ namespace yii\console\controllers;
 use yii\base\Application;
 use yii\base\InlineAction;
 use yii\console\Controller;
+use yii\util\StringHelper;
 
 /**
  * This command provides help information about console commands.
@@ -54,16 +55,16 @@ class HelpController extends Controller
 		} else {
 			$result = \Yii::$application->createController($args[0]);
 			if ($result === false) {
-				echo "Unknown command: " . $args[0] . "\n";
+				echo "Error: no help for unknown command \"{$args[0]}\".\n";
 				return 1;
 			}
 
-			list($controller, $action) = $result;
+			list($controller, $actionID) = $result;
 
-			if ($action === '') {
+			if ($actionID === '') {
 				$status = $this->getControllerHelp($controller);
 			} else {
-				$status = $this->getActionHelp($controller, $action);
+				$status = $this->getActionHelp($controller, $actionID);
 			}
 		}
 		return $status;
@@ -87,13 +88,13 @@ class HelpController extends Controller
 	 */
 	public function getActions($controller)
 	{
-		$actions = array_keys($controller->actionMap);
+		$actions = array_keys($controller->actions());
 		$class = new \ReflectionClass($controller);
 		foreach ($class->getMethods() as $method) {
 			/** @var $method \ReflectionMethod */
 			$name = $method->getName();
-			if ($method->isPublic() && !$method->isStatic() && strpos($name, 'action') === 0) {
-				$actions[] = lcfirst(substr($name, 6));
+			if ($method->isPublic() && !$method->isStatic() && strpos($name, 'action') === 0 && $name !== 'actions') {
+				$actions[] = StringHelper::camel2id(substr($name, 6));
 			}
 		}
 		sort($actions);
@@ -107,11 +108,7 @@ class HelpController extends Controller
 	 */
 	protected function getModuleCommands($module)
 	{
-		if ($module instanceof Application) {
-			$prefix = '';
-		} else {
-			$prefix = $module->getUniqueId() . '/';
-		}
+		$prefix = $module instanceof Application ? '' : $module->getUniqueID() . '/';
 
 		$commands = array();
 		foreach (array_keys($module->controllerMap) as $id) {
@@ -145,12 +142,12 @@ class HelpController extends Controller
 	{
 		$commands = $this->getCommands();
 		if ($commands !== array()) {
-			echo "\n    Usage: yiic <command-name> [...options...]\n\n";
-			echo "The following commands are available:\n";
+			echo "\nUsage: yiic <command-name> [...options...]\n\n";
+			echo "The following commands are available:\n\n";
 			foreach ($commands as $command) {
-				echo " - $command\n";
+				echo " * $command\n";
 			}
-			echo "\nTo see individual command help, enter:\n";
+			echo "\nTo see the help of each command, enter:\n";
 			echo "\n    yiic help <command-name>\n";
 		} else {
 			echo "\nNo commands are found.\n";
@@ -195,7 +192,7 @@ class HelpController extends Controller
 			$prefix = $controller->getUniqueId();
 			foreach ($actions as $action) {
 				if ($controller->defaultAction === $action) {
-					echo " * $prefix/$action (default)\n";
+					echo " * $prefix (default)\n";
 				} else {
 					echo " * $prefix/$action\n";
 				}
@@ -216,7 +213,7 @@ class HelpController extends Controller
 	{
 		$action = $controller->createAction($actionID);
 		if ($action === null) {
-			echo "Unknown sub-command: " . $controller->getUniqueId() . "/$actionID\n";
+			echo 'Error: no help for unknown sub-command "' . $controller->getUniqueId() . "/$actionID\".\n";
 			return 1;
 		}
 		if ($action instanceof InlineAction) {
@@ -312,7 +309,7 @@ class HelpController extends Controller
 	{
 		$options = array();
 		foreach ($class->getProperties() as $property) {
-			if (!$property->isPublic() || $property->isStatic() || $property->getDeclaringClass()->getName() === 'yii\base\Controller') {
+			if (!$property->isPublic() || $property->isStatic() || $property->getDeclaringClass()->getName() !== get_class($controller)) {
 				continue;
 			}
 			$name = $property->getName();
diff --git a/framework/console/controllers/MigrateController.php b/framework/console/controllers/MigrateController.php
index 1da8001..e104856 100644
--- a/framework/console/controllers/MigrateController.php
+++ b/framework/console/controllers/MigrateController.php
@@ -10,6 +10,7 @@
 
 namespace yii\console\controllers;
 
+use Yii;
 use yii\console\Controller;
 
 /**
@@ -60,25 +61,25 @@ use yii\console\Controller;
  */
 class MigrateController extends Controller
 {
-	const BASE_MIGRATION='m000000_000000_base';
+	const BASE_MIGRATION = 'm000000_000000_base';
 
 	/**
 	 * @var string the directory that stores the migrations. This must be specified
 	 * in terms of a path alias, and the corresponding directory must exist.
 	 * Defaults to 'application.migrations' (meaning 'protected/migrations').
 	 */
-	public $migrationPath='application.migrations';
+	public $migrationPath = '@application/migrations';
 	/**
 	 * @var string the name of the table for keeping applied migration information.
 	 * This table will be automatically created if not exists. Defaults to 'tbl_migration'.
 	 * The table structure is: (version varchar(255) primary key, apply_time integer)
 	 */
-	public $migrationTable='tbl_migration';
+	public $migrationTable = 'tbl_migration';
 	/**
 	 * @var string the application component ID that specifies the database connection for
 	 * storing migration information. Defaults to 'db'.
 	 */
-	public $connectionID='db';
+	public $connectionID = 'db';
 	/**
 	 * @var string the path of the template file for generating new migrations. This
 	 * must be specified in terms of a path alias (e.g. application.migrations.template).
@@ -88,26 +89,29 @@ class MigrateController extends Controller
 	/**
 	 * @var string the default command action. It defaults to 'up'.
 	 */
-	public $defaultAction='up';
+	public $defaultAction = 'up';
 	/**
 	 * @var boolean whether to execute the migration in an interactive mode. Defaults to true.
 	 * Set this to false when performing migration in a cron job or background process.
 	 */
-	public $interactive=true;
+	public $interactive = true;
+
 
 	public function beforeAction($action)
 	{
-		$path = \Yii::getAlias($this->migrationPath);
-		if($path===false || !is_dir($path)) {
-			echo 'Error: The migration directory does not exist: ' . $this->migrationPath . "\n";
-			\Yii::$application->end(1);
+		if (parent::beforeAction($action)) {
+			$path = Yii::getAlias($this->migrationPath);
+			if ($path === false || !is_dir($path)) {
+				echo 'Error: the migration directory does not exist "' . $this->migrationPath . "\"\n";
+				return false;
+			}
+			$this->migrationPath = $path;
+			$version = Yii::getVersion();
+			echo "\nYii Migration Tool v2.0 (based on Yii v{$version})\n\n";
+			return true;
+		} else {
+			return false;
 		}
-		$this->migrationPath=$path;
-
-		$yiiVersion = \Yii::getVersion();
-		echo "\nYii Migration Tool v2.0 (based on Yii v{$yiiVersion})\n\n";
-
-		return parent::beforeAction($action);
 	}
 
 	/**
@@ -115,34 +119,32 @@ class MigrateController extends Controller
 	 */
 	public function actionUp($args)
 	{
-		if(($migrations = $this->getNewMigrations())===array())
-		{
+		if (($migrations = $this->getNewMigrations()) === array()) {
 			echo "No new migration found. Your system is up-to-date.\n";
-			\Yii::$application->end();
+			Yii::$application->end();
 		}
 
-		$total=count($migrations);
-		$step=isset($args[0]) ? (int)$args[0] : 0;
-		if($step>0) {
-			$migrations=array_slice($migrations,0,$step);
+		$total = count($migrations);
+		$step = isset($args[0]) ? (int)$args[0] : 0;
+		if ($step > 0) {
+			$migrations = array_slice($migrations, 0, $step);
 		}
 
-		$n=count($migrations);
-		if($n===$total)
-			echo "Total $n new ".($n===1 ? 'migration':'migrations')." to be applied:\n";
-		else
-			echo "Total $n out of $total new ".($total===1 ? 'migration':'migrations')." to be applied:\n";
+		$n = count($migrations);
+		if ($n === $total) {
+			echo "Total $n new " . ($n === 1 ? 'migration' : 'migrations') . " to be applied:\n";
+		} else {
+			echo "Total $n out of $total new " . ($total === 1 ? 'migration' : 'migrations') . " to be applied:\n";
+		}
 
-		foreach($migrations as $migration)
+		foreach ($migrations as $migration) {
 			echo "    $migration\n";
+		}
 		echo "\n";
 
-		if($this->confirm('Apply the above '.($n===1 ? 'migration':'migrations')."?"))
-		{
-			foreach($migrations as $migration)
-			{
-				if($this->migrateUp($migration)===false)
-				{
+		if ($this->confirm('Apply the above ' . ($n === 1 ? 'migration' : 'migrations') . "?")) {
+			foreach ($migrations as $migration) {
+				if ($this->migrateUp($migration) === false) {
 					echo "\nMigration failed. All later migrations are canceled.\n";
 					return;
 				}
@@ -153,29 +155,27 @@ class MigrateController extends Controller
 
 	public function actionDown($args)
 	{
-		$step=isset($args[0]) ? (int)$args[0] : 1;
-		if($step<1)
+		$step = isset($args[0]) ? (int)$args[0] : 1;
+		if ($step < 1) {
 			die("Error: The step parameter must be greater than 0.\n");
+		}
 
-		if(($migrations=$this->getMigrationHistory($step))===array())
-		{
+		if (($migrations = $this->getMigrationHistory($step)) === array()) {
 			echo "No migration has been done before.\n";
 			return;
 		}
-		$migrations=array_keys($migrations);
+		$migrations = array_keys($migrations);
 
-		$n=count($migrations);
-		echo "Total $n ".($n===1 ? 'migration':'migrations')." to be reverted:\n";
-		foreach($migrations as $migration)
+		$n = count($migrations);
+		echo "Total $n " . ($n === 1 ? 'migration' : 'migrations') . " to be reverted:\n";
+		foreach ($migrations as $migration) {
 			echo "    $migration\n";
+		}
 		echo "\n";
 
-		if($this->confirm('Revert the above '.($n===1 ? 'migration':'migrations')."?"))
-		{
-			foreach($migrations as $migration)
-			{
-				if($this->migrateDown($migration)===false)
-				{
+		if ($this->confirm('Revert the above ' . ($n === 1 ? 'migration' : 'migrations') . "?")) {
+			foreach ($migrations as $migration) {
+				if ($this->migrateDown($migration) === false) {
 					echo "\nMigration failed. All later migrations are canceled.\n";
 					return;
 				}
@@ -186,37 +186,33 @@ class MigrateController extends Controller
 
 	public function actionRedo($args)
 	{
-		$step=isset($args[0]) ? (int)$args[0] : 1;
-		if($step<1)
+		$step = isset($args[0]) ? (int)$args[0] : 1;
+		if ($step < 1) {
 			die("Error: The step parameter must be greater than 0.\n");
+		}
 
-		if(($migrations=$this->getMigrationHistory($step))===array())
-		{
+		if (($migrations = $this->getMigrationHistory($step)) === array()) {
 			echo "No migration has been done before.\n";
 			return;
 		}
-		$migrations=array_keys($migrations);
+		$migrations = array_keys($migrations);
 
-		$n=count($migrations);
-		echo "Total $n ".($n===1 ? 'migration':'migrations')." to be redone:\n";
-		foreach($migrations as $migration)
+		$n = count($migrations);
+		echo "Total $n " . ($n === 1 ? 'migration' : 'migrations') . " to be redone:\n";
+		foreach ($migrations as $migration) {
 			echo "    $migration\n";
+		}
 		echo "\n";
 
-		if($this->confirm('Redo the above '.($n===1 ? 'migration':'migrations')."?"))
-		{
-			foreach($migrations as $migration)
-			{
-				if($this->migrateDown($migration)===false)
-				{
+		if ($this->confirm('Redo the above ' . ($n === 1 ? 'migration' : 'migrations') . "?")) {
+			foreach ($migrations as $migration) {
+				if ($this->migrateDown($migration) === false) {
 					echo "\nMigration failed. All later migrations are canceled.\n";
 					return;
 				}
 			}
-			foreach(array_reverse($migrations) as $migration)
-			{
-				if($this->migrateUp($migration)===false)
-				{
+			foreach (array_reverse($migrations) as $migration) {
+				if ($this->migrateUp($migration) === false) {
 					echo "\nMigration failed. All later migrations are canceled.\n";
 					return;
 				}
@@ -227,38 +223,37 @@ class MigrateController extends Controller
 
 	public function actionTo($args)
 	{
-		if(isset($args[0]))
-			$version=$args[0];
-		else
+		if (isset($args[0])) {
+			$version = $args[0];
+		} else {
 			$this->usageError('Please specify which version to migrate to.');
+		}
 
-		$originalVersion=$version;
-		if(preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/',$version,$matches))
-			$version='m'.$matches[1];
-		else
+		$originalVersion = $version;
+		if (preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/', $version, $matches)) {
+			$version = 'm' . $matches[1];
+		} else {
 			die("Error: The version option must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).\n");
+		}
 
 		// try migrate up
-		$migrations=$this->getNewMigrations();
-		foreach($migrations as $i=>$migration)
-		{
-			if(strpos($migration,$version.'_')===0)
-			{
-				$this->actionUp(array($i+1));
+		$migrations = $this->getNewMigrations();
+		foreach ($migrations as $i => $migration) {
+			if (strpos($migration, $version . '_') === 0) {
+				$this->actionUp(array($i + 1));
 				return;
 			}
 		}
 
 		// try migrate down
-		$migrations=array_keys($this->getMigrationHistory(-1));
-		foreach($migrations as $i=>$migration)
-		{
-			if(strpos($migration,$version.'_')===0)
-			{
-				if($i===0)
+		$migrations = array_keys($this->getMigrationHistory(-1));
+		foreach ($migrations as $i => $migration) {
+			if (strpos($migration, $version . '_') === 0) {
+				if ($i === 0) {
 					echo "Already at '$originalVersion'. Nothing needs to be done.\n";
-				else
+				} else {
 					$this->actionDown(array($i));
+				}
 				return;
 			}
 		}
@@ -268,32 +263,30 @@ class MigrateController extends Controller
 
 	public function actionMark($args)
 	{
-		if(isset($args[0]))
-			$version=$args[0];
-		else
+		if (isset($args[0])) {
+			$version = $args[0];
+		} else {
 			$this->usageError('Please specify which version to mark to.');
-		$originalVersion=$version;
-		if(preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/',$version,$matches))
-			$version='m'.$matches[1];
-		else
+		}
+		$originalVersion = $version;
+		if (preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/', $version, $matches)) {
+			$version = 'm' . $matches[1];
+		} else {
 			die("Error: The version option must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).\n");
+		}
 
-		$db=$this->getDb();
+		$db = $this->getDb();
 
 		// try mark up
-		$migrations=$this->getNewMigrations();
-		foreach($migrations as $i=>$migration)
-		{
-			if(strpos($migration,$version.'_')===0)
-			{
-				if($this->confirm("Set migration history at $originalVersion?"))
-				{
-					$command=$db->createCommand();
-					for($j=0;$j<=$i;++$j)
-					{
+		$migrations = $this->getNewMigrations();
+		foreach ($migrations as $i => $migration) {
+			if (strpos($migration, $version . '_') === 0) {
+				if ($this->confirm("Set migration history at $originalVersion?")) {
+					$command = $db->createCommand();
+					for ($j = 0; $j <= $i; ++$j) {
 						$command->insert($this->migrationTable, array(
-							'version'=>$migrations[$j],
-							'apply_time'=>time(),
+							'version' => $migrations[$j],
+							'apply_time' => time(),
 						));
 					}
 					echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n";
@@ -303,20 +296,17 @@ class MigrateController extends Controller
 		}
 
 		// try mark down
-		$migrations=array_keys($this->getMigrationHistory(-1));
-		foreach($migrations as $i=>$migration)
-		{
-			if(strpos($migration,$version.'_')===0)
-			{
-				if($i===0)
+		$migrations = array_keys($this->getMigrationHistory(-1));
+		foreach ($migrations as $i => $migration) {
+			if (strpos($migration, $version . '_') === 0) {
+				if ($i === 0) {
 					echo "Already at '$originalVersion'. Nothing needs to be done.\n";
-				else
-				{
-					if($this->confirm("Set migration history at $originalVersion?"))
-					{
-						$command=$db->createCommand();
-						for($j=0;$j<$i;++$j)
-							$command->delete($this->migrationTable, $db->quoteColumnName('version').'=:version', array(':version'=>$migrations[$j]));
+				} else {
+					if ($this->confirm("Set migration history at $originalVersion?")) {
+						$command = $db->createCommand();
+						for ($j = 0; $j < $i; ++$j) {
+							$command->delete($this->migrationTable, $db->quoteColumnName('version') . '=:version', array(':version' => $migrations[$j]));
+						}
 						echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n";
 					}
 				}
@@ -329,125 +319,115 @@ class MigrateController extends Controller
 
 	public function actionHistory($args)
 	{
-		$limit=isset($args[0]) ? (int)$args[0] : -1;
-		$migrations=$this->getMigrationHistory($limit);
-		if($migrations===array())
+		$limit = isset($args[0]) ? (int)$args[0] : -1;
+		$migrations = $this->getMigrationHistory($limit);
+		if ($migrations === array()) {
 			echo "No migration has been done before.\n";
-		else
-		{
-			$n=count($migrations);
-			if($limit>0)
-				echo "Showing the last $n applied ".($n===1 ? 'migration' : 'migrations').":\n";
-			else
-				echo "Total $n ".($n===1 ? 'migration has' : 'migrations have')." been applied before:\n";
-			foreach($migrations as $version=>$time)
-				echo "    (".date('Y-m-d H:i:s',$time).') '.$version."\n";
+		} else {
+			$n = count($migrations);
+			if ($limit > 0) {
+				echo "Showing the last $n applied " . ($n === 1 ? 'migration' : 'migrations') . ":\n";
+			} else {
+				echo "Total $n " . ($n === 1 ? 'migration has' : 'migrations have') . " been applied before:\n";
+			}
+			foreach ($migrations as $version => $time) {
+				echo "    (" . date('Y-m-d H:i:s', $time) . ') ' . $version . "\n";
+			}
 		}
 	}
 
 	public function actionNew($args)
 	{
-		$limit=isset($args[0]) ? (int)$args[0] : -1;
-		$migrations=$this->getNewMigrations();
-		if($migrations===array())
+		$limit = isset($args[0]) ? (int)$args[0] : -1;
+		$migrations = $this->getNewMigrations();
+		if ($migrations === array()) {
 			echo "No new migrations found. Your system is up-to-date.\n";
-		else
-		{
-			$n=count($migrations);
-			if($limit>0 && $n>$limit)
-			{
-				$migrations=array_slice($migrations,0,$limit);
-				echo "Showing $limit out of $n new ".($n===1 ? 'migration' : 'migrations').":\n";
+		} else {
+			$n = count($migrations);
+			if ($limit > 0 && $n > $limit) {
+				$migrations = array_slice($migrations, 0, $limit);
+				echo "Showing $limit out of $n new " . ($n === 1 ? 'migration' : 'migrations') . ":\n";
+			} else {
+				echo "Found $n new " . ($n === 1 ? 'migration' : 'migrations') . ":\n";
 			}
-			else
-				echo "Found $n new ".($n===1 ? 'migration' : 'migrations').":\n";
 
-			foreach($migrations as $migration)
-				echo "    ".$migration."\n";
+			foreach ($migrations as $migration) {
+				echo "    " . $migration . "\n";
+			}
 		}
 	}
 
 	public function actionCreate($args)
 	{
-		if(isset($args[0]))
-			$name=$args[0];
-		else
+		if (isset($args[0])) {
+			$name = $args[0];
+		} else {
 			$this->usageError('Please provide the name of the new migration.');
+		}
 
-		if(!preg_match('/^\w+$/',$name))
+		if (!preg_match('/^\w+$/', $name)) {
 			die("Error: The name of the migration must contain letters, digits and/or underscore characters only.\n");
+		}
 
-		$name='m'.gmdate('ymd_His').'_'.$name;
-		$content=strtr($this->getTemplate(), array('{ClassName}'=>$name));
-		$file=$this->migrationPath.DIRECTORY_SEPARATOR.$name.'.php';
+		$name = 'm' . gmdate('ymd_His') . '_' . $name;
+		$content = strtr($this->getTemplate(), array('{ClassName}' => $name));
+		$file = $this->migrationPath . DIRECTORY_SEPARATOR . $name . '.php';
 
-		if($this->confirm("Create new migration '$file'?"))
-		{
+		if ($this->confirm("Create new migration '$file'?")) {
 			file_put_contents($file, $content);
 			echo "New migration created successfully.\n";
 		}
 	}
 
-	public function confirm($message)
-	{
-		if(!$this->interactive)
-			return true;
-		return parent::confirm($message);
-	}
-
 	protected function migrateUp($class)
 	{
-		if($class===self::BASE_MIGRATION)
+		if ($class === self::BASE_MIGRATION) {
 			return;
+		}
 
 		echo "*** applying $class\n";
-		$start=microtime(true);
-		$migration=$this->instantiateMigration($class);
-		if($migration->up()!==false)
-		{
+		$start = microtime(true);
+		$migration = $this->instantiateMigration($class);
+		if ($migration->up() !== false) {
 			$this->getDb()->createCommand()->insert($this->migrationTable, array(
-				'version'=>$class,
-				'apply_time'=>time(),
+				'version' => $class,
+				'apply_time' => time(),
 			));
-			$time=microtime(true)-$start;
-			echo "*** applied $class (time: ".sprintf("%.3f",$time)."s)\n\n";
-		}
-		else
-		{
-			$time=microtime(true)-$start;
-			echo "*** failed to apply $class (time: ".sprintf("%.3f",$time)."s)\n\n";
+			$time = microtime(true) - $start;
+			echo "*** applied $class (time: " . sprintf("%.3f", $time) . "s)\n\n";
+		} else {
+			$time = microtime(true) - $start;
+			echo "*** failed to apply $class (time: " . sprintf("%.3f", $time) . "s)\n\n";
 			return false;
 		}
 	}
 
 	protected function migrateDown($class)
 	{
-		if($class===self::BASE_MIGRATION)
+		if ($class === self::BASE_MIGRATION) {
 			return;
+		}
 
 		echo "*** reverting $class\n";
-		$start=microtime(true);
-		$migration=$this->instantiateMigration($class);
-		if($migration->down()!==false)
-		{
-			$db=$this->getDb();
-			$db->createCommand()->delete($this->migrationTable, $db->quoteColumnName('version').'=:version', array(':version'=>$class));
-			$time=microtime(true)-$start;
-			echo "*** reverted $class (time: ".sprintf("%.3f",$time)."s)\n\n";
-		}
-		else
-		{
-			$time=microtime(true)-$start;
-			echo "*** failed to revert $class (time: ".sprintf("%.3f",$time)."s)\n\n";
+		$start = microtime(true);
+		$migration = $this->instantiateMigration($class);
+		if ($migration->down() !== false) {
+			$db = $this->getDb();
+			$db->createCommand()->delete($this->migrationTable, $db->quoteColumnName('version') . '=:version', array(':version' => $class));
+			$time = microtime(true) - $start;
+			echo "*** reverted $class (time: " . sprintf("%.3f", $time) . "s)\n\n";
+		} else {
+			$time = microtime(true) - $start;
+			echo "*** failed to revert $class (time: " . sprintf("%.3f", $time) . "s)\n\n";
 			return false;
 		}
 	}
 
 	protected function instantiateMigration($class)
 	{
-		$file=$this->migrationPath.DIRECTORY_SEPARATOR.$class.'.php';
+		$file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php';
 		require_once($file);
-		$migration=new $class;
+		$migration = new $class;
 		$migration->setDb($this->getDb());
 		return $migration;
 	}
@@ -456,21 +436,24 @@ class MigrateController extends Controller
 	 * @var CDbConnection
 	 */
 	private $_db;
+
 	protected function getDb()
 	{
-		if($this->_db!==null)
-			return $this->_db;
-		else if(($this->_db=\Yii::$application->getComponent($this->connectionID)) instanceof CDbConnection)
+		if ($this->_db !== null) {
 			return $this->_db;
-		else
-			die("Error: CMigrationCommand.connectionID '{$this->connectionID}' is invalid. Please make sure it refers to the ID of a CDbConnection application component.\n");
+		} else {
+			if (($this->_db = Yii::$application->getComponent($this->connectionID)) instanceof CDbConnection) {
+				return $this->_db;
+			} else {
+				die("Error: CMigrationCommand.connectionID '{$this->connectionID}' is invalid. Please make sure it refers to the ID of a CDbConnection application component.\n");
+			}
+		}
 	}
 
 	protected function getMigrationHistory($limit)
 	{
-		$db=$this->getDb();
-		if($db->schema->getTable($this->migrationTable)===null)
-		{
+		$db = $this->getDb();
+		if ($db->schema->getTable($this->migrationTable) === null) {
 			$this->createMigrationHistoryTable();
 		}
 		return CHtml::listData($db->createCommand()
@@ -483,34 +466,36 @@ class MigrateController extends Controller
 
 	protected function createMigrationHistoryTable()
 	{
-		$db=$this->getDb();
-		echo 'Creating migration history table "'.$this->migrationTable.'"...';
-		$db->createCommand()->createTable($this->migrationTable,array(
-			'version'=>'string NOT NULL PRIMARY KEY',
-			'apply_time'=>'integer',
+		$db = $this->getDb();
+		echo 'Creating migration history table "' . $this->migrationTable . '"...';
+		$db->createCommand()->createTable($this->migrationTable, array(
+			'version' => 'string NOT NULL PRIMARY KEY',
+			'apply_time' => 'integer',
 		));
-		$db->createCommand()->insert($this->migrationTable,array(
-			'version'=>self::BASE_MIGRATION,
-			'apply_time'=>time(),
+		$db->createCommand()->insert($this->migrationTable, array(
+			'version' => self::BASE_MIGRATION,
+			'apply_time' => time(),
 		));
 		echo "done.\n";
 	}
 
 	protected function getNewMigrations()
 	{
-		$applied=array();
-		foreach($this->getMigrationHistory(-1) as $version=>$time)
-			$applied[substr($version,1,13)]=true;
-
-		$migrations=array();
-		$handle=opendir($this->migrationPath);
-		while(($file=readdir($handle))!==false)
-		{
-			if($file==='.' || $file==='..')
+		$applied = array();
+		foreach ($this->getMigrationHistory(-1) as $version => $time) {
+			$applied[substr($version, 1, 13)] = true;
+		}
+
+		$migrations = array();
+		$handle = opendir($this->migrationPath);
+		while (($file = readdir($handle)) !== false) {
+			if ($file === '.' || $file === '..') {
 				continue;
-			$path=$this->migrationPath.DIRECTORY_SEPARATOR.$file;
-			if(preg_match('/^(m(\d{6}_\d{6})_.*?)\.php$/',$file,$matches) && is_file($path) && !isset($applied[$matches[2]]))
-				$migrations[]=$matches[1];
+			}
+			$path = $this->migrationPath . DIRECTORY_SEPARATOR . $file;
+			if (preg_match('/^(m(\d{6}_\d{6})_.*?)\.php$/', $file, $matches) && is_file($path) && !isset($applied[$matches[2]])) {
+				$migrations[] = $matches[1];
+			}
 		}
 		closedir($handle);
 		sort($migrations);
@@ -519,9 +504,9 @@ class MigrateController extends Controller
 
 	protected function getTemplate()
 	{
-		if($this->templateFile!==null)
-			return file_get_contents(Yii::getPathOfAlias($this->templateFile).'.php');
-		else
+		if ($this->templateFile !== null) {
+			return file_get_contents(Yii::getPathOfAlias($this->templateFile) . '.php');
+		} else {
 			return <<<EOD
 <?php
 
@@ -549,5 +534,6 @@ class {ClassName} extends CDbMigration
 	*/
 }
 EOD;
+		}
 	}
 }
diff --git a/framework/console/controllers/ShellController.php b/framework/console/controllers/ShellController.php
deleted file mode 100644
index 4fbb2bf..0000000
--- a/framework/console/controllers/ShellController.php
+++ /dev/null
@@ -1,149 +0,0 @@
-<?php
-/**
- * ShellController class file.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
-
-namespace yii\console\controllers;
-
-use yii\console\Controller;
-
-/**
- * This command executes the specified Web application and provides a shell for interaction.
- *
- * @property string $help The help information for the shell command.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @since 2.0
- */
-class ShellController extends Controller
-{
-	/**
-	 * @return string the help information for the shell command
-	 */
-	public function getHelp()
-	{
-		return <<<EOD
-USAGE
-  yiic shell [entry-script | config-file]
-
-DESCRIPTION
-  This command allows you to interact with a Web application
-  on the command line. It also provides tools to automatically
-  generate new controllers, views and data models.
-
-  It is recommended that you execute this command under
-  the directory that contains the entry script file of
-  the Web application.
-
-PARAMETERS
- * entry-script | config-file: optional, the path to
-   the entry script file or the configuration file for
-   the Web application. If not given, it is assumed to be
-   the 'index.php' file under the current directory.
-
-EOD;
-	}
-
-	/**
-	 * Execute the action.
-	 * @param array $args command line parameters specific for this command
-	 */
-	public function run($args)
-	{
-		if(!isset($args[0]))
-			$args[0]='index.php';
-		$entryScript=isset($args[0]) ? $args[0] : 'index.php';
-		if(($entryScript=realpath($args[0]))===false || !is_file($entryScript))
-			$this->usageError("{$args[0]} does not exist or is not an entry script file.");
-
-		// fake the web server setting
-		$cwd=getcwd();
-		chdir(dirname($entryScript));
-		$_SERVER['SCRIPT_NAME']='/'.basename($entryScript);
-		$_SERVER['REQUEST_URI']=$_SERVER['SCRIPT_NAME'];
-		$_SERVER['SCRIPT_FILENAME']=$entryScript;
-		$_SERVER['HTTP_HOST']='localhost';
-		$_SERVER['SERVER_NAME']='localhost';
-		$_SERVER['SERVER_PORT']=80;
-
-		// reset context to run the web application
-		restore_error_handler();
-		restore_exception_handler();
-		Yii::setApplication(null);
-		Yii::setPathOfAlias('application',null);
-
-		ob_start();
-		$config=require($entryScript);
-		ob_end_clean();
-
-		// oops, the entry script turns out to be a config file
-		if(is_array($config))
-		{
-			chdir($cwd);
-			$_SERVER['SCRIPT_NAME']='/index.php';
-			$_SERVER['REQUEST_URI']=$_SERVER['SCRIPT_NAME'];
-			$_SERVER['SCRIPT_FILENAME']=$cwd.DIRECTORY_SEPARATOR.'index.php';
-			Yii::createWebApplication($config);
-		}
-
-		restore_error_handler();
-		restore_exception_handler();
-
-		$yiiVersion=Yii::getVersion();
-		echo <<<EOD
-Yii Interactive Tool v1.1 (based on Yii v{$yiiVersion})
-Please type 'help' for help. Type 'exit' to quit.
-EOD;
-		$this->runShell();
-	}
-
-	protected function runShell()
-	{
-		// disable E_NOTICE so that the shell is more friendly
-		error_reporting(E_ALL ^ E_NOTICE);
-
-		$_runner_=new CConsoleCommandRunner;
-		$_runner_->addCommands(dirname(__FILE__).'/shell');
-		$_runner_->addCommands(Yii::getPathOfAlias('application.commands.shell'));
-		if(($_path_=@getenv('YIIC_SHELL_COMMAND_PATH'))!==false)
-			$_runner_->addCommands($_path_);
-		$_commands_=$_runner_->commands;
-		$log=\Yii::$application->log;
-
-		while(($_line_=$this->prompt("\n>>"))!==false)
-		{
-			$_line_=trim($_line_);
-			if($_line_==='exit')
-				return;
-			try
-			{
-				$_args_=preg_split('/[\s,]+/',rtrim($_line_,';'),-1,PREG_SPLIT_NO_EMPTY);
-				if(isset($_args_[0]) && isset($_commands_[$_args_[0]]))
-				{
-					$_command_=$_runner_->createCommand($_args_[0]);
-					array_shift($_args_);
-					$_command_->init();
-					$_command_->run($_args_);
-				}
-				else
-					echo eval($_line_.';');
-			}
-			catch(Exception $e)
-			{
-				if($e instanceof ShellException)
-					echo $e->getMessage();
-				else
-					echo $e;
-			}
-		}
-	}
-}
-
-class ShellException extends CException
-{
-}
\ No newline at end of file
diff --git a/framework/db/Command.php b/framework/db/Command.php
index 0fb099a..3531fa7 100644
--- a/framework/db/Command.php
+++ b/framework/db/Command.php
@@ -485,16 +485,41 @@ class Command extends \yii\base\Component
 	 *
 	 * @param string $table the table that new rows will be inserted into.
 	 * @param array $columns the column data (name=>value) to be inserted into the table.
-	 * @param array $params the parameters to be bound to the command
 	 * @return Command the command object itself
 	 */
-	public function insert($table, $columns, $params = array())
+	public function insert($table, $columns)
 	{
+		$params = array();
 		$sql = $this->db->getQueryBuilder()->insert($table, $columns, $params);
 		return $this->setSql($sql)->bindValues($params);
 	}
 
 	/**
+	 * Creates a batch INSERT command.
+	 * For example,
+	 *
+	 * ~~~
+	 * $connection->createCommand()->batchInsert('tbl_user', array('name', 'age'), array(
+	 *     array('Tom', 30),
+	 *     array('Jane', 20),
+	 *     array('Linda', 25),
+	 * ))->execute();
+	 * ~~~
+	 *
+	 * Not that the values in each row must match the corresponding column names.
+	 *
+	 * @param string $table the table that new rows will be inserted into.
+	 * @param array $columns the column names
+	 * @param array $rows the rows to be batch inserted into the table
+	 * @return Command the command object itself
+	 */
+	public function batchInsert($table, $columns, $rows)
+	{
+		$sql = $this->db->getQueryBuilder()->batchInsert($table, $columns, $rows);
+		return $this->setSql($sql);
+	}
+
+	/**
 	 * Creates an UPDATE command.
 	 * For example,
 	 *
diff --git a/framework/db/Exception.php b/framework/db/Exception.php
index bdc1277..209dc40 100644
--- a/framework/db/Exception.php
+++ b/framework/db/Exception.php
@@ -34,4 +34,12 @@ class Exception extends \yii\base\Exception
 		$this->errorInfo = $errorInfo;
 		parent::__construct($message, $code);
 	}
+
+	/**
+	 * @return string the user-friendly name of this exception
+	 */
+	public function getName()
+	{
+		return \Yii::t('yii', 'Database Exception');
+	}
 }
\ No newline at end of file
diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php
index d40da91..35bfcb3 100644
--- a/framework/db/QueryBuilder.php
+++ b/framework/db/QueryBuilder.php
@@ -115,6 +115,33 @@ class QueryBuilder extends \yii\base\Object
 	}
 
 	/**
+	 * Generates a batch INSERT SQL statement.
+	 * For example,
+	 *
+	 * ~~~
+	 * $connection->createCommand()->batchInsert('tbl_user', array('name', 'age'), array(
+	 *     array('Tom', 30),
+	 *     array('Jane', 20),
+	 *     array('Linda', 25),
+	 * ))->execute();
+	 * ~~~
+	 *
+	 * Not that the values in each row must match the corresponding column names.
+	 *
+	 * @param string $table the table that new rows will be inserted into.
+	 * @param array $columns the column names
+	 * @param array $rows the rows to be batch inserted into the table
+	 * @param array $params the parameters to be bound to the command
+	 * @return string the batch INSERT SQL statement
+	 * @throws NotSupportedException if this is not supported by the underlying DBMS
+	 */
+	public function batchInsert($table, $columns, $rows, $params = array())
+	{
+		throw new NotSupportedException($this->db->getDriverName() . ' does not support batch insert.');
+
+	}
+
+	/**
 	 * Creates an UPDATE SQL statement.
 	 * For example,
 	 *
diff --git a/framework/db/mysql/QueryBuilder.php b/framework/db/mysql/QueryBuilder.php
index 73986af..6168409 100644
--- a/framework/db/mysql/QueryBuilder.php
+++ b/framework/db/mysql/QueryBuilder.php
@@ -129,4 +129,39 @@ class QueryBuilder extends \yii\db\QueryBuilder
 	{
 		return 'SET FOREIGN_KEY_CHECKS=' . ($check ? 1 : 0);
 	}
+
+	/**
+	 * Generates a batch INSERT SQL statement.
+	 * For example,
+	 *
+	 * ~~~
+	 * $connection->createCommand()->batchInsert('tbl_user', array('name', 'age'), array(
+	 *     array('Tom', 30),
+	 *     array('Jane', 20),
+	 *     array('Linda', 25),
+	 * ))->execute();
+	 * ~~~
+	 *
+	 * Not that the values in each row must match the corresponding column names.
+	 *
+	 * @param string $table the table that new rows will be inserted into.
+	 * @param array $columns the column names
+	 * @param array $rows the rows to be batch inserted into the table
+	 * @return string the batch INSERT SQL statement
+	 */
+	public function batchInsert($table, $columns, $rows)
+	{
+		$values = array();
+		foreach ($rows as $row) {
+			$vs = array();
+			foreach ($row as $value) {
+				$vs[] = is_string($value) ? $this->db->quoteValue($value) : $value;
+			}
+			$values[] = $vs;
+		}
+
+		return 'INSERT INTO ' . $this->db->quoteTableName($table)
+			. ' (' . implode(', ', $columns) . ') VALUES ('
+			. implode(', ', $values) . ')';
+	}
 }
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 <qiang.xue@gmail.com>
  * @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..c65e4f0 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,11 +46,25 @@ 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);
 		}
 	}
 
 	/**
+	 * Normalizes a file/directory path.
+	 * After normalization, the directory separators in the path will be `DIRECTORY_SEPARATOR`,
+	 * and any trailing directory separators will be removed. For example, '/home\demo/' on Linux
+	 * will be normalized as '/home/demo'.
+	 * @param string $path the file/directory path to be normalized
+	 * @param string $ds the directory separator to be used in the normalized result. Defaults to `DIRECTORY_SEPARATOR`.
+	 * @return string the normalized file/directory path
+	 */
+	public static function normalizePath($path, $ds = DIRECTORY_SEPARATOR)
+	{
+		return rtrim(strtr($path, array('/' => $ds, '\\' => $ds)), $ds);
+	}
+
+	/**
 	 * Returns the localized version of a specified file.
 	 *
 	 * The searching is based on the specified language code. In particular,
diff --git a/framework/util/ReflectionHelper.php b/framework/util/ReflectionHelper.php
deleted file mode 100644
index cc13f94..0000000
--- a/framework/util/ReflectionHelper.php
+++ /dev/null
@@ -1,103 +0,0 @@
-<?php
-/**
- * ReflectionHelper class file.
- *
- * @link http://www.yiiframework.com/
- * @copyright Copyright &copy; 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
-
-namespace yii\util;
-
-use yii\base\Exception;
-
-/**
- * ReflectionHelper
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @since 2.0
- */
-class ReflectionHelper
-{
-	/**
-	 * Prepares parameters so that they can be bound to the specified method.
-	 * This method converts the input parameters into an array that can later be
-	 * passed to `call_user_func_array()` when calling the specified method.
-	 * The conversion is based on the matching of method parameter names
-	 * and the input array keys. For example,
-	 *
-	 * ~~~
-	 * class Foo {
-	 *     function bar($a, $b) { ... }
-	 * }
-	 * $object = new Foo;
-	 * $params = array('b' => 2, 'c' => 3, 'a' => 1);
-	 * var_export(ReflectionHelper::extractMethodParams($object, 'bar', $params));
-	 * // output: array('a' => 1, 'b' => 2);
-	 * ~~~
-	 *
-	 * @param object|string $object the object or class name that owns the specified method
-	 * @param string $method the method name
-	 * @param array $params the parameters in terms of name-value pairs
-	 * @return array parameters that are needed by the method only and
-	 * can be passed to the method via `call_user_func_array()`.
-	 * @throws Exception if any required method parameter is not found in the given parameters
-	 */
-	public static function extractMethodParams($object, $method, $params)
-	{
-		$m = new \ReflectionMethod($object, $method);
-		$ps = array();
-		foreach ($m->getParameters() as $param) {
-			$name = $param->getName();
-			if (array_key_exists($name, $params)) {
-				$ps[$name] = $params[$name];
-			} elseif ($param->isDefaultValueAvailable()) {
-				$ps[$name] = $param->getDefaultValue();
-			} else {
-				throw new Exception(\Yii::t('yii', 'Missing required parameter "{name}".', array('{name}' => $name)));
-			}
-		}
-		return $ps;
-	}
-
-	/**
-	 * Initializes an object with the given parameters.
-	 * Only the public non-static properties of the object will be initialized, and their names must
-	 * match the given parameter names. For example,
-	 *
-	 * ~~~
-	 * class Foo {
-	 *     public $a;
-	 *     protected $b;
-	 * }
-	 * $object = new Foo;
-	 * $params = array('b' => 2, 'c' => 3, 'a' => 1);
-	 * $remaining = ReflectionHelper::bindObjectParams($object, $params);
-	 * var_export($object);    // output: $object->a = 1; $object->b = null;
-	 * var_export($remaining); // output: array('b' => 2, 'c' => 3);
-	 * ~~~
-	 *
-	 * @param object $object the object whose properties are to be initialized
-	 * @param array $params the input parameters to be used to initialize the object
-	 * @return array the remaining unused input parameters
-	 */
-	public static function initObjectWithParams($object, $params)
-	{
-		if (empty($params)) {
-			return array();
-		}
-
-		$class = new \ReflectionClass(get_class($object));
-		foreach ($params as $name => $value) {
-			if ($class->hasProperty($name)) {
-				$property = $class->getProperty($name);
-				if ($property->isPublic() && !$property->isStatic()) {
-					$object->$name = $value;
-					unset($params[$name]);
-				}
-			}
-		}
-
-		return $params;
-	}
-}
diff --git a/framework/yiic.php b/framework/yiic.php
index 0f05183..55b9e60 100644
--- a/framework/yiic.php
+++ b/framework/yiic.php
@@ -1,5 +1,4 @@
 <?php
-define('YII_DEBUG', true);
 /**
  * Yii console bootstrap file.
  *
@@ -8,16 +7,17 @@ define('YII_DEBUG', true);
  * @license http://www.yiiframework.com/license/
  */
 
+defined('YII_DEBUG') or define('YII_DEBUG', true);
+
 // fcgi doesn't have STDIN defined by default
 defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
 
 require(__DIR__ . '/yii.php');
 
-$config = array(
-	'controllerPath' => '@yii/console/controllers',
-);
 $id = 'yiic';
 $basePath = __DIR__ . '/console';
 
-$application = new yii\console\Application($id, $basePath, $config);
+$application = new yii\console\Application($id, $basePath, array(
+	'controllerPath' => '@yii/console/controllers',
+));
 $application->run();
diff --git a/tests/unit/bootstrap.php b/tests/unit/bootstrap.php
index 4dc7f33..f60eee0 100644
--- a/tests/unit/bootstrap.php
+++ b/tests/unit/bootstrap.php
@@ -1,6 +1,5 @@
 <?php
 
-define('YII_ENABLE_EXCEPTION_HANDLER', false);
 define('YII_ENABLE_ERROR_HANDLER', false);
 define('YII_DEBUG', true);
 $_SERVER['SCRIPT_NAME'] = '/' . __DIR__;
diff --git a/todo.md b/todo.md
index 7e5f256..7517b07 100644
--- a/todo.md
+++ b/todo.md
@@ -1,6 +1,6 @@
 - db
 	* pgsql, sql server, oracle, db2 drivers
-	  * write a guide on creating own schema definitions
+	* unit tests on different DB drivers
 	* document-based (should allow storage-specific methods additionally to generic ones)
 	  * mongodb (put it under framework/db/mongodb)
 	* key-value-based (should allow storage-specific methods additionally to generic ones)
@@ -8,8 +8,10 @@
 - logging
 	* WebTarget (TBD after web is in place): should consider using javascript and make it into a toolbar
 	* ProfileTarget (TBD after web is in place): should consider using javascript and make it into a toolbar
+	* unit tests
 - caching
 	* a console command to clear cached data
+	* unit tests
 - validators
 	* FileValidator: depends on CUploadedFile
 	* CaptchaValidator: depends on CaptchaAction