eboreum / exceptional
轻松创建和格式化PHP异常。自动展开方法参数。确保敏感字符串,如密码、令牌、PHPSESSID等,被屏蔽,因此在结果文本中显示为例如“******”。
Requires
- php: ^8.1
- eboreum/caster: ^1.0
Requires (Dev)
- nette/neon: ^3.2
- phpstan/phpstan: ^1.4
- phpunit/phpunit: ^9.5
- sebastian/diff: ^4.0
README
轻松创建和格式化PHP异常。自动展开方法参数。确保敏感字符串,如密码、令牌、PHPSESSID等,被屏蔽,因此在结果文本中显示为例如“******”。
当方法被调用,并且 somehow 这导致异常/可抛出异常被引发时,知道方法被调用时所用的所有参数不是很好吗?Exceptional 可以为你解开这个谜团,并以简洁和有意义的方式呈现这些参数及其相应的名称。此外,与 Eboreum/Caster (https://packagist.org.cn/packages/eboreum/caster) 的集成允许揭示异常/错误发生的对象中的信息。这些信息有时非常有价值且至关重要,对于调试来说非常出色。
要求
"php": "^8.1", "eboreum/caster": "^1.0"
有关更多信息,请参阅 composer.json
文件。
安装
通过 Composer (https://packagist.org.cn/packages/eboreum/exceptional)
composer install eboreum/exceptional
通过 GitHub
git clone git@github.com:eboreum/exceptional.git
基础
异常消息生成
示例 1:基础知识
示例
<?php use Eboreum\Exceptional\ExceptionMessageGenerator; class Foo377464ece90d4b918254101d596d90a8 { /** * @throws \RuntimeException */ public function bar(int $a, bool $b, ?string $c = null): string { throw new \RuntimeException(ExceptionMessageGenerator::getInstance()->makeFailureInMethodMessage( $this, new \ReflectionMethod(self::class, __FUNCTION__), func_get_args(), )); } }; $foo = new Foo377464ece90d4b918254101d596d90a8; try { $foo->bar(42, true); } catch (\RuntimeException $e) { echo $e->getMessage() . PHP_EOL; }
输出
Failure in \Foo377464ece90d4b918254101d596d90a8->bar($a = (int) 42, $b = (bool) true, $c = (null) null) inside (object) \Foo377464ece90d4b918254101d596d90a8
注意每个参数都与从 func_get_args()
函数获取的相应值配对。参数 $c
甚至收到了其默认值,这是 func_get_args()
不会 返回的。
示例 2:提供的参数多于命名参数
示例
<?php use Eboreum\Exceptional\ExceptionMessageGenerator; class Foo1ff07b0e563e4efbb5a5280f7fe412d8 { /** * @throws \RuntimeException */ public function bar(int $a, bool $b): string { throw new \RuntimeException(ExceptionMessageGenerator::getInstance()->makeFailureInMethodMessage( $this, new \ReflectionMethod(self::class, __FUNCTION__), func_get_args(), )); } }; $foo = new Foo1ff07b0e563e4efbb5a5280f7fe412d8; try { $foo->bar(42, true, null, 'hello'); } catch (\RuntimeException $e) { echo $e->getMessage() . PHP_EOL; }
输出
Failure in \Foo1ff07b0e563e4efbb5a5280f7fe412d8->bar($a = (int) 42, $b = (bool) true, {2} = (null) null, {3} = (string(5)) "hello") inside (object) \Foo1ff07b0e563e4efbb5a5280f7fe412d8
注意 $a
和 $b
已命名,但未命名的参数已接收到它们各自的索引,即 {2}
和 {3}
。
示例 3:默认值为常量
示例
<?php use Eboreum\Exceptional\ExceptionMessageGenerator; class Fooaea91664ed3d4467aeb2dfabb2623b53 { const SOME_PARENT_CONSTANT = 42; } class Fooc261bae9da674d679de77a943ae57779 extends Fooaea91664ed3d4467aeb2dfabb2623b53 { const SOME_CONSTANT = 3.14; /** * @throws \RuntimeException */ public function bar( float $a = self::SOME_CONSTANT, int $b = self::SOME_PARENT_CONSTANT, int $c = PHP_INT_MAX ): void { throw new \RuntimeException(ExceptionMessageGenerator::getInstance()->makeFailureInMethodMessage( $this, new \ReflectionMethod(self::class, __FUNCTION__), func_get_args(), )); } }; $foo = new Fooc261bae9da674d679de77a943ae57779; try { $foo->bar(); } catch (\RuntimeException $e) { echo $e->getMessage() . PHP_EOL; }
输出
Failure in \Fooc261bae9da674d679de77a943ae57779->bar($a = (float) 3.14, $b = (int) 42, $c = (int) 9223372036854775807) inside (object) \Fooc261bae9da674d679de77a943ae57779
参数 $a
已从类常量 Fooc261bae9da674d679de77a943ae57779::SOME_CONSTANT
接收到其默认值,$b
已从类常量 Fooaea91664ed3d4467aeb2dfabb2623b53::SOME_PARENT_CONSTANT
接收到其默认值,$c
已从全局常量 GLOBAL_CONSTANT_25b105757d32443188cca9c7646ccfe6
接收到其默认值。
示例 4:静态方法调用
示例
<?php use Eboreum\Exceptional\ExceptionMessageGenerator; class Foo1a7c13d6ce9f4646a120041e36717d5a { /** * @throws \RuntimeException */ public static function bar(int $a): string { throw new \RuntimeException(ExceptionMessageGenerator::getInstance()->makeFailureInMethodMessage( static::class, new \ReflectionMethod(self::class, __FUNCTION__), func_get_args(), )); } }; try { Foo1a7c13d6ce9f4646a120041e36717d5a::bar(42); } catch (\RuntimeException $e) { echo $e->getMessage() . PHP_EOL; }
输出
Failure in \Foo1a7c13d6ce9f4646a120041e36717d5a::bar($a = (int) 42) inside (class) \Foo1a7c13d6ce9f4646a120041e36717d5a
注意,在这种情况下,使用的是 static::class
而不是 $this
。
示例 5:使用 caster 使对象描述详细
如果我们能够,除了方法参数窃听之外,还能获取有关方法失败的对象的更多信息,那岂不是很好?我们可以使用 Eboreum\Caster\Caster
集成来实现这一点。
示例
<?php use Eboreum\Caster\Attribute\DebugIdentifier; use Eboreum\Caster\Collection\Formatter\ObjectFormatterCollection; use Eboreum\Caster\Contract\CasterInterface; use Eboreum\Caster\Contract\TextuallyIdentifiableInterface; use Eboreum\Caster\Contract\DebugIdentifierAttributeInterface; use Eboreum\Caster\Formatter\Object_\DebugIdentifierAttributeInterfaceFormatter; use Eboreum\Caster\Formatter\Object_\TextuallyIdentifiableInterfaceFormatter; use Eboreum\Exceptional\Caster; use Eboreum\Exceptional\ExceptionMessageGenerator; // Using TextuallyIdentifiableInterface class Foo1990801ff8324df1b73e323d7fca71a8 implements TextuallyIdentifiableInterface { protected int $id = 42; /** * @throws \RuntimeException */ public function bar(int $a): string { $caster = Caster::getInstance(); $caster = $caster->withCustomObjectFormatterCollection(new ObjectFormatterCollection([ new TextuallyIdentifiableInterfaceFormatter(), ])); $exceptionMessageGenerator = ExceptionMessageGenerator::getInstance()->withCaster($caster); throw new \RuntimeException($exceptionMessageGenerator->makeFailureInMethodMessage( $this, new \ReflectionMethod(self::class, __FUNCTION__), func_get_args(), )); } /** * {@inheritDoc} */ public function toTextualIdentifier(CasterInterface $caster): string { return sprintf( 'My ID is: %d', $this->id, ); } }; $foo = new Foo1990801ff8324df1b73e323d7fca71a8; try { $foo->bar(7); } catch (\RuntimeException $e) { echo $e->getMessage() . PHP_EOL; } /** * Using DebugIdentifierAttributeInterface */ class Foo31eda25b57e8456fb2b3e8158232b5e5 implements DebugIdentifierAttributeInterface { #[DebugIdentifier] protected int $id = 42; /** * @throws \RuntimeException */ public function bar(int $a): string { $caster = Caster::getInstance(); $caster = $caster->withCustomObjectFormatterCollection(new ObjectFormatterCollection([ new DebugIdentifierAttributeInterfaceFormatter(), ])); $exceptionMessageGenerator = ExceptionMessageGenerator::getInstance()->withCaster($caster); throw new \RuntimeException($exceptionMessageGenerator->makeFailureInMethodMessage( $this, new \ReflectionMethod(self::class, __FUNCTION__), func_get_args(), )); } }; $foo = new Foo31eda25b57e8456fb2b3e8158232b5e5; try { $foo->bar(7); } catch (\RuntimeException $e) { echo $e->getMessage() . PHP_EOL; }
输出
Failure in \Foo1990801ff8324df1b73e323d7fca71a8->bar($a = (int) 7) inside (object) \Foo1990801ff8324df1b73e323d7fca71a8: My ID is: 42
Failure in \Foo31eda25b57e8456fb2b3e8158232b5e5->bar($a = (int) 7) inside (object) \Foo31eda25b57e8456fb2b3e8158232b5e5 {$id = (int) 42}
注意,现在我们可以从上述对象中获取有用的信息,其 ID 为 42(并且参数 $a
为 7)。
为了使上述功能正常工作,必须在 makeFailureInMethodMessage
调用中使用 $this
作为参数(而不是 static::class
)。
异常格式化器
示例 1:默认格式化器
类:Eboreum\Exceptional\Formatting\DefaultFormatter
纯文本格式化器。包含换行符和缩进。
<?php use Eboreum\Exceptional\Caster; use Eboreum\Exceptional\Formatting\DefaultFormatter; $caster = Caster::getInstance(); $defaultFormatter = new DefaultFormatter($caster); $throwable = new \Exception('foo'); $result = $defaultFormatter->format($throwable); echo $result;
输出
\Exception
Message:
foo
File: /some/file/path/script/misc/readme/formatter/example-1-defaultformatter.php
Line: 13
Code: 0\nStacktrace:\n #0 /path/to/some/file.php:34: fake_function()\nPrevious: (None)
示例 2:HTML5 <table>
格式化器
类:Eboreum\Exceptional\Formatting\HTML5TableFormatter
将可抛出异常格式化为 HTML5 <table>
。
<?php use Eboreum\Caster\CharacterEncoding; use Eboreum\Exceptional\Caster; use Eboreum\Exceptional\Formatting\HTML5TableFormatter; $caster = Caster::getInstance(); $characterEncoding = new CharacterEncoding('UTF-8'); $html5TableFormatter = new HTML5TableFormatter($caster, $characterEncoding); $html5TableFormatter = $html5TableFormatter->withIsPrettyPrinting(true); $throwable = new \Exception('foo'); $result = $html5TableFormatter->format($throwable); echo $result;
输出
<table>
<tbody>
<tr>
<td colspan="2">
<h1>\Exception</h1>
</td>
</tr>
<tr>
<td>Message:</td>
<td>foo</td>
</tr>
<tr>
<td>File:</td>
<td>/some/file/path/script/misc/readme/formatter/example-2-html5tableformatter.php</td>
</tr>
<tr>
<td>Line:</td>
<td>16</td>
</tr>
<tr>
<td>Code:</td>
<td>0</td>
</tr>
<tr>
<td>Stacktrace:</td>
<td>
<pre>#0 /path/to/some/file.php:34: fake_function()</pre>
</td>
</tr>
<tr>
<td>Previous:</td>
<td>(None)</td>
</tr>
</tbody>
</table>
示例 3:JSON 格式化器
类:Eboreum\Exceptional\Formatting\JSONFormatter
将可抛出异常格式化为 JSON。
<?php use Eboreum\Caster\CharacterEncoding; use Eboreum\Exceptional\Caster; use Eboreum\Exceptional\Formatting\JSONFormatter; $caster = Caster::getInstance(); $characterEncoding = new CharacterEncoding('UTF-8'); $jsonFormatter = new JSONFormatter($caster, $characterEncoding); $jsonFormatter = $jsonFormatter->withFlags(JSON_PRETTY_PRINT); $throwable = new \Exception('foo'); $result = $jsonFormatter->format($throwable); echo $result;
输出
{
"class": "\\Exception",
"file": "\/some\/file\/path\/script\/misc\/readme\/formatter\/example-3-jsonformatter.php",
"line": "16",
"code": "0",
"message": "foo",
"stacktrace": "#0 \/path\/to\/some\/file.php:34: fake_function()"
"previous": null
}
示例 4:单行格式化器
类:Eboreum\Exceptional\Formatting\OnelineFormatter
将可抛出异常格式化为字符串,其所有内容都在一行上。非常适合(改进的)错误日志输出,因为不允许换行符。
<?php use Eboreum\Caster\CharacterEncoding; use Eboreum\Exceptional\Caster; use Eboreum\Exceptional\Formatting\OnelineFormatter; $caster = Caster::getInstance(); $onelineFormatter = new OnelineFormatter($caster); $throwable = new \Exception('foo'); $result = $onelineFormatter->format($throwable); echo $result;
输出
\Exception. Message: foo. File: /some/file/path/script/misc/readme/formatter/example-4-onelineformatter.php. Line: 14. Code: 0. Stacktrace: #0 /path/to/some/file.php:34: fake_function(). Previous: (None)
示例 5:XML 格式化器
类:Eboreum\Exceptional\Formatting\XMLFormatter
将可抛出异常格式化为 XML。
<?php use Eboreum\Caster\CharacterEncoding; use Eboreum\Exceptional\Caster; use Eboreum\Exceptional\Formatting\XMLFormatter; $caster = Caster::getInstance(); $characterEncoding = new CharacterEncoding('UTF-8'); $xmlFormatter = new XMLFormatter($caster, $characterEncoding); $xmlFormatter = $xmlFormatter->withIsPrettyPrinting(true); $throwable = new \Exception('foo'); $result = $xmlFormatter->format($throwable); echo $result;
输出
<?xml version="1.0" encoding="UTF-8"?>
<exception>
<class>\Exception</class>
<file>/some/file/path/script/misc/readme/formatter/example-5-xmlformatter.php</file>
<line>16</line>
<code>0</code>
<message>foo</message>
<stacktrace>#0 /path/to/some/file.php:34: fake_function()</stacktrace>
<previous/>
</exception>
测试/开发要求
"nette/neon": "^3.2", "phpstan/phpstan": "^1.4", "phpunit/phpunit": "^9.5", "sebastian/diff": "^4.0"
运行测试
对于所有单元测试,首先遵循以下步骤:
cd tests
php ../vendor/bin/phpunit
许可和免责声明
请参阅LICENSE
文件。基本上:使用此库风险自负。
贡献
我们更倾向于您在https://github.com/eboreum/exceptional创建一个工单或者拉取请求,并在这里讨论功能或错误。
鸣谢
作者
- Kasper Søfren (kafoso)
电子邮件: soefritz@gmail.com
个人主页: https://github.com/kafoso - Carsten Jørgensen (corex)
电子邮件: dev@corex.dk
个人主页: https://github.com/corex