From ac74fede28ff06bfcd64d679243c982d13484ba1 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 28 Mar 2014 16:59:21 -0400 Subject: [PATCH] Fixes #2910: Added `Application::end()` --- framework/CHANGELOG.md | 1 + framework/base/Application.php | 89 ++++++++++++++++++++++++++++++- framework/base/ExitException.php | 46 ++++++++++++++++ framework/console/Application.php | 9 ---- framework/web/Application.php | 18 ------- 5 files changed, 135 insertions(+), 28 deletions(-) create mode 100644 framework/base/ExitException.php diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 2416f2dc25..ff9cfdb204 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -157,6 +157,7 @@ Yii Framework 2 Change Log - Enh #2756: Added support for injecting custom `isEmpty` check for all validators (qiangxue) - Enh #2775: Added `yii\base\Application::bootstrap` and `yii\base\BootstrapInterface` to support running bootstrap classes when starting an application (qiangxue) - Enh #2892: ActiveRecord dirty attributes are now reset after call to `afterSave()` so information about changed attributes is available in `afterSave`-event (cebe) +- Enh #2910: Added `Application::end()` (qiangxue) - Enh: Added support for using arrays as option values for console commands (qiangxue) - Enh: Added `favicon.ico` and `robots.txt` to default application templates (samdark) - Enh: Added `Widget::autoIdPrefix` to support prefixing automatically generated widget IDs (qiangxue) diff --git a/framework/base/Application.php b/framework/base/Application.php index cc5578f54d..f23b20a2aa 100644 --- a/framework/base/Application.php +++ b/framework/base/Application.php @@ -59,12 +59,40 @@ abstract class Application extends Module */ const EVENT_AFTER_ACTION = 'afterAction'; + /** + * Application state used by [[state]]: application just started. + */ + const STATE_BEGIN = 0; + /** + * Application state used by [[state]]: application is initializing. + */ + const STATE_INIT = 1; + /** + * Application state used by [[state]]: application is triggering [[EVENT_BEFORE_REQUEST]]. + */ + const STATE_BEFORE_REQUEST = 2; + /** + * Application state used by [[state]]: application is handling the request. + */ + const STATE_HANDLING_REQUEST = 3; + /** + * Application state used by [[state]]: application is triggering [[EVENT_AFTER_REQUEST]].. + */ + const STATE_AFTER_REQUEST = 4; + /** + * Application state used by [[state]]: application is about to send response. + */ + const STATE_SENDING_RESPONSE = 5; + /** + * Application state used by [[state]]: application has ended. + */ + const STATE_END = 6; + /** * @var string the namespace that controller classes are in. If not set, * it will use the "app\controllers" namespace. */ public $controllerNamespace = 'app\\controllers'; - /** * @var string the application name. */ @@ -144,6 +172,11 @@ abstract class Application extends Module * it means the application is handling some exception and extra care should be taken. */ public $exception; + /** + * @var integer the current application state during a request handling life cycle. + * This property is managed by the application. Do not modify this property. + */ + public $_state; /** * @var string Used to reserve memory for fatal error handler. @@ -160,10 +193,14 @@ abstract class Application extends Module { Yii::$app = $this; + $this->state = self::STATE_BEGIN; + $this->preInit($config); $this->registerErrorHandlers(); Component::__construct($config); + + $this->state = self::STATE_INIT; } /** @@ -309,11 +346,20 @@ abstract class Application extends Module */ public function run() { + $this->state = self::STATE_BEFORE_REQUEST; $this->trigger(self::EVENT_BEFORE_REQUEST); + + $this->state = self::STATE_HANDLING_REQUEST; $response = $this->handleRequest($this->getRequest()); + + $this->state = self::STATE_AFTER_REQUEST; $this->trigger(self::EVENT_AFTER_REQUEST); + + $this->state = self::STATE_SENDING_RESPONSE; $response->send(); + $this->state = self::STATE_END; + return $response->exitStatus; } @@ -459,6 +505,15 @@ abstract class Application extends Module return $this->get('request'); } + /** + * Returns the response component. + * @return \yii\web\Response|\yii\console\Response the response component + */ + public function getResponse() + { + return $this->get('response'); + } + /** * Returns the view object. * @return View|\yii\web\View the view object that is used to render various view files. @@ -532,6 +587,34 @@ abstract class Application extends Module ]; } + /** + * Terminates the application. + * This method replaces the `exit()` function by ensuring the application life cycle is completed + * before terminating the application. + * @param integer $status the exit status (value 0 means normal exit while other values mean abnormal exit). + * @param Response $response the response to be sent. If not set, the default application [[response]] component will be used. + * @throws ExitException if the application is in testing mode + */ + public function end($status = 0, $response = null) + { + if ($this->state === self::STATE_BEFORE_REQUEST || $this->state === self::STATE_HANDLING_REQUEST) { + $this->state = self::STATE_AFTER_REQUEST; + $this->trigger(self::EVENT_AFTER_REQUEST); + } + + if ($this->state !== self::STATE_SENDING_RESPONSE && $this->state !== self::STATE_END) { + $this->state = self::STATE_END; + $response = $response ? : $this->getResponse(); + $response->send(); + } + + if (YII_ENV_TEST) { + throw new ExitException($status); + } else { + exit($status); + } + } + /** * Handles uncaught PHP exceptions. * @@ -541,6 +624,10 @@ abstract class Application extends Module */ public function handleException($exception) { + if ($exception instanceof ExitException) { + return; + } + $this->exception = $exception; // disable error capturing to avoid recursive errors while handling exceptions diff --git a/framework/base/ExitException.php b/framework/base/ExitException.php new file mode 100644 index 0000000000..32bdebef31 --- /dev/null +++ b/framework/base/ExitException.php @@ -0,0 +1,46 @@ + + * @since 2.0 + */ +class ExitException extends Exception +{ + /** + * @var integer the exit status code + */ + public $statusCode; + + + /** + * Constructor. + * @param integer $status the exit status code + * @param string $message error message + * @param integer $code error code + * @param \Exception $previous The previous exception used for the exception chaining. + */ + public function __construct($status = 0, $message = null, $code = 0, \Exception $previous = null) + { + $this->statusCode = $status; + parent::__construct($message, $code, $previous); + } + + /** + * @return string the user-friendly name of this exception + */ + public function getName() + { + return 'Application Ended'; + } +} diff --git a/framework/console/Application.php b/framework/console/Application.php index d6f3e8c62b..7d14fac710 100644 --- a/framework/console/Application.php +++ b/framework/console/Application.php @@ -148,15 +148,6 @@ class Application extends \yii\base\Application } } - /** - * Returns the response component. - * @return Response the response component - */ - public function getResponse() - { - return $this->get('response'); - } - /** * Runs a controller action specified by a route. * This method parses the specified route and creates the corresponding child module(s), controller and action diff --git a/framework/web/Application.php b/framework/web/Application.php index d5e70d2528..e09bd98f0d 100644 --- a/framework/web/Application.php +++ b/framework/web/Application.php @@ -123,24 +123,6 @@ class Application extends \yii\base\Application $this->_homeUrl = $value; } - /** - * Returns the request component. - * @return Request the request component - */ - public function getRequest() - { - return $this->get('request'); - } - - /** - * Returns the response component. - * @return Response the response component - */ - public function getResponse() - { - return $this->get('response'); - } - /** * Returns the session component. * @return Session the session component