From 689080519e10aefdb015dfffe57160f4ce08b9d7 Mon Sep 17 00:00:00 2001
From: Carsten Brandt <mail@cebe.cc>
Date: Sun, 26 May 2013 04:38:13 +0200
Subject: [PATCH] Added support for previous exceptions

PHP supports exception stacks since 5.3 so we should use it.
Also Errorhandler is now able to display the stack: #297
---
 framework/yii/base/ErrorHandler.php                    | 22 ++++++++++++++++++++++
 framework/yii/base/HttpException.php                   |  2 +-
 framework/yii/console/Application.php                  |  2 +-
 framework/yii/db/ActiveRecord.php                      |  2 +-
 framework/yii/db/Command.php                           |  6 +++---
 framework/yii/db/Connection.php                        |  2 +-
 framework/yii/views/errorHandler/callStackItem.php     |  1 -
 framework/yii/views/errorHandler/main.php              | 36 +++++++++++++++++++++++++++++++++---
 framework/yii/views/errorHandler/previousException.php | 20 ++++++++++++++++++++
 9 files changed, 82 insertions(+), 11 deletions(-)
 create mode 100644 framework/yii/views/errorHandler/previousException.php

diff --git a/framework/yii/base/ErrorHandler.php b/framework/yii/base/ErrorHandler.php
index 8997b9e..6077af6 100644
--- a/framework/yii/base/ErrorHandler.php
+++ b/framework/yii/base/ErrorHandler.php
@@ -49,6 +49,10 @@ class ErrorHandler extends Component
 	 */
 	public $callStackItemView = '@yii/views/errorHandler/callStackItem.php';
 	/**
+	 * @var string the path of the view file for rendering previous exceptions.
+	 */
+	public $previousExceptionView = '@yii/views/errorHandler/previousException.php';
+	/**
 	 * @var \Exception the exception that is being handled currently.
 	 */
 	public $exception;
@@ -161,6 +165,24 @@ class ErrorHandler extends Component
 	}
 
 	/**
+	 * Renders the previous exception stack for a given Exception.
+	 * @param \Exception $exception the exception whose precursors should be rendered.
+	 * @return string HTML content of the rendered previous exceptions.
+	 * Empty string if there are none.
+	 */
+	public function renderPreviousExceptions($exception)
+	{
+		if (($previous = $exception->getPrevious()) === null) {
+			return '';
+		}
+		$view = new View();
+		return $view->renderFile($this->previousExceptionView, array(
+			'exception' => $previous,
+			'previousHtml' => $this->renderPreviousExceptions($previous),
+		), $this);
+	}
+
+	/**
 	 * Renders a single call stack element.
 	 * @param string $file name where call has happened.
 	 * @param integer $line number on which call has happened.
diff --git a/framework/yii/base/HttpException.php b/framework/yii/base/HttpException.php
index 2b014f7..4d63764 100644
--- a/framework/yii/base/HttpException.php
+++ b/framework/yii/base/HttpException.php
@@ -103,7 +103,7 @@ class HttpException extends UserException
 		if (isset($httpCodes[$this->statusCode])) {
 			return $httpCodes[$this->statusCode];
 		} else {
-			return \Yii::t('yii', 'Error');
+			return 'Error';
 		}
 	}
 }
diff --git a/framework/yii/console/Application.php b/framework/yii/console/Application.php
index 58c2de4..6cc114a 100644
--- a/framework/yii/console/Application.php
+++ b/framework/yii/console/Application.php
@@ -113,7 +113,7 @@ class Application extends \yii\base\Application
 		try {
 			return parent::runAction($route, $params);
 		} catch (InvalidRouteException $e) {
-			throw new Exception(\Yii::t('yii', 'Unknown command "{command}".', array('{command}' => $route)));
+			throw new Exception(\Yii::t('yii', 'Unknown command "{command}".', array('{command}' => $route)), 0, $e);
 		}
 	}
 
diff --git a/framework/yii/db/ActiveRecord.php b/framework/yii/db/ActiveRecord.php
index dd90782..a58ac4d 100644
--- a/framework/yii/db/ActiveRecord.php
+++ b/framework/yii/db/ActiveRecord.php
@@ -1215,7 +1215,7 @@ class ActiveRecord extends Model
 				return $relation;
 			}
 		} catch (UnknownMethodException $e) {
-			throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".');
+			throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".', 0, $e);
 		}
 	}
 
diff --git a/framework/yii/db/Command.php b/framework/yii/db/Command.php
index e05bde7..657ef8d 100644
--- a/framework/yii/db/Command.php
+++ b/framework/yii/db/Command.php
@@ -148,7 +148,7 @@ class Command extends \yii\base\Component
 			} catch (\Exception $e) {
 				Yii::error($e->getMessage() . "\nFailed to prepare SQL: $sql", __METHOD__);
 				$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
-				throw new Exception($e->getMessage(), $errorInfo, (int)$e->getCode());
+				throw new Exception($e->getMessage(), $errorInfo, (int)$e->getCode(), $e);
 			}
 		}
 	}
@@ -298,7 +298,7 @@ class Command extends \yii\base\Component
 			Yii::error("$message\nFailed to execute SQL: $rawSql", __METHOD__);
 
 			$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
-			throw new Exception($message, $errorInfo, (int)$e->getCode());
+			throw new Exception($message, $errorInfo, (int)$e->getCode(), $e);
 		}
 	}
 
@@ -433,7 +433,7 @@ class Command extends \yii\base\Component
 			$message = $e->getMessage();
 			Yii::error("$message\nCommand::$method() failed: $rawSql", __METHOD__);
 			$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
-			throw new Exception($message, $errorInfo, (int)$e->getCode());
+			throw new Exception($message, $errorInfo, (int)$e->getCode(), $e);
 		}
 	}
 
diff --git a/framework/yii/db/Connection.php b/framework/yii/db/Connection.php
index d956691..e14eeb7 100644
--- a/framework/yii/db/Connection.php
+++ b/framework/yii/db/Connection.php
@@ -319,7 +319,7 @@ class Connection extends Component
 				Yii::endProfile($token, __METHOD__);
 				Yii::error("Failed to open DB connection ({$this->dsn}): " . $e->getMessage(), __METHOD__);
 				$message = YII_DEBUG ? 'Failed to open DB connection: ' . $e->getMessage() : 'Failed to open DB connection.';
-				throw new Exception($message, $e->errorInfo, (int)$e->getCode());
+				throw new Exception($message, $e->errorInfo, (int)$e->getCode(), $e);
 			}
 		}
 	}
diff --git a/framework/yii/views/errorHandler/callStackItem.php b/framework/yii/views/errorHandler/callStackItem.php
index 3854ea4..2139c46 100644
--- a/framework/yii/views/errorHandler/callStackItem.php
+++ b/framework/yii/views/errorHandler/callStackItem.php
@@ -11,7 +11,6 @@
  */
 $context = $this->context;
 ?>
-
 <li class="<?php if (!$context->isCoreFile($file)) echo 'application'; ?> call-stack-item">
 	<div class="element-wrap">
 		<div class="element">
diff --git a/framework/yii/views/errorHandler/main.php b/framework/yii/views/errorHandler/main.php
index 93fa589..b92e29b 100644
--- a/framework/yii/views/errorHandler/main.php
+++ b/framework/yii/views/errorHandler/main.php
@@ -93,6 +93,35 @@ html,body{
 	font-size: 20px;
 	text-shadow: 0 1px 0 #cacaca;
 }
+/* previous exceptions */
+.header div.previous{
+	margin: 20px 20px 0 0;
+}
+.header div.previous div{
+	margin: 15px 20px 0 25px;
+}
+.header div.previous h1{
+	font-size: 20px;
+	margin-bottom: 10px;
+}
+h1 span.arrow{
+	display: inline-block;
+	-moz-transform: scale(-1, 1);
+	-webkit-transform: scale(-1, 1);
+	-o-transform: scale(-1, 1);
+	transform: scale(-1, 1);
+	filter: progid:DXImageTransform.Microsoft.BasicImage(mirror=1);
+	width: 30px;
+	text-align: center;
+}
+.header div.previous h2{
+	font-size: 15px;
+	margin-left: 30px;
+}
+.header div.previous p{
+	margin: 10px 0 0 30px;
+	color: #aaa;
+}
 
 /* call stack */
 .call-stack{
@@ -131,7 +160,7 @@ html,body{
 	display: inline-block;
 }
 .call-stack ul li .text{
-	color: #bbb;
+	color: #aaa;
 }
 .call-stack ul li.application .text{
 	color: #505050;
@@ -139,7 +168,7 @@ html,body{
 .call-stack ul li .at{
 	position: absolute;
 	right: 110px; /* 50px + 60px */
-	color: #bbb;
+	color: #aaa;
 }
 .call-stack ul li.application .at{
 	color: #505050;
@@ -185,7 +214,7 @@ html,body{
 	line-height: 18px;
 	font-size: 14px;
 	font-family: Consolas, Courier New, monospace;
-	color: #bbb;
+	color: #aaa;
 }
 .call-stack ul li .code pre{
 	position: relative;
@@ -335,6 +364,7 @@ pre .diff .change{
 			?></h1>
 		<?php endif; ?>
 		<h2><?php echo $context->htmlEncode($exception->getMessage()); ?></h2>
+		<?php echo $context->renderPreviousExceptions($exception); ?>
 	</div>
 
 	<div class="call-stack">
diff --git a/framework/yii/views/errorHandler/previousException.php b/framework/yii/views/errorHandler/previousException.php
new file mode 100644
index 0000000..4a3d9c6
--- /dev/null
+++ b/framework/yii/views/errorHandler/previousException.php
@@ -0,0 +1,20 @@
+<div class="previous">
+	<h1><span class="arrow">&crarr;</span><span>Caused by: </span><?php
+		/**
+		 * @var \yii\base\View $this
+		 * @var \yii\base\Exception $exception
+		 * @var string $previousHtml
+		 * @var \yii\base\ErrorHandler $context
+		 */
+		$context = $this->context;
+		if ($exception instanceof \yii\base\Exception) {
+			echo '<span>' . $context->htmlEncode($exception->getName()) . '</span>';
+			echo ' &ndash; ' . $context->addTypeLinks(get_class($exception));
+		} else {
+			echo '<span>' . $context->htmlEncode(get_class($exception)) . '</span>';
+		}
+	?></h1>
+	<h2><?php echo $context->htmlEncode($exception->getMessage()); ?></h2>
+	<p>In <span class="file"><?php echo $exception->getFile(); ?></span> at line <span class="line"><?php echo $exception->getLine(); ?></span></p>
+	<?php echo $previousHtml; ?>
+</div>
\ No newline at end of file
--
libgit2 0.27.1