diff --git a/src/error/PhutilAggregateException.php b/src/error/PhutilAggregateException.php --- a/src/error/PhutilAggregateException.php +++ b/src/error/PhutilAggregateException.php @@ -27,25 +27,29 @@ private $exceptions = array(); - public function __construct($message, array $other_exceptions) { + public function __construct($message, array $exceptions) { // We don't call assert_instances_of($other_exceptions, 'Exception') to not // throw another exception in this exception. - $this->exceptions = $other_exceptions; + $this->exceptions = $exceptions; + parent::__construct($message); + } + + public function __toString() { + $message = array(); + $message[] = $this->getMessage(); - $full_message = array(); - $full_message[] = $message; - foreach ($other_exceptions as $key => $exception) { + foreach ($this->exceptions as $key => $exception) { $ex_message = (is_string($key) ? $key.': ' : ''). get_class($exception).': '. $exception->getMessage(); $ex_message = ' - '.str_replace("\n", "\n ", $ex_message); - $full_message[] = $ex_message; + $message[] = $ex_message; } - parent::__construct(implode("\n", $full_message), count($other_exceptions)); + return implode("\n", $message); } public function getExceptions() { diff --git a/src/error/PhutilErrorHandler.php b/src/error/PhutilErrorHandler.php --- a/src/error/PhutilErrorHandler.php +++ b/src/error/PhutilErrorHandler.php @@ -77,6 +77,28 @@ /** + * Gets all exceptions of an aggregate/proxy exception as an iterator. + * + * @param Exception Exception to unnest. + * @return ArrayIterator Iterator for nested exceptions. + * @task exutil + */ + public static function getAllExceptions(Exception $ex) { + if ($ex instanceof PhutilAggregateException) { + $exceptions = $ex->getExceptions(); + array_unshift($exceptions, $ex); + } else { + $exceptions = array(); + + do { + $exceptions[] = $ex; + } while ($ex = self::getPreviousException($ex)); + } + + return new ArrayIterator($exceptions); + } + + /** * Gets the previous exception of a nested exception. Prior to PHP 5.3 you * can use @{class:PhutilProxyException} to nest exceptions; after PHP 5.3 * all exceptions are nestable. @@ -365,9 +387,11 @@ case self::EXCEPTION: $messages = array(); $current = $value; - do { - $messages[] = '('.get_class($current).') '.$current->getMessage(); - } while ($current = self::getPreviousException($current)); + + foreach (self::getAllExceptions($current) as $ex) { + $messages[] = '('.get_class($ex).') '.$ex->getMessage(); + } + $messages = implode(' {>} ', $messages); if (strlen($messages) > 4096) { @@ -384,6 +408,13 @@ $metadata['default_message'] = $default_message; error_log($default_message); self::outputStacktrace(self::getRootException($value)->getTrace()); + + if ($value instanceof PhutilAggregateException) { + foreach ($current->getExceptions() as $ex) { + self::outputStacktrace($ex->getTrace()); + } + } + break; case self::PHLOG: $default_message = sprintf(