diff options
author | ctrlaltca <> | 2012-07-12 11:21:01 +0000 |
---|---|---|
committer | ctrlaltca <> | 2012-07-12 11:21:01 +0000 |
commit | 903ae8a581fac1e6917fc3e31d2ad8fb91df80c3 (patch) | |
tree | e08bf04f0823650a231227ac3499121270172a23 /framework/Exceptions/TErrorHandler.php | |
parent | 3e4e6e66aeb3f8fea4e1eb4237498ef9d2358f63 (diff) |
standardize the use of unix eol; use svn properties to enforce native eol
Diffstat (limited to 'framework/Exceptions/TErrorHandler.php')
-rw-r--r-- | framework/Exceptions/TErrorHandler.php | 826 |
1 files changed, 413 insertions, 413 deletions
diff --git a/framework/Exceptions/TErrorHandler.php b/framework/Exceptions/TErrorHandler.php index 7940fcae..f105cd1a 100644 --- a/framework/Exceptions/TErrorHandler.php +++ b/framework/Exceptions/TErrorHandler.php @@ -1,413 +1,413 @@ -<?php
-/**
- * TErrorHandler class file
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @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
- * html files with keywords (e.g. %%ErrorMessage%%, %%Version%%)
- * to be replaced with the 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" class="TErrorHandler" ErrorTemplatePath="System.Exceptions" />
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Id$
- * @package System.Exceptions
- * @since 3.0
- */
-class TErrorHandler extends TModule
-{
- /**
- * error template file basename
- */
- const ERROR_FILE_NAME='error';
- /**
- * exception template file basename
- */
- const EXCEPTION_FILE_NAME='exception';
- /**
- * number of lines before and after the error line to be displayed in case of an exception
- */
- const SOURCE_LINES=12;
-
- /**
- * @var string error template directory
- */
- private $_templatePath=null;
-
- /**
- * Initializes the module.
- * This method is required by IModule and is invoked by application.
- * @param TXmlElement module configuration
- */
- public function init($config)
- {
- $this->getApplication()->setErrorHandler($this);
- }
-
- /**
- * @return string the directory containing error template files.
- */
- public function getErrorTemplatePath()
- {
- if($this->_templatePath===null)
- $this->_templatePath=Prado::getFrameworkPath().'/Exceptions/templates';
- 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($value))!==null && is_dir($templatePath))
- $this->_templatePath=$templatePath;
- else
- 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,
- // because within error and exception handlers, new errors and exceptions
- // cannot be handled properly by PHP
- restore_error_handler();
- restore_exception_handler();
- // ensure that we do not enter infinite loop of error handling
- if($handling)
- $this->handleRecursiveError($param);
- else
- {
- $handling=true;
- if(($response=$this->getResponse())!==null)
- $response->clear();
- if(!headers_sent())
- header('Content-Type: text/html; charset=UTF-8');
- if($param instanceof THttpException)
- $this->handleExternalError($param->getStatusCode(),$param);
- else if($this->getApplication()->getMode()===TApplicationMode::Debug)
- $this->displayException($param);
- else
- $this->handleExternalError(500,$param);
- }
- }
-
-
- /**
- * @param string $value
- * @param Exception|null$exception
- * @return string
- * @since 3.1.6
- */
- protected static function hideSecurityRelated($value, $exception=null)
- {
- $aRpl = array();
- if($exception !== null && $exception instanceof Exception)
- {
- $aTrace = $exception->getTrace();
- foreach($aTrace as $item)
- {
- if(isset($item['file']))
- $aRpl[dirname($item['file']) . DIRECTORY_SEPARATOR] = '<hidden>' . DIRECTORY_SEPARATOR;
- }
- }
- $aRpl[$_SERVER['DOCUMENT_ROOT']] = '${DocumentRoot}';
- $aRpl[str_replace('/', DIRECTORY_SEPARATOR, $_SERVER['DOCUMENT_ROOT'])] = '${DocumentRoot}';
- $aRpl[PRADO_DIR . DIRECTORY_SEPARATOR] = '${PradoFramework}' . DIRECTORY_SEPARATOR;
- if(isset($aRpl[DIRECTORY_SEPARATOR])) unset($aRpl[DIRECTORY_SEPARATOR]);
- $aRpl = array_reverse($aRpl, true);
-
- return str_replace(array_keys($aRpl), $aRpl, $value);
- }
-
- /**
- * 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))
- error_log($exception->__toString());
-
- $content=$this->getErrorTemplate($statusCode,$exception);
-
- $serverAdmin=isset($_SERVER['SERVER_ADMIN'])?$_SERVER['SERVER_ADMIN']:'';
-
- $isDebug = $this->getApplication()->getMode()===TApplicationMode::Debug;
-
- $errorMessage = $exception->getMessage();
- if($isDebug)
- $version=$_SERVER['SERVER_SOFTWARE'].' <a href="http://www.pradosoft.com/">PRADO</a>/'.Prado::getVersion();
- else
- {
- $version='';
- $errorMessage = self::hideSecurityRelated($errorMessage, $exception);
- }
- $tokens=array(
- '%%StatusCode%%' => "$statusCode",
- '%%ErrorMessage%%' => htmlspecialchars($errorMessage),
- '%%ServerAdmin%%' => $serverAdmin,
- '%%Version%%' => $version,
- '%%Time%%' => @strftime('%Y-%m-%d %H:%M',time())
- );
-
- $CGI=substr(php_sapi_name(), 0, 3) == 'cgi'; // FastCGI / IIS
- if($isDebug)
- {
- if ($CGI)
- header("Status: $statusCode ".$exception->getMessage(), true, TPropertyValue::ensureInteger($statusCode));
- else
- header("HTTP/1.0 $statusCode ".$exception->getMessage(), true, TPropertyValue::ensureInteger($statusCode));
- } else {
- if ($CGI)
- header("Status: $statusCode", true, TPropertyValue::ensureInteger($statusCode));
- else
- header("HTTP/1.0 $statusCode", true, TPropertyValue::ensureInteger($statusCode));
- }
-
- echo strtr($content,$tokens);
- }
-
- /**
- * 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($this->getApplication()->getMode()===TApplicationMode::Debug)
- {
- echo "<html><head><title>Recursive Error</title></head>\n";
- echo "<body><h1>Recursive Error</h1>\n";
- echo "<pre>".$exception->__toString()."</pre>\n";
- echo "</body></html>";
- }
- else
- {
- error_log("Error happened while processing an existing error:\n".$exception->__toString());
- header('HTTP/1.0 500 Internal Error');
- }
- }
-
- /**
- * 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)
- {
- if(php_sapi_name()==='cli')
- {
- echo $exception->getMessage()."\n";
- echo $exception->getTraceAsString();
- return;
- }
-
- if($exception instanceof TTemplateException)
- {
- $fileName=$exception->getTemplateFile();
- $lines=empty($fileName)?explode("\n",$exception->getTemplateSource()):@file($fileName);
- $source=$this->getSourceCode($lines,$exception->getLineNumber());
- if($fileName==='')
- $fileName='---embedded template---';
- $errorLine=$exception->getLineNumber();
- }
- else
- {
- if(($trace=$this->getExactTrace($exception))!==null)
- {
- $fileName=$trace['file'];
- $errorLine=$trace['line'];
- }
- else
- {
- $fileName=$exception->getFile();
- $errorLine=$exception->getLine();
- }
- $source=$this->getSourceCode(@file($fileName),$errorLine);
- }
-
- if($this->getApplication()->getMode()===TApplicationMode::Debug)
- $version=$_SERVER['SERVER_SOFTWARE'].' <a href="http://www.pradosoft.com/">PRADO</a>/'.Prado::getVersion();
- else
- $version='';
-
- $tokens=array(
- '%%ErrorType%%' => get_class($exception),
- '%%ErrorMessage%%' => $this->addLink(htmlspecialchars($exception->getMessage())),
- '%%SourceFile%%' => htmlspecialchars($fileName).' ('.$errorLine.')',
- '%%SourceCode%%' => $source,
- '%%StackTrace%%' => htmlspecialchars($exception->getTraceAsString()),
- '%%Version%%' => $version,
- '%%Time%%' => @strftime('%Y-%m-%d %H:%M',time())
- );
-
- $content=$this->getExceptionTemplate($exception);
-
- echo strtr($content,$tokens);
- }
-
- /**
- * Retrieves the template used for displaying internal exceptions.
- * Internal exceptions will be displayed with source code causing the exception.
- * This occurs when the application is in debug mode.
- * @param Exception the exception to be displayed
- * @return string the template content
- */
- protected function getExceptionTemplate($exception)
- {
- $lang=Prado::getPreferredLanguage();
- $exceptionFile=Prado::getFrameworkPath().'/Exceptions/templates/'.self::EXCEPTION_FILE_NAME.'-'.$lang.'.html';
- if(!is_file($exceptionFile))
- $exceptionFile=Prado::getFrameworkPath().'/Exceptions/templates/'.self::EXCEPTION_FILE_NAME.'.html';
- if(($content=@file_get_contents($exceptionFile))===false)
- die("Unable to open exception template file '$exceptionFile'.");
- return $content;
- }
-
- /**
- * Retrieves the template used for displaying external exceptions.
- * External exceptions are those displayed to end-users. They do not contain
- * error source code. Therefore, you might want to override this method
- * to provide your own error template for displaying certain external exceptions.
- * The following tokens in the template will be replaced with corresponding content:
- * %%StatusCode%% : the status code of the exception
- * %%ErrorMessage%% : the error message (HTML encoded).
- * %%ServerAdmin%% : the server admin information (retrieved from Web server configuration)
- * %%Version%% : the version information of the Web server.
- * %%Time%% : the time the exception occurs at
- *
- * @param integer status code (such as 404, 500, etc.)
- * @param Exception the exception to be displayed
- * @return string the template content
- */
- protected function getErrorTemplate($statusCode,$exception)
- {
- $base=$this->getErrorTemplatePath().DIRECTORY_SEPARATOR.self::ERROR_FILE_NAME;
- $lang=Prado::getPreferredLanguage();
- if(is_file("$base$statusCode-$lang.html"))
- $errorFile="$base$statusCode-$lang.html";
- else if(is_file("$base$statusCode.html"))
- $errorFile="$base$statusCode.html";
- else if(is_file("$base-$lang.html"))
- $errorFile="$base-$lang.html";
- else
- $errorFile="$base.html";
- if(($content=@file_get_contents($errorFile))===false)
- die("Unable to open error template file '$errorFile'.");
- return $content;
- }
-
- private function getExactTrace($exception)
- {
- $trace=$exception->getTrace();
- $result=null;
- // if PHP exception, we want to show the 2nd stack level context
- // because the 1st stack level is of little use (it's in error handler)
- if($exception instanceof TPhpErrorException)
- $result=isset($trace[0]['file'])?$trace[0]:$trace[1];
- else if($exception instanceof TInvalidOperationException)
- {
- // in case of getter or setter error, find out the exact file and row
- if(($result=$this->getPropertyAccessTrace($trace,'__get'))===null)
- $result=$this->getPropertyAccessTrace($trace,'__set');
- }
- if($result!==null && strpos($result['file'],': eval()\'d code')!==false)
- return null;
-
- return $result;
- }
-
- private function getPropertyAccessTrace($trace,$pattern)
- {
- $result=null;
- foreach($trace as $t)
- {
- if(isset($t['function']) && $t['function']===$pattern)
- $result=$t;
- else
- break;
- }
- return $result;
- }
-
- private function getSourceCode($lines,$errorLine)
- {
- $beginLine=$errorLine-self::SOURCE_LINES>=0?$errorLine-self::SOURCE_LINES:0;
- $endLine=$errorLine+self::SOURCE_LINES<=count($lines)?$errorLine+self::SOURCE_LINES:count($lines);
-
- $source='';
- for($i=$beginLine;$i<$endLine;++$i)
- {
- if($i===$errorLine-1)
- {
- $line=htmlspecialchars(sprintf("%04d: %s",$i+1,str_replace("\t",' ',$lines[$i])));
- $source.="<div class=\"error\">".$line."</div>";
- }
- else
- $source.=htmlspecialchars(sprintf("%04d: %s",$i+1,str_replace("\t",' ',$lines[$i])));
- }
- return $source;
- }
-
- private function addLink($message)
- {
- $baseUrl='http://www.pradosoft.com/docs/classdoc';
- return preg_replace('/\b(T[A-Z]\w+)\b/',"<a href=\"$baseUrl/\${1}\" target=\"_blank\">\${1}</a>",$message);
- }
-}
-
+<?php +/** + * TErrorHandler class file + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @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 + * html files with keywords (e.g. %%ErrorMessage%%, %%Version%%) + * to be replaced with the 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" class="TErrorHandler" ErrorTemplatePath="System.Exceptions" /> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id$ + * @package System.Exceptions + * @since 3.0 + */ +class TErrorHandler extends TModule +{ + /** + * error template file basename + */ + const ERROR_FILE_NAME='error'; + /** + * exception template file basename + */ + const EXCEPTION_FILE_NAME='exception'; + /** + * number of lines before and after the error line to be displayed in case of an exception + */ + const SOURCE_LINES=12; + + /** + * @var string error template directory + */ + private $_templatePath=null; + + /** + * Initializes the module. + * This method is required by IModule and is invoked by application. + * @param TXmlElement module configuration + */ + public function init($config) + { + $this->getApplication()->setErrorHandler($this); + } + + /** + * @return string the directory containing error template files. + */ + public function getErrorTemplatePath() + { + if($this->_templatePath===null) + $this->_templatePath=Prado::getFrameworkPath().'/Exceptions/templates'; + 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($value))!==null && is_dir($templatePath)) + $this->_templatePath=$templatePath; + else + 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, + // because within error and exception handlers, new errors and exceptions + // cannot be handled properly by PHP + restore_error_handler(); + restore_exception_handler(); + // ensure that we do not enter infinite loop of error handling + if($handling) + $this->handleRecursiveError($param); + else + { + $handling=true; + if(($response=$this->getResponse())!==null) + $response->clear(); + if(!headers_sent()) + header('Content-Type: text/html; charset=UTF-8'); + if($param instanceof THttpException) + $this->handleExternalError($param->getStatusCode(),$param); + else if($this->getApplication()->getMode()===TApplicationMode::Debug) + $this->displayException($param); + else + $this->handleExternalError(500,$param); + } + } + + + /** + * @param string $value + * @param Exception|null$exception + * @return string + * @since 3.1.6 + */ + protected static function hideSecurityRelated($value, $exception=null) + { + $aRpl = array(); + if($exception !== null && $exception instanceof Exception) + { + $aTrace = $exception->getTrace(); + foreach($aTrace as $item) + { + if(isset($item['file'])) + $aRpl[dirname($item['file']) . DIRECTORY_SEPARATOR] = '<hidden>' . DIRECTORY_SEPARATOR; + } + } + $aRpl[$_SERVER['DOCUMENT_ROOT']] = '${DocumentRoot}'; + $aRpl[str_replace('/', DIRECTORY_SEPARATOR, $_SERVER['DOCUMENT_ROOT'])] = '${DocumentRoot}'; + $aRpl[PRADO_DIR . DIRECTORY_SEPARATOR] = '${PradoFramework}' . DIRECTORY_SEPARATOR; + if(isset($aRpl[DIRECTORY_SEPARATOR])) unset($aRpl[DIRECTORY_SEPARATOR]); + $aRpl = array_reverse($aRpl, true); + + return str_replace(array_keys($aRpl), $aRpl, $value); + } + + /** + * 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)) + error_log($exception->__toString()); + + $content=$this->getErrorTemplate($statusCode,$exception); + + $serverAdmin=isset($_SERVER['SERVER_ADMIN'])?$_SERVER['SERVER_ADMIN']:''; + + $isDebug = $this->getApplication()->getMode()===TApplicationMode::Debug; + + $errorMessage = $exception->getMessage(); + if($isDebug) + $version=$_SERVER['SERVER_SOFTWARE'].' <a href="http://www.pradosoft.com/">PRADO</a>/'.Prado::getVersion(); + else + { + $version=''; + $errorMessage = self::hideSecurityRelated($errorMessage, $exception); + } + $tokens=array( + '%%StatusCode%%' => "$statusCode", + '%%ErrorMessage%%' => htmlspecialchars($errorMessage), + '%%ServerAdmin%%' => $serverAdmin, + '%%Version%%' => $version, + '%%Time%%' => @strftime('%Y-%m-%d %H:%M',time()) + ); + + $CGI=substr(php_sapi_name(), 0, 3) == 'cgi'; // FastCGI / IIS + if($isDebug) + { + if ($CGI) + header("Status: $statusCode ".$exception->getMessage(), true, TPropertyValue::ensureInteger($statusCode)); + else + header("HTTP/1.0 $statusCode ".$exception->getMessage(), true, TPropertyValue::ensureInteger($statusCode)); + } else { + if ($CGI) + header("Status: $statusCode", true, TPropertyValue::ensureInteger($statusCode)); + else + header("HTTP/1.0 $statusCode", true, TPropertyValue::ensureInteger($statusCode)); + } + + echo strtr($content,$tokens); + } + + /** + * 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($this->getApplication()->getMode()===TApplicationMode::Debug) + { + echo "<html><head><title>Recursive Error</title></head>\n"; + echo "<body><h1>Recursive Error</h1>\n"; + echo "<pre>".$exception->__toString()."</pre>\n"; + echo "</body></html>"; + } + else + { + error_log("Error happened while processing an existing error:\n".$exception->__toString()); + header('HTTP/1.0 500 Internal Error'); + } + } + + /** + * 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) + { + if(php_sapi_name()==='cli') + { + echo $exception->getMessage()."\n"; + echo $exception->getTraceAsString(); + return; + } + + if($exception instanceof TTemplateException) + { + $fileName=$exception->getTemplateFile(); + $lines=empty($fileName)?explode("\n",$exception->getTemplateSource()):@file($fileName); + $source=$this->getSourceCode($lines,$exception->getLineNumber()); + if($fileName==='') + $fileName='---embedded template---'; + $errorLine=$exception->getLineNumber(); + } + else + { + if(($trace=$this->getExactTrace($exception))!==null) + { + $fileName=$trace['file']; + $errorLine=$trace['line']; + } + else + { + $fileName=$exception->getFile(); + $errorLine=$exception->getLine(); + } + $source=$this->getSourceCode(@file($fileName),$errorLine); + } + + if($this->getApplication()->getMode()===TApplicationMode::Debug) + $version=$_SERVER['SERVER_SOFTWARE'].' <a href="http://www.pradosoft.com/">PRADO</a>/'.Prado::getVersion(); + else + $version=''; + + $tokens=array( + '%%ErrorType%%' => get_class($exception), + '%%ErrorMessage%%' => $this->addLink(htmlspecialchars($exception->getMessage())), + '%%SourceFile%%' => htmlspecialchars($fileName).' ('.$errorLine.')', + '%%SourceCode%%' => $source, + '%%StackTrace%%' => htmlspecialchars($exception->getTraceAsString()), + '%%Version%%' => $version, + '%%Time%%' => @strftime('%Y-%m-%d %H:%M',time()) + ); + + $content=$this->getExceptionTemplate($exception); + + echo strtr($content,$tokens); + } + + /** + * Retrieves the template used for displaying internal exceptions. + * Internal exceptions will be displayed with source code causing the exception. + * This occurs when the application is in debug mode. + * @param Exception the exception to be displayed + * @return string the template content + */ + protected function getExceptionTemplate($exception) + { + $lang=Prado::getPreferredLanguage(); + $exceptionFile=Prado::getFrameworkPath().'/Exceptions/templates/'.self::EXCEPTION_FILE_NAME.'-'.$lang.'.html'; + if(!is_file($exceptionFile)) + $exceptionFile=Prado::getFrameworkPath().'/Exceptions/templates/'.self::EXCEPTION_FILE_NAME.'.html'; + if(($content=@file_get_contents($exceptionFile))===false) + die("Unable to open exception template file '$exceptionFile'."); + return $content; + } + + /** + * Retrieves the template used for displaying external exceptions. + * External exceptions are those displayed to end-users. They do not contain + * error source code. Therefore, you might want to override this method + * to provide your own error template for displaying certain external exceptions. + * The following tokens in the template will be replaced with corresponding content: + * %%StatusCode%% : the status code of the exception + * %%ErrorMessage%% : the error message (HTML encoded). + * %%ServerAdmin%% : the server admin information (retrieved from Web server configuration) + * %%Version%% : the version information of the Web server. + * %%Time%% : the time the exception occurs at + * + * @param integer status code (such as 404, 500, etc.) + * @param Exception the exception to be displayed + * @return string the template content + */ + protected function getErrorTemplate($statusCode,$exception) + { + $base=$this->getErrorTemplatePath().DIRECTORY_SEPARATOR.self::ERROR_FILE_NAME; + $lang=Prado::getPreferredLanguage(); + if(is_file("$base$statusCode-$lang.html")) + $errorFile="$base$statusCode-$lang.html"; + else if(is_file("$base$statusCode.html")) + $errorFile="$base$statusCode.html"; + else if(is_file("$base-$lang.html")) + $errorFile="$base-$lang.html"; + else + $errorFile="$base.html"; + if(($content=@file_get_contents($errorFile))===false) + die("Unable to open error template file '$errorFile'."); + return $content; + } + + private function getExactTrace($exception) + { + $trace=$exception->getTrace(); + $result=null; + // if PHP exception, we want to show the 2nd stack level context + // because the 1st stack level is of little use (it's in error handler) + if($exception instanceof TPhpErrorException) + $result=isset($trace[0]['file'])?$trace[0]:$trace[1]; + else if($exception instanceof TInvalidOperationException) + { + // in case of getter or setter error, find out the exact file and row + if(($result=$this->getPropertyAccessTrace($trace,'__get'))===null) + $result=$this->getPropertyAccessTrace($trace,'__set'); + } + if($result!==null && strpos($result['file'],': eval()\'d code')!==false) + return null; + + return $result; + } + + private function getPropertyAccessTrace($trace,$pattern) + { + $result=null; + foreach($trace as $t) + { + if(isset($t['function']) && $t['function']===$pattern) + $result=$t; + else + break; + } + return $result; + } + + private function getSourceCode($lines,$errorLine) + { + $beginLine=$errorLine-self::SOURCE_LINES>=0?$errorLine-self::SOURCE_LINES:0; + $endLine=$errorLine+self::SOURCE_LINES<=count($lines)?$errorLine+self::SOURCE_LINES:count($lines); + + $source=''; + for($i=$beginLine;$i<$endLine;++$i) + { + if($i===$errorLine-1) + { + $line=htmlspecialchars(sprintf("%04d: %s",$i+1,str_replace("\t",' ',$lines[$i]))); + $source.="<div class=\"error\">".$line."</div>"; + } + else + $source.=htmlspecialchars(sprintf("%04d: %s",$i+1,str_replace("\t",' ',$lines[$i]))); + } + return $source; + } + + private function addLink($message) + { + $baseUrl='http://www.pradosoft.com/docs/classdoc'; + return preg_replace('/\b(T[A-Z]\w+)\b/',"<a href=\"$baseUrl/\${1}\" target=\"_blank\">\${1}</a>",$message); + } +} + |