memran/marwa-debugbar

A lightweight, framework-agnostic PHP debug bar with profiling capabilities

Maintainers

Package info

github.com/memran/marwa-debugbar

pkg:composer/memran/marwa-debugbar

Statistics

Installs: 279

Dependents: 2

Suggesters: 0

Stars: 0

Open Issues: 0

v1.1.0 2026-04-14 18:18 UTC

This package is auto-updated.

Last update: 2026-04-14 18:37:43 UTC


README

Latest Version on Packagist Total Downloads PHP Version CI Workflow PHPStan Level PHPUnit

A lightweight, framework-agnostic debug bar for PHP applications. It collects timing, logs, SQL queries, dumps, exceptions, request data, session data, and runtime KPIs without requiring a specific framework.

Why This Package

memran/marwa-debugbar is designed for plain PHP, small custom frameworks, and package-level integrations where you want:

  • a self-contained in-browser debug bar
  • explicit collector registration
  • no framework lock-in
  • low ceremony instrumentation
  • extension points for your own collectors

Use it for local development and diagnostics. Do not treat it as production monitoring.

Requirements

  • PHP 8.1+
  • ext-json

Optional integrations:

  • psr/log for forwarding logs to a PSR-3 logger
  • symfony/var-dumper for richer dump formatting
  • twig/twig for the optional Twig dump extension

Installation

composer require memran/marwa-debugbar --dev

Quick Start

<?php

require __DIR__ . '/vendor/autoload.php';

use Marwa\DebugBar\Collectors\AlertCollector;
use Marwa\DebugBar\Collectors\DbQueryCollector;
use Marwa\DebugBar\Collectors\ExceptionCollector;
use Marwa\DebugBar\Collectors\KpiCollector;
use Marwa\DebugBar\Collectors\LogCollector;
use Marwa\DebugBar\Collectors\MemoryCollector;
use Marwa\DebugBar\Collectors\PhpCollector;
use Marwa\DebugBar\Collectors\RequestCollector;
use Marwa\DebugBar\Collectors\TimelineCollector;
use Marwa\DebugBar\Collectors\VarDumperCollector;
use Marwa\DebugBar\DebugBar;
use Marwa\DebugBar\Renderer;

$bar = new DebugBar((getenv('DEBUGBAR_ENABLED') ?: '0') === '1');

$bar->collectors()->register(KpiCollector::class);
$bar->collectors()->register(AlertCollector::class);
$bar->collectors()->register(TimelineCollector::class);
$bar->collectors()->register(VarDumperCollector::class);
$bar->collectors()->register(LogCollector::class);
$bar->collectors()->register(ExceptionCollector::class);
$bar->collectors()->register(DbQueryCollector::class);
$bar->collectors()->register(MemoryCollector::class);
$bar->collectors()->register(PhpCollector::class);
$bar->collectors()->register(RequestCollector::class);

$bar->mark('bootstrap');
$bar->log('info', 'Debug bar mounted', ['userId' => 42]);
$bar->addQuery('SELECT * FROM users WHERE id = ?', [42], 3.42, 'mysql');
$bar->addDump(['id' => 42, 'name' => 'Ada'], 'User payload');
$bar->mark('controller_done');

echo (new Renderer($bar))->render();

Tutorial

1. Create the debug bar

use Marwa\DebugBar\DebugBar;

$bar = new DebugBar(true);

The constructor accepts a single flag:

  • true: start collecting immediately
  • false: keep the bar dormant until you call enable()

2. Register the collectors you want

Collectors are opt-in. Register only what you need.

use Marwa\DebugBar\Collectors\AlertCollector;
use Marwa\DebugBar\Collectors\DbQueryCollector;
use Marwa\DebugBar\Collectors\KpiCollector;
use Marwa\DebugBar\Collectors\LogCollector;
use Marwa\DebugBar\Collectors\RequestCollector;
use Marwa\DebugBar\Collectors\TimelineCollector;
use Marwa\DebugBar\Collectors\VarDumperCollector;

$collectors = $bar->collectors();

$collectors->register(KpiCollector::class);
$collectors->register(AlertCollector::class);
$collectors->register(TimelineCollector::class);
$collectors->register(VarDumperCollector::class);
$collectors->register(LogCollector::class);
$collectors->register(DbQueryCollector::class);
$collectors->register(RequestCollector::class);

3. Instrument your request lifecycle

$bar->mark('bootstrap');

$bar->log('info', 'Loading dashboard', ['tenant' => 'acme']);

$bar->addQuery(
    'SELECT * FROM invoices WHERE tenant_id = ? ORDER BY created_at DESC',
    ['acme'],
    12.84,
    'mysql'
);

$bar->addDump($currentUser, 'Authenticated user');

try {
    $response = $controller->handle($request);
} catch (\Throwable $e) {
    $bar->addException($e);
    throw $e;
}

$bar->mark('response_ready');

4. Render it at the end of the response

use Marwa\DebugBar\Renderer;

$renderer = new Renderer($bar);

echo $html;
echo $renderer->render();

5. Use helper functions if you prefer a global entry point

The package autoloads two helpers:

$bar = debugbar();
$bar->enable();

db_dump($payload, 'Payload');

Configuration

Base toggle

The package does not force a configuration system. The simplest approach is environment variables:

DEBUGBAR_ENABLED=1

Recommended policy:

  • enable only in local or explicitly trusted environments
  • disable in staging and production unless you fully control access

Alert thresholds

If you register AlertCollector, these environment variables are supported:

Variable Default Meaning
DEBUGBAR_ALERTS_ENABLED 1 Turns alert evaluation on or off
DEBUGBAR_SLOW_QUERY_MS 100 Threshold for a slow SQL query
DEBUGBAR_SLOW_REQUEST_MS 1000 Threshold for total request duration
DEBUGBAR_SLOW_SPAN_MS 250 Threshold for delta between consecutive timeline marks
DEBUGBAR_HIGH_MEMORY_MB 64 Threshold for peak memory usage
DEBUGBAR_LARGE_RESPONSE_BYTES 1048576 Threshold for buffered response size

Severity rules are deterministic:

  • warning when the metric reaches the threshold
  • critical when the metric is at least 2x the threshold

Example:

  • 140 ms query with DEBUGBAR_SLOW_QUERY_MS=100 => warning
  • 220 ms query with DEBUGBAR_SLOW_QUERY_MS=100 => critical

Built-In Collectors

Collector Key Purpose
KpiCollector kpi Request KPIs such as duration, SQL time, status, memory, and response size
AlertCollector alerts Performance warnings for slow queries, spans, request duration, memory, and response size
TimelineCollector timeline Chronological marks and deltas between marks
VarDumperCollector dumps Dumps added through addDump() or db_dump()
LogCollector logs Structured log entries added through log()
ExceptionCollector exceptions Captured exceptions and stack traces
DbQueryCollector queries SQL statements, params, durations, and connection names
MemoryCollector memory Current and peak PHP memory usage
PhpCollector php PHP version, extensions, and opcache status
RequestCollector request Sanitized request metadata, headers, input, cookies, files, and server values
SessionCollector session Sanitized session metadata and attributes
CacheCollector cache Placeholder/example collector for cache metrics

Example registration sets

Minimal:

$bar->collectors()->register(KpiCollector::class);
$bar->collectors()->register(TimelineCollector::class);
$bar->collectors()->register(LogCollector::class);

Full diagnostics:

$bar->collectors()->register(KpiCollector::class);
$bar->collectors()->register(AlertCollector::class);
$bar->collectors()->register(TimelineCollector::class);
$bar->collectors()->register(VarDumperCollector::class);
$bar->collectors()->register(LogCollector::class);
$bar->collectors()->register(ExceptionCollector::class);
$bar->collectors()->register(DbQueryCollector::class);
$bar->collectors()->register(MemoryCollector::class);
$bar->collectors()->register(PhpCollector::class);
$bar->collectors()->register(RequestCollector::class);
$bar->collectors()->register(SessionCollector::class);

Public API Reference

DebugBar

__construct(bool $enabled = false)

Create a new bar.

$bar = new DebugBar();
$enabledBar = new DebugBar(true);

enable(): void

Start collecting data.

$bar->enable();

disable(): void

Stop collecting new state.

$bar->disable();

isEnabled(): bool

Check whether collection is active.

if ($bar->isEnabled()) {
    $bar->log('debug', 'Collector active');
}

setLogger(LoggerInterface $logger): void

Mirror log calls to your PSR-3 logger.

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

$logger = new Logger('debugbar');
$logger->pushHandler(new StreamHandler('php://stdout'));

$bar->setLogger($logger);

setMaxDumps(int $maxDumps): void

Cap retained dump entries.

$bar->setMaxDumps(50);

getStartTime(): float

Return the request start timestamp recorded by the bar.

$start = $bar->getStartTime();

elapsedMilliseconds(): float

Return current elapsed time from the bar start.

$elapsed = $bar->elapsedMilliseconds();

collectors(): CollectorManager

Access the collector registry.

$manager = $bar->collectors();
$manager->register(KpiCollector::class);

mark(string $label): void

Create a timeline mark.

$bar->mark('bootstrap');
$bar->mark('router_resolved');
$bar->mark('response_sent');

log(string $level, string $message, array $context = []): void

Add a structured log record.

$bar->log('warning', 'Rate limit nearly reached', ['remaining' => 3]);

addQuery(string $sql, array $params = [], float $durationMs = 0.0, ?string $connection = null): void

Add a SQL query record.

$bar->addQuery(
    'SELECT * FROM users WHERE email = ?',
    ['ada@example.com'],
    8.37,
    'mysql'
);

addDump(mixed $value, ?string $name = null, ?string $file = null, ?int $line = null): void

Add a dump entry.

$bar->addDump($user, 'Current user');
$bar->addDump($payload, 'Payload', __FILE__, __LINE__);

addException(Throwable $exception): void

Record an exception manually.

try {
    $service->run();
} catch (\Throwable $e) {
    $bar->addException($e);
}

registerExceptionHandlers(bool $capturePhpErrorsAsExceptions = true): void

Capture uncaught exceptions and optionally PHP errors.

$bar->registerExceptionHandlers();

Disable PHP error conversion if you only want uncaught exceptions:

$bar->registerExceptionHandlers(false);

state(): DebugState

Expose the immutable state passed into collectors.

$state = $bar->state();
$queryCount = count($state->queries);

CollectorManager

register(string $collectorClass, bool $enabled = true): void

Register a collector class.

$bar->collectors()->register(KpiCollector::class);
$bar->collectors()->register(SessionCollector::class, enabled: false);

setEnabled(string $key, bool $enabled): void

Enable or disable a registered collector by key.

$bar->collectors()->setEnabled('session', true);

autoDiscover(string $directory, string $baseNamespace): int

Scan a directory for collector classes.

$count = $bar->collectors()->autoDiscover(
    __DIR__ . '/src/DebugCollectors',
    'App\\DebugCollectors'
);

renderAll(DebugState $state): array

Render all enabled collectors and return their metadata, HTML, and collected data. This is mainly useful for advanced integrations and tests.

$rows = $bar->collectors()->renderAll($bar->state());

metadata(): array

Return enabled collector metadata without rendering.

$tabs = $bar->collectors()->metadata();

Renderer

__construct(DebugBar $debugBar)

Create the renderer.

$renderer = new Renderer($bar);

render(): string

Render the full HTML and JavaScript for the debug bar.

echo $renderer->render();

Global helper functions

debugbar(): DebugBar

Resolve a shared global debug bar instance.

$bar = debugbar();
$bar->enable();

db_dump(mixed $value, ?string $name = null): void

Shortcut for debugbar()->addDump(...).

db_dump($requestData, 'Request Data');

Collector contract

Implement this interface to create your own tab.

Collector::key(): string

Return a unique tab key.

public static function key(): string
{
    return 'jobs';
}

Collector::label(): string

Return the visible tab label.

public static function label(): string
{
    return 'Jobs';
}

Collector::icon(): string

Return a small icon string.

public static function icon(): string
{
    return '⚙️';
}

Collector::order(): int

Return the sort order.

public static function order(): int
{
    return 210;
}

collect(DebugState $state): array

Collect tab data from the immutable state.

public function collect(DebugState $state): array
{
    return ['jobs' => $this->jobs];
}

renderHtml(array $data): string

Return the tab HTML.

public function renderHtml(array $data): string
{
    return '<div>Queued jobs: ' . count($data['jobs']) . '</div>';
}

Custom Collector Example

<?php

namespace App\DebugCollectors;

use Marwa\DebugBar\Contracts\Collector;
use Marwa\DebugBar\Core\DebugState;

final class FeatureFlagCollector implements Collector
{
    public static function key(): string
    {
        return 'feature_flags';
    }

    public static function label(): string
    {
        return 'Flags';
    }

    public static function icon(): string
    {
        return '🚩';
    }

    public static function order(): int
    {
        return 240;
    }

    public function collect(DebugState $state): array
    {
        unset($state);

        return [
            'items' => [
                'new_checkout' => true,
                'beta_dashboard' => false,
            ],
        ];
    }

    public function renderHtml(array $data): string
    {
        $items = $data['items'] ?? [];

        return '<pre>' . htmlspecialchars(print_r($items, true), ENT_QUOTES, 'UTF-8') . '</pre>';
    }
}

Register it:

$bar->collectors()->register(\App\DebugCollectors\FeatureFlagCollector::class);

Alerts Tab

AlertCollector adds a dedicated Alerts tab that summarizes slow runtime behavior in one place.

It detects:

  • slow queries
  • slow total request duration
  • slow timeline spans between consecutive marks
  • high peak memory usage
  • large buffered response size

Each alert includes:

  • severity
  • type
  • message
  • metric value
  • context

If alerts are disabled with DEBUGBAR_ALERTS_ENABLED=0, the tab renders a disabled message instead of findings.

Request and Session Redaction

RequestCollector and SessionCollector redact common secret-bearing keys such as:

  • password
  • token
  • authorization
  • cookie
  • csrf
  • secret

Uploaded file metadata is summarized without exposing temporary paths.

Twig Integration

If you use Twig, register the bundled extension:

use Marwa\DebugBar\Extensions\TwigDumpExtension;

$twig->addExtension(new TwigDumpExtension($bar));

Then inside Twig templates:

{{ db_dump(user, 'Current user') }}

Example Bootstrap

The repository ships with a small example app under example/.

Serve it locally with:

php -S 127.0.0.1:8000 -t example/public

Project Layout

src/
  Collectors/   Built-in collectors and shared HTML helpers
  Contracts/    Collector interface and exceptions
  Core/         Immutable runtime state passed to collectors
  Extensions/   Optional integrations such as Twig
example/        Minimal demo bootstrap and public entrypoint
tests/          PHPUnit coverage for core behavior and regressions

Development

Install dependencies:

composer install

Run the local quality checks:

composer test
composer run lint
composer run analyze

Run the combined CI gate:

composer run ci

Apply automatic coding-standard fixes:

composer run fix

Testing and Quality

  • PHPUnit covers core state management, collectors, rendering, and redaction behavior
  • PHPStan runs at level 8
  • PHPCS enforces PSR-12 across src/, tests/, and example/
  • GitHub Actions runs validation on pushes and pull requests

Security Guidance

  • keep the bar disabled outside local or explicitly trusted environments
  • avoid rendering the bar for public traffic
  • remember that even redacted collectors may still reveal development details
  • treat alerts as local diagnostics, not production monitoring

Contributing

See AGENTS.md for repository-specific contribution guidelines, coding conventions, and review expectations.