eboreum/exceptional

轻松创建和格式化PHP异常。自动展开方法参数。确保敏感字符串,如密码、令牌、PHPSESSID等,被屏蔽,因此在结果文本中显示为例如“******”。

1.0.0 2022-04-22 10:00 UTC

This package is auto-updated.

Last update: 2024-09-24 17:50:03 UTC


README

license build Code Coverage PHPStan Level

轻松创建和格式化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创建一个工单或者拉取请求,并在这里讨论功能或错误。

鸣谢

作者