Skip to content

Commit 61f99a4

Browse files
committed
Change how to stringify throwable objects
I'm matching the recent format changes, using "{}" to represent objects. In the case of throwables, I thought it would make more sense to have the message and the location, which is the exact behavior of the first line of the output of the `__toString()` method of an exception. That means we no longer include the exception code. Also, for optimization, I'm changing the stringifier that the `ThrowableObjectStringifier` uses because I know it only stringifies a string, hence using the `JsonEncodableStringifier` improves performance as we don't need to call all other stringifies. Signed-off-by: Henrique Moody <[email protected]>
1 parent b0eb786 commit 61f99a4

File tree

5 files changed

+131
-119
lines changed

5 files changed

+131
-119
lines changed

src/Stringifiers/ClusterStringifier.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public static function createDefault(): self
4343
$stringifier->setStringifiers([
4444
new IteratorObjectStringifier($stringifier, $quoter),
4545
new DateTimeStringifier($quoter, DateTimeInterface::ATOM),
46-
new ThrowableStringifier($stringifier, $quoter),
46+
new ThrowableObjectStringifier($jsonEncodableStringifier, $quoter),
4747
new StringableObjectStringifier($jsonEncodableStringifier, $quoter),
4848
new JsonSerializableObjectStringifier($jsonEncodableStringifier, $quoter),
4949
new ObjectStringifier($stringifier, $quoter),

src/Stringifiers/ThrowableStringifier.php renamed to src/Stringifiers/ThrowableObjectStringifier.php

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
use function sprintf;
1919
use function str_replace;
2020

21-
final class ThrowableStringifier implements Stringifier
21+
final class ThrowableObjectStringifier implements Stringifier
2222
{
2323
public function __construct(
2424
private readonly Stringifier $stringifier,
@@ -32,29 +32,26 @@ public function stringify(mixed $raw, int $depth): ?string
3232
return null;
3333
}
3434

35+
if ($raw->getMessage() === '') {
36+
return $this->quoter->quote(
37+
sprintf('%s { in %s }', $raw::class, $this->getSource($raw)),
38+
$depth
39+
);
40+
}
41+
3542
return $this->quoter->quote(
3643
sprintf(
37-
'[throwable] (%s: %s)',
44+
'%s { %s in %s }',
3845
$raw::class,
39-
$this->stringifier->stringify($this->getData($raw), $depth + 1)
46+
$this->stringifier->stringify($raw->getMessage(), $depth + 1),
47+
$this->getSource($raw),
4048
),
4149
$depth
4250
);
4351
}
4452

45-
/**
46-
* @return mixed[]
47-
*/
48-
private function getData(Throwable $throwable): array
53+
private function getSource(Throwable $throwable): string
4954
{
50-
return [
51-
'message' => $throwable->getMessage(),
52-
'code' => $throwable->getCode(),
53-
'file' => sprintf(
54-
'%s:%d',
55-
str_replace(getcwd() . '/', '', $throwable->getFile()),
56-
$throwable->getLine()
57-
),
58-
];
55+
return str_replace(getcwd() . '/', '', $throwable->getFile()) . ':' . $throwable->getLine();
5956
}
6057
}

tests/integration/stringify-object-throwable.phpt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ declare(strict_types=1);
66
require 'vendor/autoload.php';
77

88
output(new InvalidArgumentException());
9+
output(new InvalidArgumentException('My exception message'));
910
?>
1011
--EXPECTF--
11-
`[throwable] (InvalidArgumentException: ["message": "", "code": 0, "file": "%s:%d"])`
12+
`InvalidArgumentException { in %s:%d }`
13+
`InvalidArgumentException { "My exception message" in %s:%d }`
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
/*
4+
* This file is part of Respect/Stringifier.
5+
* Copyright (c) Henrique Moody <[email protected]>
6+
* SPDX-License-Identifier: MIT
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
namespace Respect\Stringifier\Test\Unit\Stringifiers;
12+
13+
use DomainException;
14+
use Error;
15+
use ErrorException;
16+
use Exception;
17+
use InvalidArgumentException;
18+
use PHPUnit\Framework\Attributes\CoversClass;
19+
use PHPUnit\Framework\Attributes\DataProvider;
20+
use PHPUnit\Framework\Attributes\Test;
21+
use PHPUnit\Framework\TestCase;
22+
use Respect\Stringifier\Stringifiers\ThrowableObjectStringifier;
23+
use Respect\Stringifier\Test\Double\FakeQuoter;
24+
use Respect\Stringifier\Test\Double\FakeStringifier;
25+
use RuntimeException;
26+
use stdClass;
27+
use Throwable;
28+
use TypeError;
29+
30+
use function sprintf;
31+
32+
#[CoversClass(ThrowableObjectStringifier::class)]
33+
final class ThrowableObjectStringifierTest extends TestCase
34+
{
35+
private const DEPTH = 0;
36+
37+
#[Test]
38+
public function isShouldNotStringifyRawValueWhenItIsNotThrowable(): void
39+
{
40+
$sut = new ThrowableObjectStringifier(new FakeStringifier(), new FakeQuoter());
41+
42+
self::assertNull($sut->stringify(new stdClass(), self::DEPTH));
43+
}
44+
45+
#[Test]
46+
#[DataProvider('throwableWithMessageProvider')]
47+
public function itShouldStringifyRawValueWhenItIsThrowableWithMessage(Throwable $raw): void
48+
{
49+
$stringifier = new FakeStringifier();
50+
$quoter = new FakeQuoter();
51+
52+
$sut = new ThrowableObjectStringifier($stringifier, $quoter);
53+
54+
$actual = $sut->stringify($raw, self::DEPTH);
55+
$expectedValue = $quoter->quote(
56+
sprintf(
57+
'%s { %s in tests/unit/Stringifiers/ThrowableObjectStringifierTest.php:%d }',
58+
$raw::class,
59+
$stringifier->stringify($raw->getMessage(), self::DEPTH + 1),
60+
$raw->getLine(),
61+
),
62+
self::DEPTH
63+
);
64+
65+
self::assertSame($expectedValue, $actual);
66+
}
67+
68+
#[Test]
69+
#[DataProvider('throwableWithoutMessageProvider')]
70+
public function itShouldStringifyRawValueWhenItIsThrowableWithoutMessage(Throwable $raw): void
71+
{
72+
$stringifier = new FakeStringifier();
73+
$quoter = new FakeQuoter();
74+
75+
$sut = new ThrowableObjectStringifier($stringifier, $quoter);
76+
77+
$actual = $sut->stringify($raw, self::DEPTH);
78+
$expectedValue = $quoter->quote(
79+
sprintf(
80+
'%s { in tests/unit/Stringifiers/ThrowableObjectStringifierTest.php:%d }',
81+
$raw::class,
82+
$raw->getLine(),
83+
),
84+
self::DEPTH
85+
);
86+
87+
self::assertSame($expectedValue, $actual);
88+
}
89+
90+
/**
91+
* @return array<array{0: Throwable}>
92+
*/
93+
public static function throwableWithMessageProvider(): array
94+
{
95+
return [
96+
[new Exception('Message for Exception')],
97+
[new ErrorException('Message for ErrorException')],
98+
[new Error('Message for Error')],
99+
[new TypeError('Message for TypeError')],
100+
];
101+
}
102+
103+
/**
104+
* @return array<array{0: Throwable}>
105+
*/
106+
public static function throwableWithoutMessageProvider(): array
107+
{
108+
return [
109+
[new InvalidArgumentException()],
110+
[new RuntimeException()],
111+
[new DomainException()],
112+
];
113+
}
114+
}

tests/unit/Stringifiers/ThrowableStringifierTest.php

Lines changed: 0 additions & 101 deletions
This file was deleted.

0 commit comments

Comments
 (0)