summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxue <>2007-03-02 21:05:38 +0000
committerxue <>2007-03-02 21:05:38 +0000
commit13495b462f6a618530f0f6af71fb6fad6bafed91 (patch)
treeebe213dc568f858d16c48403948227c7e0932592
parent56df1d34bd381e864f613cc3b72fb886dc548008 (diff)
Improved error display.
-rw-r--r--HISTORY2
-rw-r--r--framework/Exceptions/TErrorHandler.php99
-rw-r--r--framework/Exceptions/TException.php65
-rw-r--r--framework/Exceptions/messages.txt3
-rw-r--r--framework/Exceptions/templates/exception-en.html3
-rw-r--r--framework/Exceptions/templates/exception-fr.html3
-rw-r--r--framework/Exceptions/templates/exception-zh.html3
-rw-r--r--framework/TComponent.php10
-rw-r--r--framework/Web/Services/TPageService.php2
-rw-r--r--framework/Web/UI/TTemplateManager.php14
10 files changed, 162 insertions, 42 deletions
diff --git a/HISTORY b/HISTORY
index bbc5ef7e..044456ef 100644
--- a/HISTORY
+++ b/HISTORY
@@ -14,6 +14,8 @@ ENH: Added TUser.getState() and setState() for storing user session data (Qiang)
ENH: Added renderer feature to TRepeater, TDataList and TDataGrid (Qiang)
ENH: Added support to include external application configuration files (Qiang)
ENH: Added PRADO version info in PRADO-generated directories to avoid upgrading issue (Qiang)
+ENH: Improved exception display with hyperlinked keywords (Qiang)
+ENH: Improved exception display about template syntax error (Qiang)
NEW: TShellApplication (Qiang)
NEW: TDbCache (Qiang)
NEW: Active Record driver for IBM DB2 (Cesar Ramos)
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.="<div class=\"error\">".$line."</div>";
+ $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.="<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('/(T[A-Z]\w+)/',"<a href=\"$baseUrl/\${1}\" target=\"_blank\">\${1}</a>",$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
@@ -209,6 +209,71 @@ class TConfigurationException extends TSystemException
}
/**
+ * TTemplateException class
+ *
+ * TTemplateException represents an exception caused by invalid template syntax.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @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
*
* TIOException represents an exception related with improper IO operations.
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 @@
-<!DOCTYPE html PUBLIC
+<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
@@ -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 @@
-<!DOCTYPE html PUBLIC
+<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
@@ -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 @@
-<!DOCTYPE html PUBLIC
+<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh" lang="zh">
@@ -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;
}
/**