ju1ius / footprints

PHP 的可过滤回溯

0.2 2023-01-17 16:00 UTC

This package is auto-updated.

Last update: 2024-09-17 20:17:48 UTC


README

codecov

PHP 的可过滤回溯

安装

composer require ju1ius/footprints

使用

捕获堆栈跟踪

use ju1ius\Footprints\Backtrace;

// Capture the current stack trace
$trace = Backtrace::capture();
// retrieve the array of stack frames
$frames = $trace->frames();

// Capture the current stack trace, skipping the current stack frame
$trace = Backtrace::capture(1);

// Second argument is the flags for \debug_backtrace()
$trace = Backtrace::capture(0, Backtrace::PROVIDE_OBJECT|Backtrace::IGNORE_ARGS);

// You can capture error/exception traces too
try {
    // ...
} catch (\Throwable $err) {
    // Capture the exception trace, skipping the two topmost frames
    $trace = Backtrace::captureThrowable($err, 2);
}

过滤堆栈跟踪

要过滤回溯,提供了 Backtrace::accept()Backtrace::reject() 方法,这两个方法都接受一个 callable 断言并返回一个过滤后的 Backtrace 对象。

$predicate 参数具有以下签名: callable(Frame, int, Frame[]): bool:它接收一个 Frame,它的索引和整个帧数组,并返回一个布尔值,表示断言是否匹配。

Backtrace::accept() 将接受(保留)返回真值的结果的帧,而 Backtrace::reject() 将拒绝(过滤掉)返回真值的结果的帧。

use ju1ius\Footprints\Backtrace;
use ju1ius\Footprints\Frame;

// Keep only frames for:
// * the top-level foo() function
// * any method named foo() regardless of it's class.
$trace = Backtrace::capture()
    ->accept(fn(Frame $frame) => $frame->function === 'foo');

// Filters out frames for:
// * the top-level foo() function
// * any method named foo() regardless of it's class.
$trace = Backtrace::capture()
    ->reject(fn(Frame $frame) => $frame->function === 'foo');

为了方便,这个库提供了一些内置的断言。

内置断言

isFunction(string ...$functionNames)

use ju1ius\Footprints\Backtrace;
use ju1ius\Footprints\Predicate;

$trace = Backtrace::capture()->reject(Predicate::isFunction(
    'foo',
    'Acme\\foobar',
));

isClass(string ...$classNames)

use ju1ius\Footprints\Backtrace;
use ju1ius\Footprints\Predicate;

$trace = Backtrace::capture()->reject(Predicate::isClass(
    'Foo',
    'Acme\\FooBar',
));

isMethod(string ...$methodNames)

use ju1ius\Footprints\Backtrace;
use ju1ius\Footprints\Predicate;

$trace = Backtrace::capture()->reject(Predicate::isMethod(
    // rejects method `bar` of class `Foo`
    'Foo->bar',
    // rejects static method `baz` of class `Acme\FooBar`
    'Acme\\FooBar::baz',
));

isNamespace(string ...$namespaces)

use ju1ius\Footprints\Backtrace;
use ju1ius\Footprints\Predicate;

$trace = Backtrace::capture()->reject(Predicate::isNamespace(
    // rejects everything in namespace `Acme\Foo` and all it's sub-namespaces.
    'Acme\\Foo',
));

isFile(string ...$globPatterns)

IsFile 断言接受 fnmatch 语法接受的 glob 模式。

use ju1ius\Footprints\Backtrace;
use ju1ius\Footprints\Predicate;

$trace = Backtrace::capture()->reject(Predicate::isFile(
    // rejects everything in `/src/foo.php`
    '/src/foo.php',
    // rejects everything in the `/vendor` directory
    '/vendor/*',
    // rejects files having a `.inc.php` extension
    '*.inc.php',
));

断言组合

可以使用 Predicate::and()Predicate::or()Predicate::not() 断言组合断言。

use ju1ius\Footprints\Backtrace;
use ju1ius\Footprints\Frame;
use ju1ius\Footprints\Predicate;

// The following filters out:
// * Foo::bar() and Bar::bar() methods (whether static or not)
// * top-level baz() and qux() functions
$trace = Backtrace::capture()->reject(Predicate::or(
    Predicate::and(
        fn(Frame $frame) => \in_array($frame->class, ['Foo', 'Bar']), 
        fn(Frame $frame) => $frame->function === 'bar', 
    ),
    Predicate::isFunction('baz', 'qux'),
));