Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ body:
- database
- database-mysql
- database-pgsql
- debugbar
- dev-server
- encryption
- encryption-openssl
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ body:
- database
- database-mysql
- database-pgsql
- debugbar
- dev-server
- encryption
- encryption-openssl
Expand Down
6 changes: 6 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@
"type": "path",
"url": "packages/database-pgsql"
},
{
"type": "path",
"url": "packages/debugbar"
},
{
"type": "path",
"url": "packages/dev-server"
Expand Down Expand Up @@ -378,6 +382,7 @@
"friendsofphp/php-cs-fixer": "^3.92",
"guzzlehttp/guzzle": "^7.0",
"latte/latte": "^3.0",
"marko/debugbar": "self.version",
"pestphp/pest": "^4.3",
"php-amqplib/php-amqplib": "^3.0",
"predis/predis": "^2.0",
Expand Down Expand Up @@ -420,6 +425,7 @@
"Marko\\Core\\Tests\\": "packages/core/tests/",
"Marko\\Cors\\Tests\\": "packages/cors/tests/",
"Marko\\Database\\Tests\\": "packages/database/tests/",
"Marko\\Debugbar\\Tests\\": "packages/debugbar/tests/",
"Marko\\DevServer\\Tests\\": "packages/dev-server/tests/",
"Marko\\Database\\MySql\\Tests\\": "packages/database-mysql/tests/",
"Marko\\Database\\PgSql\\Tests\\": "packages/database-pgsql/tests/",
Expand Down
210 changes: 210 additions & 0 deletions docs/src/content/docs/packages/debugbar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
---
title: marko/debugbar
description: Development debugbar and request profiler for Marko apps with collectors for requests, responses, timing, memory, messages, database queries, logs, views, and Inertia payloads.
---

Development debugbar and request profiler for the Marko Framework. Auto-injects a toolbar into HTML responses and stores a snapshot for every request, with collectors for request data, response, timing, memory, messages, database queries, logs, rendered views, Inertia payloads, and (opt-in) configuration. Inspired by `fruitcake/laravel-debugbar`, but built around Marko's module, plugin, and response lifecycle.

> Use this package only in development. Debugbars expose application internals by design.

## Installation

```bash
composer require marko/debugbar --dev
```

The package auto-registers as a Marko module. By default it enables itself when `APP_DEBUG=true`.

## Configuration

Configure via `config/debugbar.php`. The package ships defaults; an app-level `config/debugbar.php` can override them.

```php title="config/debugbar.php"
return [
'enabled' => env('DEBUGBAR_ENABLED', env('APP_DEBUG', false)),
'inject' => env('DEBUGBAR_INJECT', true),
'capture_cli' => env('DEBUGBAR_CAPTURE_CLI', false),
'theme' => env('DEBUGBAR_THEME', 'auto'),
'route' => [
'open' => env('DEBUGBAR_ROUTE_OPEN', false),
'allowed_ips' => ['127.0.0.1', '::1'],
],
'storage' => [
'enabled' => env('DEBUGBAR_STORAGE_ENABLED', true),
'path' => env('DEBUGBAR_STORAGE_PATH', 'storage/debugbar'),
'max_files' => env('DEBUGBAR_STORAGE_MAX_FILES', 100),
],
'collectors' => [
'messages' => env('DEBUGBAR_COLLECTORS_MESSAGES', true),
'time' => env('DEBUGBAR_COLLECTORS_TIME', true),
'memory' => env('DEBUGBAR_COLLECTORS_MEMORY', true),
'request' => env('DEBUGBAR_COLLECTORS_REQUEST', true),
'response' => env('DEBUGBAR_COLLECTORS_RESPONSE', true),
'inertia' => env('DEBUGBAR_COLLECTORS_INERTIA', true),
'views' => env('DEBUGBAR_COLLECTORS_VIEWS', true),
'database' => env('DEBUGBAR_COLLECTORS_DATABASE', true),
'logs' => env('DEBUGBAR_COLLECTORS_LOGS', true),
'config' => env('DEBUGBAR_COLLECTORS_CONFIG', false),
],
'options' => [
'messages' => [
'trace' => env('DEBUGBAR_OPTIONS_MESSAGES_TRACE', false),
],
'database' => [
'with_bindings' => env('DEBUGBAR_OPTIONS_DATABASE_WITH_BINDINGS', true),
'slow_threshold_ms' => env('DEBUGBAR_OPTIONS_DATABASE_SLOW_THRESHOLD_MS', 100),
],
],
];
```

| Key | Purpose |
| --- | --- |
| `enabled` | Master switch. Defaults to `APP_DEBUG`. |
| `inject` | When true, the toolbar is injected into HTML responses before `</body>`. |
| `capture_cli` | When true, captures CLI invocations as well as HTTP requests. |
| `theme` | Toolbar theme: `auto`, `light`, or `dark`. |
| `route.open` | When false, profiler routes are restricted to `route.allowed_ips`. Set to `true` only on trusted networks. |
| `storage.path` | Directory (relative to project root) where request snapshots are written. |
| `storage.max_files` | Snapshot retention cap. Older files are pruned. |
| `collectors.*` | Toggle individual collectors. The `config` collector is off by default. |
| `options.database.slow_threshold_ms` | Queries slower than this are highlighted. |

## Usage

The debugbar boots automatically with the framework. No controller wiring is required for the toolbar to render on HTML responses.

### Adding messages

Use the `debugbar()` helper to log a message against the current request:

```php
debugbar('Loaded dashboard');
debugbar('Payment failed', 'error', ['invoice' => $invoiceId]);
```

PSR-style level methods are also available on the instance:

```php
debugbar()?->debug('Starting import');
debugbar()?->info('Report generated');
debugbar()?->warning('Slow external API');
debugbar()?->error('Payment failed', ['invoice' => $invoiceId]);
```

### Measuring time

```php
$result = debugbar()?->measure('build report', fn () => buildReport());

debugbar()?->startMeasure('external api');
// ...
debugbar()?->stopMeasure('external api');
```

### Database, logs, and views

The package ships three plugins (`DatabaseConnectionPlugin`, `LoggerPlugin`, `ViewPlugin`) that intercept calls through Marko's `ConnectionInterface`, `LoggerInterface`, and `ViewInterface`. Anything that goes through those interfaces is captured automatically — no controller changes required.

Captured query data: type (`query` or `execute`), SQL, bindings (configurable), start offset, duration, and row count. Queries above `options.database.slow_threshold_ms` are highlighted.

Current limitation: prepared statement execution is captured only when it goes through `ConnectionInterface::query()` or `ConnectionInterface::execute()`.

### Inertia

The Inertia collector detects Marko Inertia HTML and `X-Inertia` JSON payloads from the final response body — no hard dependency on an Inertia package. It surfaces component name, URL, version, prop count, prop keys, and partial-reload headers when present.

### Profiler UI

Every captured request gets a stable debug ID. The injected toolbar starts in a compact rail showing method, duration, memory, message count, query count, log count, and URI. `Expand` opens the inline detail panel.

The toolbar links to the per-request profiler page:

```text
/_debugbar/{id}
```

The index lists stored requests:

```text
/_debugbar
```

Raw collector dataset for a request:

```text
/_debugbar/{id}/json
```

JSON/API responses are not modified, but the snapshot is still written and the per-request URL is exposed via the `X-Marko-Debugbar-Url` response header.

By default, profiler routes are available only when the debugbar is enabled and the request comes from `127.0.0.1` or `::1`. Set `DEBUGBAR_ROUTE_OPEN=true` only for trusted local/dev environments.

### Response headers

When capturing, every response carries:

- `X-Marko-Debugbar: true`
- `X-Marko-Debugbar-Id: {id}`
- `X-Marko-Debugbar-Url: {profiler URL}`
- `Server-Timing: marko;dur={ms};desc="Marko"`

### Sensitive value masking

The request and config collectors mask common sensitive keys. The config collector default mask list:

- `*.key`
- `*.password`
- `*.secret`
- `*.token`
- `*.api_key`
- `*.private_key`

Override via `debugbar.options.config.masked` in app config for project-specific rules.

## API Reference

```php
namespace Marko\Debugbar;

class Debugbar
{
public static function current(): ?self;
public static function forgetCurrent(): void;

public function boot(): void;
public function isEnabled(): bool;
public function isCapturing(): bool;
public function id(): string;
public function profilerUrl(): string;

public function addMessage(string $message, string $level = 'info', array $context = []): void;
public function debug(string $message, array $context = []): void;
public function info(string $message, array $context = []): void;
public function warning(string $message, array $context = []): void;
public function error(string $message, array $context = []): void;

public function startMeasure(string $name): void;
public function stopMeasure(string $name): void;
public function measure(string $name, Closure $callback): mixed;

public function inject(string $html): string;
public function collect(?string $responseBody = null): array;
}
```

The `debugbar()` helper is registered globally:

```php
function debugbar(?string $message = null, string $level = 'info', array $context = []): ?Debugbar;
```

It returns the current `Debugbar` instance (or `null` when no request is active) and adds the message when one is provided.

## Related Packages

- [`marko/config`](/docs/packages/config/) — provides the configuration repository
- [`marko/database`](/docs/packages/database/) — `ConnectionInterface` is what the database collector intercepts
- [`marko/log`](/docs/packages/log/) — `LoggerInterface` is what the logs collector intercepts
- [`marko/view`](/docs/packages/view/) — `ViewInterface` is what the views collector intercepts
- [`marko/routing`](/docs/packages/routing/) — registers the `/_debugbar` profiler routes
5 changes: 5 additions & 0 deletions packages/debugbar/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/tests export-ignore
/.github export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/phpunit.xml.dist export-ignore
21 changes: 21 additions & 0 deletions packages/debugbar/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) Devtomic LLC

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
25 changes: 25 additions & 0 deletions packages/debugbar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# marko/debugbar

Development debugbar and request profiler for the Marko Framework.

## Installation

```bash
composer require marko/debugbar --dev
```

## Quick Example

With `APP_DEBUG=true` (or `DEBUGBAR_ENABLED=true`), the debugbar auto-injects into HTML responses and stores a snapshot for every request. Add custom messages and timings from anywhere in your app:

```php
debugbar('Rendering dashboard', 'info', ['user_id' => $user->id]);

$report = debugbar()?->measure('build report', fn () => $this->reports->build());
```

Open `/_debugbar` to browse stored requests, or follow the `X-Marko-Debugbar-Url` header on JSON responses for the per-request profiler page.

## Documentation

Full configuration, collectors, and API reference: [marko/debugbar](https://marko.build/docs/packages/debugbar/)
45 changes: 45 additions & 0 deletions packages/debugbar/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "marko/debugbar",
"type": "marko-module",
"description": "Development debugbar and request profiler for the Marko Framework with request, response, timing, memory, messages, database, logs, views, Inertia, and config collectors",
"license": "MIT",
"keywords": [
"marko",
"debugbar",
"profiler",
"debug",
"webprofiler",
"dev"
],
"require": {
"php": "^8.5",
"marko/config": "self.version",
"marko/core": "self.version",
"marko/database": "self.version",
"marko/env": "self.version",
"marko/log": "self.version",
"marko/routing": "self.version",
"marko/view": "self.version"
},
"require-dev": {
"marko/testing": "self.version"
},
"autoload": {
"psr-4": {
"Marko\\Debugbar\\": "src/"
},
"files": [
"src/helpers.php"
]
},
"autoload-dev": {
"psr-4": {
"Marko\\Debugbar\\Tests\\": "tests/"
}
},
"extra": {
"marko": {
"module": true
}
}
}
Loading