diff options
-rw-r--r-- | .gitattributes | 3 | ||||
-rw-r--r-- | demos/controls/protected/application.xml | 2 | ||||
-rw-r--r-- | framework/Exceptions/TErrorHandler.php | 102 | ||||
-rw-r--r-- | framework/Exceptions/error503-en.html | 27 | ||||
-rw-r--r-- | framework/Exceptions/error503-zh.html | 27 | ||||
-rw-r--r-- | framework/Exceptions/error503.html | 27 | ||||
-rw-r--r-- | framework/Exceptions/messages.txt | 3 | ||||
-rw-r--r-- | framework/TApplication.php | 41 | ||||
-rw-r--r-- | framework/Web/TAssetManager.php | 4 |
9 files changed, 226 insertions, 10 deletions
diff --git a/.gitattributes b/.gitattributes index d8001fcc..b1ece447 100644 --- a/.gitattributes +++ b/.gitattributes @@ -46,6 +46,9 @@ framework/Exceptions/error404.html -text framework/Exceptions/error500-en.html -text framework/Exceptions/error500-zh.html -text framework/Exceptions/error500.html -text +framework/Exceptions/error503-en.html -text +framework/Exceptions/error503-zh.html -text +framework/Exceptions/error503.html -text framework/Exceptions/exception-en.html -text framework/Exceptions/exception-zh.html -text framework/Exceptions/exception.html -text diff --git a/demos/controls/protected/application.xml b/demos/controls/protected/application.xml index 6f8b11bc..a66a7a0c 100644 --- a/demos/controls/protected/application.xml +++ b/demos/controls/protected/application.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?>
-<application id="controls" mode="Debug">
+<application id="controls" mode="Off">
<paths>
<alias id="Demo" path="." />
<alias id="Pages" path="pages" />
diff --git a/framework/Exceptions/TErrorHandler.php b/framework/Exceptions/TErrorHandler.php index 52ed9935..d053619f 100644 --- a/framework/Exceptions/TErrorHandler.php +++ b/framework/Exceptions/TErrorHandler.php @@ -1,8 +1,63 @@ <?php
+/**
+ * TErrorHandler class file
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Revision: $ $Date: $
+ * @package System.Exceptions
+ */
+/**
+ * TErrorHandler class
+ *
+ * TErrorHandler handles all PHP user errors and exceptions generated during
+ * servicing user requests. It displays these errors using different templates
+ * and if possible, using languages preferred by the client user.
+ * Note, PHP parsing errors cannot be caught and handled by TErrorHandler.
+ *
+ * The templates used to format the error output are stored under System.Exceptions.
+ * You may choose to use your own templates, should you not like the templates
+ * provided by Prado. Simply set {@link setErrorTemplatePath ErrorTemplatePath}
+ * to the path (in namespace format) storing your own templates.
+ *
+ * There are two sets of templates, one for errors to be displayed to client users
+ * (called external errors), one for errors to be displayed to system developers
+ * (called internal errors). The template file name for the former is
+ * <b>error[StatusCode][-LanguageCode].html</b>, and for the latter it is
+ * <b>exception[-LanguageCode].html</b>, where StatusCode refers to response status
+ * code (e.g. 404, 500) specified when {@link THttpException} is thrown,
+ * and LanguageCode is the client user preferred language code (e.g. en, zh, de).
+ * The templates <b>error.html</b> and <b>exception.html</b> are default ones
+ * that are used if no other appropriate templates are available.
+ * Note, these templates are not Prado control templates. They are simply
+ * templates with keywords (e.g. %%ErrorMessage%%, %%Version%%)
+ * to be replaced with corresponding information.
+ *
+ * By default, TErrorHandler is registered with {@link TApplication} as the
+ * error handler module. It can be accessed via {@link TApplication::getErrorHandler()}.
+ * You seldom need to deal with the error handler directly. It is mainly used
+ * by the application object to handle errors.
+ *
+ * TErrorHandler may be configured in application configuration file as follows
+ * <module id="error" type="TErrorHandler" ErrorTemplatePath="System.Exceptions" />
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Revision: $ $Date: $
+ * @package System.Exceptions
+ * @since 3.0
+ */
class TErrorHandler extends TComponent implements IModule
{
+ /**
+ * error template file basename
+ */
const ERROR_FILE_NAME='error';
+ /**
+ * exception template file basename
+ */
const EXCEPTION_FILE_NAME='exception';
/**
@@ -52,6 +107,12 @@ class TErrorHandler extends TComponent implements IModule return $this->_templatePath;
}
+ /**
+ * Sets the path storing all error and exception template files.
+ * The path must be in namespace format, such as System.Exceptions (which is the default).
+ * @param string template path in namespace format
+ * @throws TConfigurationException if the template path is invalid
+ */
public function setErrorTemplatePath($value)
{
if(($templatePath=Prado::getPathOfNamespace($this->_templatePath))!==null && is_dir($templatePath))
@@ -60,15 +121,25 @@ class TErrorHandler extends TComponent implements IModule throw new TConfigurationException('errorhandler_errortemplatepath_invalid',$value);
}
+ /**
+ * Handles PHP user errors and exceptions.
+ * This is the event handler responding to the <b>Error</b> event
+ * raised in {@link TApplication}.
+ * The method mainly uses appropriate template to display the error/exception.
+ * It terminates the application immediately after the error is displayed.
+ * @param mixed sender of the event
+ * @param mixed event parameter (if the event is raised by TApplication, it refers to the exception instance)
+ */
public function handleError($sender,$param)
{
static $handling=false;
// We need to restore error and exception handlers,
- // otherwise because errors occured in error handler
- // will not be handled properly.
+ // because within error and exception handlers, new errors and exceptions
+ // cannot be handled properly by PHP
restore_error_handler();
restore_exception_handler();
- if($handling) // ensure that we do not enter infinite loop of error handling
+ // ensure that we do not enter infinite loop of error handling
+ if($handling)
$this->handleRecursiveError($param);
else
{
@@ -85,6 +156,13 @@ class TErrorHandler extends TComponent implements IModule exit(1);
}
+ /**
+ * Displays error to the client user.
+ * THttpException and errors happened when the application is in <b>Debug</b>
+ * mode will be displayed to the client user.
+ * @param integer response status code
+ * @param Exception exception instance
+ */
protected function handleExternalError($statusCode,$exception)
{
if(!($exception instanceof THttpException))
@@ -122,6 +200,13 @@ class TErrorHandler extends TComponent implements IModule echo str_replace($fields,$values,$content);
}
+ /**
+ * Handles error occurs during error handling (called recursive error).
+ * THttpException and errors happened when the application is in <b>Debug</b>
+ * mode will be displayed to the client user.
+ * Error is displayed without using existing template to prevent further errors.
+ * @param Exception exception instance
+ */
protected function handleRecursiveError($exception)
{
if(Prado::getApplication()->getMode()==='Debug')
@@ -138,6 +223,13 @@ class TErrorHandler extends TComponent implements IModule }
}
+ /**
+ * Displays exception information.
+ * Exceptions are displayed with rich context information, including
+ * the call stack and the context source code.
+ * This method is only invoked when application is in <b>Debug</b> mode.
+ * @param Exception exception instance
+ */
protected function displayException($exception)
{
$lines=file($exception->getFile());
@@ -174,9 +266,9 @@ class TErrorHandler extends TComponent implements IModule strftime('%Y-%m-%d %H:%m',time())
);
$lang=Prado::getPreferredLanguage();
- $exceptionFile=dirname(__FILE__).'/'.self::EXCEPTION_FILE_NAME.'-'.$lang.'.html';
+ $exceptionFile=$this->_templatePath.'/'.self::EXCEPTION_FILE_NAME.'-'.$lang.'.html';
if(!is_file($exceptionFile))
- $exceptionFile=dirname(__FILE__).'/'.self::EXCEPTION_FILE_NAME.'.html';
+ $exceptionFile=$this->_templatePath.'/'.self::EXCEPTION_FILE_NAME.'.html';
if(($content=@file_get_contents($exceptionFile))===false)
die("Unable to open exception template file '$exceptionFile'.");
echo str_replace($fields,$values,$content);
diff --git a/framework/Exceptions/error503-en.html b/framework/Exceptions/error503-en.html new file mode 100644 index 00000000..60527c45 --- /dev/null +++ b/framework/Exceptions/error503-en.html @@ -0,0 +1,27 @@ +<html>
+<head>
+<title>Service Unavailable</title>
+<style>
+body {font-family:"Verdana";font-weight:normal;color:black;}
+h1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red }
+h2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon }
+h3 {font-family:"Verdana";font-weight:bold;font-size:11pt}
+p {font-family:"Verdana";font-weight:normal;color:black;font-size:9pt;margin-top: -5px}
+.version {color: gray;font-size:8pt;border-top:1px solid #aaaaaa;}
+</style>
+</head>
+
+<body bgcolor="white">
+<h1>Service Unavailable</h1>
+<p>
+Our system is currently under maintenance. Please come back later.
+</p>
+<p>
+Thank you.
+</p>
+<div class="version">
+%%Version%%<br/>
+%%Time%%
+</div>
+</body>
+</html>
\ No newline at end of file diff --git a/framework/Exceptions/error503-zh.html b/framework/Exceptions/error503-zh.html new file mode 100644 index 00000000..b6b5915c --- /dev/null +++ b/framework/Exceptions/error503-zh.html @@ -0,0 +1,27 @@ +<html>
+<head>
+<title>系统无法提供服务</title>
+<style>
+body {font-family:"Verdana";font-weight:normal;color:black;}
+h1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red }
+h2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon }
+h3 {font-family:"Verdana";font-weight:bold;font-size:11pt}
+p {font-family:"Verdana";font-weight:normal;color:black;font-size:9pt;margin-top: -5px}
+.version {color: gray;font-size:8pt;border-top:1px solid #aaaaaa;}
+</style>
+</head>
+
+<body bgcolor="white">
+<h1>系统无法提供服务</h1>
+<p>
+系统维护中,请稍后再来访问。
+</p>
+<p>
+谢谢。
+</p>
+<div class="version">
+%%Version%%<br/>
+%%Time%%
+</div>
+</body>
+</html>
\ No newline at end of file diff --git a/framework/Exceptions/error503.html b/framework/Exceptions/error503.html new file mode 100644 index 00000000..60527c45 --- /dev/null +++ b/framework/Exceptions/error503.html @@ -0,0 +1,27 @@ +<html>
+<head>
+<title>Service Unavailable</title>
+<style>
+body {font-family:"Verdana";font-weight:normal;color:black;}
+h1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red }
+h2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon }
+h3 {font-family:"Verdana";font-weight:bold;font-size:11pt}
+p {font-family:"Verdana";font-weight:normal;color:black;font-size:9pt;margin-top: -5px}
+.version {color: gray;font-size:8pt;border-top:1px solid #aaaaaa;}
+</style>
+</head>
+
+<body bgcolor="white">
+<h1>Service Unavailable</h1>
+<p>
+Our system is currently under maintenance. Please come back later.
+</p>
+<p>
+Thank you.
+</p>
+<div class="version">
+%%Version%%<br/>
+%%Time%%
+</div>
+</body>
+</html>
\ No newline at end of file diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt index 9ed1160b..17e1e627 100644 --- a/framework/Exceptions/messages.txt +++ b/framework/Exceptions/messages.txt @@ -11,6 +11,7 @@ application_configfile_inexistent = Application configuration file '%s' does no application_module_existing = Application module '%s' cannot be registered twice.
application_service_invalid = Service '%s' must implement IService interface.
application_service_unknown = Requested service '%s' is not defined.
+application_service_unavailable = Service Unavailable.
appconfig_aliaspath_invalid = Application configuration <alias id="%s"> uses an invalid file path "%s".
appconfig_aliasid_required = Application configuration <alias> element must have an "id" attribute.
@@ -55,6 +56,8 @@ memcache_connection_failed = TMemCache failed to connect to memcache server % memcache_host_unchangeable = TMemCache.Host cannot be modified after the module is initialized.
memcache_port_unchangeable = TMemCache.Port cannot be modified after the module is initialized.
+errorhandler_errortemplatepath_invalid = TErrorHandler.ErrorTemplatePath '%s' is invalid. Make sure it is in namespace form and points to a valid directory containing error template files.
+
pageservice_page_unknown = Page '%s' Not Found
diff --git a/framework/TApplication.php b/framework/TApplication.php index 7c9de44a..665ed076 100644 --- a/framework/TApplication.php +++ b/framework/TApplication.php @@ -41,6 +41,9 @@ require_once(PRADO_DIR.'/Web/Services/TPageService.php'); * TApplication coordinates modules and services, and serves as a configuration
* context for all Prado components.
*
+ * TApplication uses a configuration file to specify the settings of
+ * the application, the modules, the services, the parameters, and so on.
+ *
* TApplication adopts a modular structure. A TApplication instance is a composition
* of multiple modules. A module is an instance of class implementing
* {@link IModule} interface. Each module accomplishes certain functionalities
@@ -50,12 +53,44 @@ require_once(PRADO_DIR.'/Web/Services/TPageService.php'); * Modules cooperate with each other to serve a user request by following
* a sequence of lifecycles predefined in TApplication.
*
+ * TApplication has four modes that can be changed by setting {@link setMode Mode}
+ * property (in the application configuration file).
+ * - <b>Off</b> mode will prevent the application from serving user requests.
+ * - <b>Debug</b> mode is mainly used during application development. It ensures
+ * the cache is always up-to-date if caching is enabled. It also allows
+ * exceptions are displayed with rich context information if they occur.
+ * - <b>Normal</b> mode is mainly used during production stage. Exception information
+ * will only be recorded in system error logs. The cache is ensured to be
+ * up-to-date if it is enabled.
+ * - <b>Performance</b> mode is similar to <b>Normal</b> mode except that it
+ * does not ensure the cache is up-to-date.
+ *
* TApplication dispatches each user request to a particular service which
* finishes the actual work for the request with the aid from the application
* modules.
*
- * TApplication uses a configuration file to specify the settings of
- * the application, the modules, the services, the parameters, and so on.
+ * TApplication maintains a lifecycle with the following stages:
+ * - [construct] : construction of the application instance
+ * - [initApplication] : load application configuration and instantiate modules and the requested service
+ * - BeginRequest : this event happens right after application initialization
+ * - Authentication : this event happens when authentication is needed for the current request
+ * - PostAuthentication : this event happens right after the authentication is done for the current request
+ * - Authorization : this event happens when authorization is needed for the current request
+ * - PostAuthorization : this event happens right after the authorization is done for the current request
+ * - LoadState : this event happens when application state needs to be loaded
+ * - PostLoadState : this event happens right after the application state is loaded
+ * - PreRunService : this event happens right before the requested service is to run
+ * - RunService : this event happens when the requested service runs
+ * - PostRunService : this event happens right after the requested service finishes running
+ * - SaveState : this event happens when application needs to save its state
+ * - PostSaveState : this event happens right after the application saves its state
+ * - EndRequest : this is the last stage an application runs
+ * - [destruct] : destruction of the application instance
+ * Modules and services can attach their methods to one or several of the above
+ * events and do appropriate processing when the events are raised. By this way,
+ * the application is able to coordinate the activities of modules and services
+ * in the above order. To terminate an application before the whole lifecycle
+ * completes, call {@link completeRequest}.
*
* Examples:
* - Create and run a Prado application:
@@ -203,6 +238,8 @@ class TApplication extends TComponent $this->_requestCompleted=false;
while($this->_step<$n)
{
+ if($this->_mode==='Off')
+ throw new THttpException(503,'application_service_unavailable');
$method='on'.self::$_steps[$this->_step];
$this->$method($this);
if($this->_requestCompleted && $this->_step<$n-1)
diff --git a/framework/Web/TAssetManager.php b/framework/Web/TAssetManager.php index fa9e649e..4dfcdf5b 100644 --- a/framework/Web/TAssetManager.php +++ b/framework/Web/TAssetManager.php @@ -19,8 +19,8 @@ * TAssetManager will copy the file to be published into a web-accessible
* directory. The default base directory for storing the file is "assets", which
* should be under the application directory. This can be changed by setting
- * the BasePath property together with the BaseUrl property that refers to
- * the URL for accessing the base path.
+ * the {@link setBasePath BasePath} property together with the
+ * {@link setBaseUrl BaseUrl} property that refers to the URL for accessing the base path.
*
* By default, TAssetManager will not publish a file or directory if it already
* exists in the publishing directory and has an older modification time.
|