Search by

php-forge / debug

terabytesoftw

Framework-neutral contracts and declarative presentation models for debugger extensions.

Package info

github.com/php-forge/debug

pkg:composer/php-forge/debug

Statistics

Installs: 849

Dependents: 7

Suggesters: 0

Stars: 1

Open Issues: 0

0.1.1 2026-09-13 12:56 UTC

This package is auto-updated.

Last update: 2026-09-13 12:57:42 UTC


README

PHP Forge

Debug


PHPUnit Mutation Testing PHPStan Security

Framework-neutral contracts for portable collectors and panels rendered by the debugger frontend.

Installation

composer require php-forge/debug:^0.1

The package ships contracts and presentation models only. Its sole requirement is PHP 8.3: it pulls in no framework, HTML library, or debugger engine, and installing it activates nothing. A host such as php-forge/debug-core renders whatever an extension declares through these contracts.

A complete panel in two classes

A collector buffers diagnostics while the request runs; a panel turns the stored capture into a view. Neither imports a debugger engine, and both share one identifier.

use PHPForge\Debug\CollectorInterface;

final class CacheCollector implements CollectorInterface
{
    /**
     * @var list<array{string, string, string}>
     */
    private array $operations = [];
    private bool $started = false;

    public function capture(): array|null
    {
        return $this->started ? ['operations' => $this->operations] : null;
    }

    public function id(): string
    {
        return 'cache';
    }

    public function record(string $operation, string $key, string $result): void
    {
        if ($this->started) {
            $this->operations[] = [$operation, $key, $result];
        }
    }

    public function shutdown(): void
    {
        $this->started = false;
        $this->operations = [];
    }

    public function startup(): void
    {
        $this->started = true;
    }
}
use PHPForge\Debug\{ColumnStyle, Panel, PanelView};

final class CachePanel extends Panel
{
    protected const string ICON = 'db';
    protected const string ID = 'cache';
    protected const string TITLE = 'Cache';

    public function present(array $data): PanelView
    {
        $operations = is_array($data['operations'] ?? null) ? $data['operations'] : [];

        $view = PanelView::create()
            ->summary(count($operations) === 1 ? ' operation' : ' operations', count($operations))
            ->toolbar('Cache', count($operations))
            ->active($operations !== []);

        return $operations === []
            ? $view->emptyState('No cache operations', 'The cache was observed, but nothing happened.')
            : $view->table(
                ['Operation', 'Key', 'Result'],
                $operations,
                collapsible: true,
                styles: [1 => ColumnStyle::IDENTIFIER],
            );
    }
}

Call record() from the application service that already knows about the operation. capture() returns null when there is nothing to report, and an array otherwise: an empty array is an observed empty request, not absence. The host encodes that array strictly, so omit secrets and keep values JSON-encodable.

Register it

Merge these fragments into an application whose debugger is already enabled. The collector's id() and the panel's ID must match: that is how the host pairs a capture with its panel.

// Yii2: inside the YII_DEBUG guard. Declare 'modules' => [] in the application configuration so the offset stays
// typed under PHPStan level max; the guard then only fills in the debug entry.
$config['modules']['debug'] = [
    'class' => DebugModule::class,
    'collectors' => ['cache-operations' => new CacheCollector()],
    'panels' => ['cache-operations' => new CachePanel()],
];
// Yii3: return the extended registry from the application's development DI factory.
$collector = new CacheCollector();

$registry = $registry
    ->withCollector($collector)
    ->withPanel(new CachePanel());

Both hosts derive the IDs from the objects themselves. CollectorInterface is the only collector contract the debugger has, so the collector is registered as it is, and only the panel is adapted to the host's own panel type. No catalog entry, icon enum, storage dispatch entry, or change to an official package is needed. Inject the same $collector into the application service that calls record().

A runnable version of this example, capturing through PSR-3 instead of a direct call, lives in tests/Support; python3 tools/check-consumer.py installs it as an independent Composer package and replays a stored capture with no debugger host present.

Presentation vocabulary

Five types are published: CollectorInterface, Panel, PanelView, Tone, and ColumnStyle. Everything a panel can display is a PanelView method, so there is no value class to import and no shape to build by hand.

use PHPForge\Debug\{ColumnStyle, PanelView, Tone};

PanelView::create()
    ->summary(' props', 2)
    ->toolbar('Props', 2)
    ->heading('Props', section: true)
    ->overview(['Component' => 'Site', 'State' => PanelView::badge('shared', Tone::INFO)])
    ->paragraph('Rendered by ', PanelView::code('Inertia::render()'))
    ->callout(Tone::WARNING, 'Runtime inspection is unavailable.')
    ->table(['Prop', 'Value'], [['auth', PanelView::value(['id' => 1])]], styles: [0 => ColumnStyle::IDENTIFIER])
    ->group('Component', PanelView::create()->paragraph('Nested content'))
    ->emptyState('No operations', 'The cache was observed, but nothing happened.')
    ->disclosure('Raw payload', $json)
    ->active(true);

Plain scalars and null become text. PanelView::text(), ::strong(), ::code(), ::preview(), ::badge(), and ::value() produce validated inline values accepted wherever a scalar is accepted. Every method validates its arguments and rejects invalid input with an explicit InvalidArgumentException. The host reads the finished description through summaryMetrics(), toolbarMetrics(), blocks(), and isActive().

Documentation

Package information

PHP PHPStan Level Max Latest Stable Version Total Downloads

Code quality

Codecov Quality StyleCI

Social networks

Follow on X

License

License