Skip to content

Commit 56df5a7

Browse files
authored
Merge pull request #87 from yiisoft/fix-bug
Support multiple exceptions, fixed a bug with try/finally
2 parents 72467ba + e0f96c2 commit 56df5a7

File tree

9 files changed

+910
-811
lines changed

9 files changed

+910
-811
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## 3.1.0 under development
44

5+
- New #87: Add `CompositeException` to be able to render multiple exceptions (@xepozz)
6+
- Bug #87: Fix a bug with try/finally from #75 (@xepozz)
57
- Chg #75: Dispatch `ApplicationError` in `ErrorCatcher` (@xepozz)
68
- Enh #82: Add `HeadersProvider` (@xepozz)
79

src/CompositeException.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\ErrorHandler;
6+
7+
use Exception;
8+
use Throwable;
9+
10+
/**
11+
* Aggregate multiple exceptions into one.
12+
*/
13+
final class CompositeException extends Exception
14+
{
15+
/**
16+
* @var Throwable[]
17+
*/
18+
private array $rest;
19+
20+
public function __construct(
21+
private Throwable $first,
22+
Throwable ...$rest,
23+
) {
24+
$this->rest = $rest;
25+
parent::__construct($first->getMessage(), (int) $first->getCode(), $first);
26+
}
27+
28+
public function getFirstException(): Throwable
29+
{
30+
return $this->first;
31+
}
32+
33+
/**
34+
* @return Throwable[]
35+
*/
36+
public function getPreviousExceptions(): array
37+
{
38+
return $this->rest;
39+
}
40+
}

src/ErrorHandler.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
use function error_get_last;
1717
use function error_reporting;
1818
use function function_exists;
19-
use function ini_set;
2019
use function http_response_code;
20+
use function ini_set;
2121
use function register_shutdown_function;
2222
use function set_error_handler;
2323
use function set_exception_handler;
@@ -59,9 +59,7 @@ public function handle(
5959
ThrowableRendererInterface $renderer = null,
6060
ServerRequestInterface $request = null
6161
): ErrorData {
62-
if ($renderer === null) {
63-
$renderer = $this->defaultRenderer;
64-
}
62+
$renderer ??= $this->defaultRenderer;
6563

6664
try {
6765
$this->logger->error((string) (new PlainTextRenderer())->renderVerbose($t, $request), ['throwable' => $t]);

src/Exception/ErrorException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ private function addXDebugTraceToFatalIfAvailable(): void
113113
$trace[] = $frame;
114114
}
115115

116-
$ref = new ReflectionProperty('Exception', 'trace');
116+
$ref = new ReflectionProperty(\Exception::class, 'trace');
117117
$ref->setAccessible(true);
118118
$ref->setValue($this, $trace);
119119
}

src/Middleware/ErrorCatcher.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
use Psr\Http\Server\MiddlewareInterface;
1414
use Psr\Http\Server\RequestHandlerInterface;
1515
use Throwable;
16-
use Yiisoft\ErrorHandler\Event\ApplicationError;
16+
use Yiisoft\ErrorHandler\CompositeException;
1717
use Yiisoft\ErrorHandler\ErrorHandler;
18+
use Yiisoft\ErrorHandler\Event\ApplicationError;
1819
use Yiisoft\ErrorHandler\HeadersProvider;
1920
use Yiisoft\ErrorHandler\Renderer\HeaderRenderer;
2021
use Yiisoft\ErrorHandler\Renderer\HtmlRenderer;
@@ -128,13 +129,14 @@ public function forceContentType(string $contentType): self
128129

129130
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
130131
{
131-
$t = null;
132132
try {
133133
return $handler->handle($request);
134134
} catch (Throwable $t) {
135-
$this->eventDispatcher?->dispatch(new ApplicationError($t));
136-
} finally {
137-
/** @psalm-suppress PossiblyNullArgument $t is set in catch() statement */
135+
try {
136+
$this->eventDispatcher?->dispatch(new ApplicationError($t));
137+
} catch (Throwable $e) {
138+
$t = new CompositeException($e, $t);
139+
}
138140
return $this->generateErrorResponse($t, $request);
139141
}
140142
}

src/Renderer/HtmlRenderer.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Psr\Http\Message\ServerRequestInterface;
1010
use RuntimeException;
1111
use Throwable;
12+
use Yiisoft\ErrorHandler\CompositeException;
1213
use Yiisoft\ErrorHandler\ErrorData;
1314
use Yiisoft\ErrorHandler\ThrowableRendererInterface;
1415
use Yiisoft\FriendlyException\FriendlyExceptionInterface;
@@ -32,9 +33,9 @@
3233
use function mb_strlen;
3334
use function mb_substr;
3435
use function ob_clean;
36+
use function ob_end_clean;
3537
use function ob_get_clean;
3638
use function ob_get_level;
37-
use function ob_end_clean;
3839
use function ob_implicit_flush;
3940
use function ob_start;
4041
use function realpath;
@@ -212,9 +213,17 @@ public function parseMarkdown(string $content): string
212213
*/
213214
public function renderPreviousExceptions(Throwable $t): string
214215
{
215-
if (($previous = $t->getPrevious()) !== null) {
216-
$templatePath = $this->defaultTemplatePath . '/_previous-exception.php';
217-
return $this->renderTemplate($templatePath, ['throwable' => $previous]);
216+
$templatePath = $this->defaultTemplatePath . '/_previous-exception.php';
217+
218+
if ($t instanceof CompositeException) {
219+
$result = [];
220+
foreach ($t->getPreviousExceptions() as $exception) {
221+
$result[] = $this->renderTemplate($templatePath, ['throwable' => $exception]);
222+
}
223+
return implode($result);
224+
}
225+
if ($t->getPrevious() !== null) {
226+
return $this->renderTemplate($templatePath, ['throwable' => $t->getPrevious()]);
218227
}
219228

220229
return '';

0 commit comments

Comments
 (0)