From 13495b462f6a618530f0f6af71fb6fad6bafed91 Mon Sep 17 00:00:00 2001 From: xue <> Date: Fri, 2 Mar 2007 21:05:38 +0000 Subject: Improved error display. --- framework/Exceptions/TErrorHandler.php | 99 ++++++++++++++++++------ framework/Exceptions/TException.php | 65 ++++++++++++++++ framework/Exceptions/messages.txt | 3 +- framework/Exceptions/templates/exception-en.html | 3 +- framework/Exceptions/templates/exception-fr.html | 3 +- framework/Exceptions/templates/exception-zh.html | 3 +- framework/TComponent.php | 10 +-- framework/Web/Services/TPageService.php | 2 +- framework/Web/UI/TTemplateManager.php | 14 ++-- 9 files changed, 160 insertions(+), 42 deletions(-) (limited to 'framework') diff --git a/framework/Exceptions/TErrorHandler.php b/framework/Exceptions/TErrorHandler.php index 14c824c9..869ccb00 100644 --- a/framework/Exceptions/TErrorHandler.php +++ b/framework/Exceptions/TErrorHandler.php @@ -207,39 +207,34 @@ class TErrorHandler extends TModule */ protected function displayException($exception) { - $fileName=$exception->getFile(); - $errorLine=$exception->getLine(); - if($exception instanceof TPhpErrorException) + + if($exception instanceof TTemplateException) { - // 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) - $trace=$exception->getTrace(); - if(isset($trace[1]) && isset($trace[1]['file']) && isset($trace[1]['line'])) - { - $fileName=$trace[1]['file']; - $errorLine=$trace[1]['line']; - } + $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(); } - $lines=file($fileName); - - $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) + else { - if($i===$errorLine-1) + if(($trace=$this->getExactTrace($exception))!==null) { - $line=htmlspecialchars(sprintf("%04d: %s",$i+1,str_replace("\t",' ',$lines[$i]))); - $source.="
".$line."
"; + $fileName=$trace['file']; + $errorLine=$trace['line']; } else - $source.=htmlspecialchars(sprintf("%04d: %s",$i+1,str_replace("\t",' ',$lines[$i]))); + { + $fileName=$exception->getFile(); + $errorLine=$exception->getLine(); + } + $source=$this->getSourceCode(@file($fileName),$errorLine); } $tokens=array( '%%ErrorType%%' => get_class($exception), - '%%ErrorMessage%%' => htmlspecialchars($exception->getMessage()), + '%%ErrorMessage%%' => $this->addLink(htmlspecialchars($exception->getMessage())), '%%SourceFile%%' => htmlspecialchars($fileName).' ('.$errorLine.')', '%%SourceCode%%' => $source, '%%StackTrace%%' => htmlspecialchars($exception->getTraceAsString()), @@ -254,6 +249,64 @@ class TErrorHandler extends TModule die("Unable to open exception template file '$exceptionFile'."); echo strtr($content,$tokens); } + + 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=$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.="
".$line."
"; + } + 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('/(T[A-Z]\w+)/',"\${1}",$message); + } } ?> \ No newline at end of file diff --git a/framework/Exceptions/TException.php b/framework/Exceptions/TException.php index 745d5769..6f9a8a3f 100644 --- a/framework/Exceptions/TException.php +++ b/framework/Exceptions/TException.php @@ -208,6 +208,71 @@ class TConfigurationException extends TSystemException { } +/** + * TTemplateException class + * + * TTemplateException represents an exception caused by invalid template syntax. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Exceptions + * @since 3.1 + */ +class TTemplateException extends TConfigurationException +{ + private $_template=''; + private $_lineNumber=0; + private $_fileName=''; + + /** + * @return string the template source code that causes the exception. This is empty if {@link getTemplateFile TemplateFile} is not empty. + */ + public function getTemplateSource() + { + return $this->_template; + } + + /** + * @param string the template source code that causes the exception + */ + public function setTemplateSource($value) + { + $this->_template=$value; + } + + /** + * @return string the template file that causes the exception. This could be empty if the template is an embedded template. In this case, use {@link getTemplateSource TemplateSource} to obtain the actual template content. + */ + public function getTemplateFile() + { + return $this->_fileName; + } + + /** + * @param string the template file that causes the exception + */ + public function setTemplateFile($value) + { + $this->_fileName=$value; + } + + /** + * @return integer the line number at which the template has error + */ + public function getLineNumber() + { + return $this->_lineNumber; + } + + /** + * @param integer the line number at which the template has error + */ + public function setLineNumber($value) + { + $this->_lineNumber=TPropertyValue::ensureInteger($value); + } +} + /** * TIOException class * diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt index b35ba7bf..79fdb8d1 100644 --- a/framework/Exceptions/messages.txt +++ b/framework/Exceptions/messages.txt @@ -124,8 +124,7 @@ template_property_readonly = {0} has a read-only property '{1}'. template_event_forbidden = {0} is a non-control component. No handler can be attached to its event '{1}' in a template. template_databind_forbidden = {0} is a non-control component. Expressions cannot be bound to its property '{1}'. template_component_required = '{0}' is not a component. Only components can appear in a template. -template_format_invalid = Error in {0} (line {1}) : {2} -template_format_invalid2 = Error at line {0} of the following template: {1} {2} +template_format_invalid = Invalid template syntax: {0} template_property_duplicated = Property {0} is configured twice or more. template_eventhandler_invalid = {0}.{1} can only accept a static string. template_controlid_invalid = {0}.ID can only accept a static text string. diff --git a/framework/Exceptions/templates/exception-en.html b/framework/Exceptions/templates/exception-en.html index f9012206..aed3755d 100644 --- a/framework/Exceptions/templates/exception-en.html +++ b/framework/Exceptions/templates/exception-en.html @@ -1,4 +1,4 @@ - @@ -12,6 +12,7 @@ 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} +a {font-family:"Verdana";color:red;} code,pre {font-family:"Lucida Console";font-size:10pt;} td,.version {color: gray;font-size:8pt;border-top:1px solid #aaaaaa;} .source {font-family:"Lucida Console";font-weight:normal;background-color:#ffffee;} diff --git a/framework/Exceptions/templates/exception-fr.html b/framework/Exceptions/templates/exception-fr.html index 993ef39e..7cb61361 100644 --- a/framework/Exceptions/templates/exception-fr.html +++ b/framework/Exceptions/templates/exception-fr.html @@ -1,4 +1,4 @@ - @@ -11,6 +11,7 @@ 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} +a {font-family:"Verdana";color:red;} code,pre {font-family:"Lucida Console";font-size:10pt;} td,.version {color: gray;font-size:8pt;border-top:1px solid #aaaaaa;} .source {font-family:"Lucida Console";font-weight:normal;background-color:#ffffee;} diff --git a/framework/Exceptions/templates/exception-zh.html b/framework/Exceptions/templates/exception-zh.html index ddfe9473..257e8e69 100644 --- a/framework/Exceptions/templates/exception-zh.html +++ b/framework/Exceptions/templates/exception-zh.html @@ -1,4 +1,4 @@ - @@ -12,6 +12,7 @@ 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} +a {font-family:"Verdana";color:red;} code,pre {font-family:"Lucida Console";font-size:10pt;} td,.version {color: gray;font-size:8pt;border-top:1px solid #aaaaaa;} .source {font-family:"Lucida Console";font-weight:normal;background-color:#ffffee;} diff --git a/framework/TComponent.php b/framework/TComponent.php index 5f907082..54fe7ec7 100644 --- a/framework/TComponent.php +++ b/framework/TComponent.php @@ -389,10 +389,7 @@ class TComponent } catch(Exception $e) { - if($e instanceof TException) - throw $e; - else - throw new TInvalidOperationException('component_expression_invalid',get_class($this),$expression,$e->getMessage()); + throw new TInvalidOperationException('component_expression_invalid',get_class($this),$expression,$e->getMessage()); } } @@ -415,10 +412,7 @@ class TComponent } catch(Exception $e) { - if($e instanceof TException) - throw $e; - else - throw new TInvalidOperationException('component_statements_invalid',get_class($this),$statements,$e->getMessage()); + throw new TInvalidOperationException('component_statements_invalid',get_class($this),$statements,$e->getMessage()); } } diff --git a/framework/Web/Services/TPageService.php b/framework/Web/Services/TPageService.php index a0c4b8fb..7d434523 100644 --- a/framework/Web/Services/TPageService.php +++ b/framework/Web/Services/TPageService.php @@ -397,7 +397,7 @@ class TPageService extends TService */ protected function createPage($pagePath) { - $path=$this->getBasePath().'/'.strtr($pagePath,'.','/'); + $path=$this->getBasePath().DIRECTORY_SEPARATOR.strtr($pagePath,'.','/'); $hasTemplateFile=is_file($path.self::PAGE_FILE_EXT); $hasClassFile=is_file($path.Prado::CLASS_FILE_EXT); diff --git a/framework/Web/UI/TTemplateManager.php b/framework/Web/UI/TTemplateManager.php index b94becb1..a8caba24 100644 --- a/framework/Web/UI/TTemplateManager.php +++ b/framework/Web/UI/TTemplateManager.php @@ -65,7 +65,7 @@ class TTemplateManager extends TModule public function getTemplateByClassName($className) { $class=new ReflectionClass($className); - $tplFile=dirname($class->getFileName()).'/'.$className.self::TEMPLATE_FILE_EXT; + $tplFile=dirname($class->getFileName()).DIRECTORY_SEPARATOR.$className.self::TEMPLATE_FILE_EXT; return $this->getTemplateByFileName($tplFile); } @@ -752,7 +752,7 @@ class TTemplate extends TApplicationComponent implements ITemplate } catch(Exception $e) { - if(($e instanceof TException) && ($e->getErrorCode()==='template_format_invalid' || $e->getErrorCode()==='template_format_invalid2')) + if(($e instanceof TException) && ($e instanceof TTemplateException)) throw $e; if($matchEnd===0) $line=$this->_startingLine+1; @@ -992,6 +992,7 @@ class TTemplate extends TApplicationComponent implements ITemplate protected function handleException($e,$line,$input=null) { $srcFile=$this->_tplFile; + if(($n=count($this->_includedFiles))>0) // need to adjust error row number and file name { for($i=$n-1;$i>=0;--$i) @@ -1009,10 +1010,13 @@ class TTemplate extends TApplicationComponent implements ITemplate } } } - if(empty($srcFile)) - throw new TConfigurationException('template_format_invalid2',$line,$e->getMessage(),$input); + $exception=new TTemplateException('template_format_invalid',$e->getMessage()); + $exception->setLineNumber($line); + if(!empty($srcFile)) + $exception->setTemplateFile($srcFile); else - throw new TConfigurationException('template_format_invalid',$srcFile,$line,$e->getMessage()); + $exception->setTemplateSource($input); + throw $exception; } /** -- cgit v1.2.3