Skip to content

Commit 5aec88a

Browse files
committed
Add strategies to extract debug information
Depending on the application mode, we may need extra debugging information in the response body - which can definitely help us to solve the issue more easily. This provides the extension point and two default implementations: - `NoDebugInfo`: aimed for production mode, doesn't add any debug information - `NoTrace`: aimed for development mode, extracts all exception data but the trace - also for previous exceptions.
1 parent 60ed368 commit 5aec88a

File tree

5 files changed

+198
-0
lines changed

5 files changed

+198
-0
lines changed

src/DebugInfoStrategy.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Lcobucci\ErrorHandling;
5+
6+
use Throwable;
7+
8+
interface DebugInfoStrategy
9+
{
10+
/**
11+
* @return array<string, mixed>|null
12+
*/
13+
public function extractDebugInfo(Throwable $error): ?array;
14+
}

src/DebugInfoStrategy/NoDebugInfo.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Lcobucci\ErrorHandling\DebugInfoStrategy;
5+
6+
use Lcobucci\ErrorHandling\DebugInfoStrategy;
7+
use Throwable;
8+
9+
final class NoDebugInfo implements DebugInfoStrategy
10+
{
11+
/**
12+
* {@inheritDoc}
13+
*/
14+
public function extractDebugInfo(Throwable $error): ?array
15+
{
16+
return null;
17+
}
18+
}

src/DebugInfoStrategy/NoTrace.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Lcobucci\ErrorHandling\DebugInfoStrategy;
5+
6+
use Generator;
7+
use Lcobucci\ErrorHandling\DebugInfoStrategy;
8+
use Throwable;
9+
use function get_class;
10+
use function iterator_to_array;
11+
12+
final class NoTrace implements DebugInfoStrategy
13+
{
14+
/**
15+
* {@inheritDoc}
16+
*/
17+
public function extractDebugInfo(Throwable $error): ?array
18+
{
19+
$debugInfo = $this->format($error);
20+
$stack = iterator_to_array($this->streamStack($error->getPrevious()), false);
21+
22+
if ($stack !== []) {
23+
$debugInfo['stack'] = $stack;
24+
}
25+
26+
return $debugInfo;
27+
}
28+
29+
private function streamStack(?Throwable $previous): Generator
30+
{
31+
if ($previous === null) {
32+
return;
33+
}
34+
35+
yield $this->format($previous);
36+
yield from $this->streamStack($previous->getPrevious());
37+
}
38+
39+
/**
40+
* @return array<string, string|int>
41+
*/
42+
private function format(Throwable $error): array
43+
{
44+
return [
45+
'class' => get_class($error),
46+
'code' => $error->getCode(),
47+
'message' => $error->getMessage(),
48+
'file' => $error->getFile(),
49+
'line' => $error->getLine(),
50+
];
51+
}
52+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Lcobucci\ErrorHandling\Tests\DebugInfoStrategy;
5+
6+
use Lcobucci\ErrorHandling\DebugInfoStrategy\NoDebugInfo;
7+
use PHPUnit\Framework\TestCase;
8+
use RuntimeException;
9+
10+
/**
11+
* @coversDefaultClass \Lcobucci\ErrorHandling\DebugInfoStrategy\NoDebugInfo
12+
*/
13+
final class NoDebugInfoTest extends TestCase
14+
{
15+
/**
16+
* @test
17+
*
18+
* @covers ::extractDebugInfo
19+
*/
20+
public function extractDebugInfoShouldAlwaysReturnNull(): void
21+
{
22+
$strategy = new NoDebugInfo();
23+
24+
self::assertNull($strategy->extractDebugInfo(new RuntimeException()));
25+
}
26+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Lcobucci\ErrorHandling\Tests\DebugInfoStrategy;
5+
6+
use InvalidArgumentException;
7+
use Lcobucci\ErrorHandling\DebugInfoStrategy\NoTrace;
8+
use LogicException;
9+
use PHPUnit\Framework\TestCase;
10+
use RuntimeException;
11+
12+
/**
13+
* @coversDefaultClass \Lcobucci\ErrorHandling\DebugInfoStrategy\NoTrace
14+
*/
15+
final class NoTraceTest extends TestCase
16+
{
17+
/**
18+
* @test
19+
*
20+
* @covers ::extractDebugInfo
21+
* @covers ::format
22+
* @covers ::streamStack
23+
*/
24+
public function extractDebugInfoShouldConvertExceptionInfoWithoutTheTrace(): void
25+
{
26+
$strategy = new NoTrace();
27+
28+
self::assertSame(
29+
[
30+
'class' => RuntimeException::class,
31+
'code' => 11,
32+
'message' => 'Testing',
33+
'file' => __FILE__,
34+
'line' => __LINE__ + 2,
35+
],
36+
$strategy->extractDebugInfo(new RuntimeException('Testing', 11))
37+
);
38+
}
39+
40+
/**
41+
* @test
42+
*
43+
* @covers ::extractDebugInfo
44+
* @covers ::format
45+
* @covers ::streamStack
46+
*/
47+
public function extractDebugInfoShouldAlsoReturnPreviousExceptions(): void
48+
{
49+
$strategy = new NoTrace();
50+
51+
self::assertSame(
52+
[
53+
'class' => RuntimeException::class,
54+
'code' => 11,
55+
'message' => 'Testing',
56+
'file' => __FILE__,
57+
'line' => __LINE__ + 19,
58+
'stack' => [
59+
[
60+
'class' => InvalidArgumentException::class,
61+
'code' => 25,
62+
'message' => 'Oh no!',
63+
'file' => __FILE__,
64+
'line' => __LINE__ + 15,
65+
],
66+
[
67+
'class' => LogicException::class,
68+
'code' => 0,
69+
'message' => 'Bummer',
70+
'file' => __FILE__,
71+
'line' => __LINE__ + 11,
72+
],
73+
],
74+
],
75+
$strategy->extractDebugInfo(
76+
new RuntimeException(
77+
'Testing',
78+
11,
79+
new InvalidArgumentException(
80+
'Oh no!',
81+
25,
82+
new LogicException('Bummer')
83+
)
84+
)
85+
)
86+
);
87+
}
88+
}

0 commit comments

Comments
 (0)