diff options
-rw-r--r-- | HISTORY | 2 | ||||
-rw-r--r-- | framework/Exceptions/TErrorHandler.php | 99 | ||||
-rw-r--r-- | framework/Exceptions/TException.php | 65 | ||||
-rw-r--r-- | framework/Exceptions/messages.txt | 3 | ||||
-rw-r--r-- | framework/Exceptions/templates/exception-en.html | 3 | ||||
-rw-r--r-- | framework/Exceptions/templates/exception-fr.html | 3 | ||||
-rw-r--r-- | framework/Exceptions/templates/exception-zh.html | 3 | ||||
-rw-r--r-- | framework/TComponent.php | 10 | ||||
-rw-r--r-- | framework/Web/Services/TPageService.php | 2 | ||||
-rw-r--r-- | framework/Web/UI/TTemplateManager.php | 14 |
10 files changed, 162 insertions, 42 deletions
@@ -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;
}
/**
|