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
31 changes: 8 additions & 23 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,17 @@ All notable changes to `phalcon/debugbar` are documented here. The format is bas

### Added

- A memory collector reports current and peak PHP request usage. Time and Memory
are enabled by default and open from compact indicators on the right instead of
duplicate tabs; the indicators remain available when History is disabled.
- Optional, extensible collector summaries rendered as headline metrics above a panel. The database collector uses them to report total queries, duplicate runs (executions after the first), and accumulated SQL time, and marks repeated statements with their execution count.
- Optional browser-isolated request history with filesystem retention and an
internal `GET/DELETE /_debugbar/open` endpoint. A dedicated HttpOnly, SameSite=Lax
browser-session cookie keeps History independent from the application's PHP
session, is marked Secure on HTTPS, and is appended without replacing application
cookies; clients that do not return it create no storage. The endpoint resolves
the final base URI at request time, leaves application routes and shared responses
untouched, and is excluded from debug-bar collection and injection. Stored
payloads are versioned, bounded, expired automatically, and listed through
metadata sidecars. History requires an explicit absolute writable path and is
disabled by default. Storage creation is serialized and capped across browser
identities, with least-recently-updated eviction.
- `ResponseListener::handle()` runs the injection step with a `ResponseInterface` and no event. The MVC listener calls it, so MVC behavior does not change. [#39](https://github.com/phalcon/debugbar/issues/39)
- `Core` boots the bar with no `Phalcon\Mvc\Application` and no `Phalcon\Di\DiInterface`. It takes an optional events manager, request, config source and history parts. `Provider` is now a wrapper over `Core`, so MVC behavior does not change. [#40](https://github.com/phalcon/debugbar/issues/40)
- `memory` collector for current and peak PHP memory use. Time and Memory now open from indicators on the right, not from tabs.
- Optional collector summaries show headline metrics above a panel. The database panel shows total queries, duplicate runs and SQL time.
- Request history for each browser, at `GET/DELETE /_debugbar/open`. It is disabled by default and needs an absolute, writable `history.path`.
- `ResponseListener::handle()` injects the bar with no event. [#39](https://github.com/phalcon/debugbar/issues/39)
- `Core` boots the bar with no MVC application and no DI container. [#40](https://github.com/phalcon/debugbar/issues/40)
- `ADR\Provider` boots the bar in `Phalcon\ADR` applications (Phalcon `v5.18.0` or later, or Phalcon 6). [#14](https://github.com/phalcon/debugbar/issues/14)

### Fixed

- History now validates storage writability during boot, removes browser
directories emptied by reads, uses portable glob paths on Windows, and adds
`X-Content-Type-Options: nosniff` to its JSON responses.
- The browser asset exports its test helpers only outside a DOM environment and
discovers request method and URI through collector widget metadata instead of a
hard-coded collector name.
- History: check storage at boot, remove empty browser directories, fix glob paths on Windows, add `nosniff` to JSON responses.
- The browser asset exports test helpers only outside a DOM, and no longer hard-codes the request collector name.

## [0.4.0](https://github.com/phalcon/debugbar/releases/tag/v0.4.0) (2026-07-14)

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"squizlabs/php_codesniffer": "^4.0"
},
"suggest": {
"ext-phalcon": "Phalcon C extension (^5) - run the debug bar on Phalcon v5",
"ext-phalcon": "Phalcon C extension (^5) - run the debug bar on Phalcon v5. ADR support needs v5.18.0 or later",
"phalcon/phalcon": "Phalcon PHP implementation (^6) - run the debug bar on Phalcon v6"
},
"autoload": {
Expand Down
77 changes: 74 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The bar runs only in development. It refuses to boot when the environment is und
## Requirements

- PHP 8.1 or later.
- A Phalcon runtime: the Phalcon 5 C extension, or the Phalcon 6 PHP implementation (`phalcon/phalcon`).
- A Phalcon runtime: the Phalcon 5 C extension, or the Phalcon 6 PHP implementation (`phalcon/phalcon`). ADR support needs Phalcon `v5.18.0` or later, or Phalcon 6.
- `matthiasmullie/minify`, pulled in automatically by Composer. It minifies the bar's CSS and JavaScript before they are inlined.

## Installation
Expand All @@ -35,7 +35,7 @@ The remainder of this document covers the debug bar.

## Registering the Debug Bar

The bar is booted by `Phalcon\DebugBar\Provider`. It takes the MVC application and an optional configuration array. The application must have an events manager set before the bar boots. The provider does not add or modify application routes.
In an MVC application, the bar is booted by `Phalcon\DebugBar\Provider`. It takes the MVC application and an optional configuration array. The application must have an events manager set before the bar boots. The provider does not add or modify application routes. For other applications, see [ADR and Non-MVC Applications](#adr-and-non-mvc-applications).

```php
<?php
Expand All @@ -58,9 +58,80 @@ echo $application->handle($_SERVER['REQUEST_URI'])->getContent();

If the application has no events manager, `boot()` still registers the `Debug` facade but attaches no response listener, so nothing is injected. Set an events manager to enable the bar.

## ADR and Non-MVC Applications

### ADR

`Phalcon\DebugBar\ADR\Provider` boots the bar in a `Phalcon\ADR` application. It is a container service provider, and it takes an optional configuration array. It needs Phalcon `v5.18.0` or later, or Phalcon 6.

```php
<?php

use Phalcon\ADR\Front\AbstractHttpFront;
use Phalcon\Container\Container;
use Phalcon\DebugBar\ADR\Provider as DebugBarProvider;

class AppFront extends AbstractHttpFront
{
protected function registerProviders(Container $container): void
{
parent::registerProviders($container);

// Register the services of the application here.

// The debug bar provider must be the last provider.
(new DebugBarProvider())->provide($container);
}
}
```

The provider reads the events manager (`Phalcon\Contracts\Events\Manager`) and the request (`Phalcon\Contracts\Http\AttributeRequest`) from the container. It attaches the response listener to `application:afterHandle`. The application fires this event for a successful response and for an error response, and it emits the response after this event.

!!! warning "NOTE"

Register the provider after all other providers. It resolves the events manager and the request during registration. After that, `extend()` on these services throws `CannotExtendResolved`, and `set()` has no effect. In a blocked environment, the provider resolves no service.

These limits apply in an ADR application:

- **One request for each process.** `Phalcon\ADR\Application::handle()` supports a reused request in a long-lived worker, but collectors have no reset method. In a worker, their data grows from request to request. `HistoryCookie::fromGlobals()` also reads only at boot.
- **Events manager.** The events manager must implement `Phalcon\Events\ManagerInterface`. The default manager does. A manager that implements only `Phalcon\Contracts\Events\Manager` gets no listener, so the bar is not injected.
- **Empty panels.** The `database`, `route` and `exceptions` panels have no ADR source yet. The `view` and `cache` panels get data only when the application gives the events manager to these services. The provider reads no `config` service, so the `config` panel is empty.
- **History.** The provider does not read the `history` keys. Request history works only in MVC.

### A Host with No Events Manager

`Phalcon\DebugBar\Core` boots the bar with no framework application. It takes the configuration array. All the arguments of `boot()` are optional: the events manager, the request and the config source. Call `handle()` when you have the response. The response must implement `Phalcon\Http\ResponseInterface`.

```php
<?php

use Phalcon\DebugBar\Core;

$core = new Core(['access' => ['allow_ips' => ['127.0.0.1']]]);
$core->boot(null, $request);

// The application creates the response here.

$core->handle($response);
```

Give a request when you set `access.allow_ips`. With no request the client address is unknown, so the access gate stops the bar.

If the host has an events manager, attach the listener that `boot()` returns to the response event of the host. `boot()` returns `null` when the bar cannot boot. In this example, `host:response` is a placeholder and Phalcon does not fire it. Replace it with the event that your host fires with the response as the event data.

```php
<?php

$listener = $core->boot($eventsManager, $request);
if (null !== $listener) {
// "host:response" is a placeholder. Replace it with the response event of your host.
$eventsManager->attach('host:response', $listener);
}
```

## Configuration

The second argument to `Provider` is a nested array. Every key is optional.
The second argument to `Provider` is a nested array. Every key is optional. `Phalcon\DebugBar\ADR\Provider` and `Phalcon\DebugBar\Core` take the same array as their only argument. They do not read the `history` keys.

| Key | Type | Default | Purpose |
|--------------------|---------------------------|--------------------------|------------------------------------------------------------------|
Expand Down
74 changes: 74 additions & 0 deletions src/DebugBar/ADR/Provider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

/**
* This file is part of the Phalcon Framework.
*
* (c) Phalcon Team <team@phalcon.io>
*
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Phalcon\DebugBar\ADR;

use Phalcon\Contracts\Container\Service\Collection;
use Phalcon\Contracts\Container\Service\Provider as ProviderContract;
use Phalcon\Contracts\Events\Manager as ManagerContract;
use Phalcon\Contracts\Http\AttributeRequest;
use Phalcon\DebugBar\Core;
use Phalcon\DebugBar\DebugBarTypes;
use Phalcon\DebugBar\Exceptions\CannotUseInProduction;
use Phalcon\Events\ManagerInterface;
use Phalcon\Http\RequestInterface;

/**
* Boots the debug bar in an ADR application. Register it after all other
* providers: it resolves the events manager and the request, and the container
* cannot change a service after that. The ADR application emits the response
* after `application:afterHandle`, so the bar is injected on this event.
*
* @phpstan-import-type provider_config from DebugBarTypes
*/
final class Provider implements ProviderContract
{
private Core $core;

/**
* @param provider_config $config
*/
public function __construct(array $config = [])
{
$this->core = new Core($config);
}

/**
* @throws CannotUseInProduction When `env.strict` is set and the environment is blocked or undefined.
*/
public function provide(Collection $services): void
{
if (true !== $this->core->canBoot()) {
return;
}

$eventsManager = $this->resolve($services, ManagerContract::class);
$eventsManager = $eventsManager instanceof ManagerInterface ? $eventsManager : null;
$request = $this->resolve($services, AttributeRequest::class);
$listener = $this->core->boot(
$eventsManager,
$request instanceof RequestInterface ? $request : null
);

if (null === $eventsManager || null === $listener) {
return;
}

$eventsManager->attach('application:afterHandle', $listener);
}

private function resolve(Collection $services, string $name): mixed
{
return $services->has($name) ? $services->get($name) : null;
}
}
9 changes: 5 additions & 4 deletions src/DebugBar/ResponseListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@
use function sprintf;

/**
* The `application:beforeSendResponse` listener. On the event it runs the access
* gate, aggregates the bar, sets the diagnostic headers, and - for an injectable
* HTML response - renders and splices the bar in. `handle()` does the same work
* with no event.
* The response listener: MVC attaches it to `application:beforeSendResponse` and
* ADR to `application:afterHandle`. On the event it runs the access gate,
* aggregates the bar, sets the diagnostic headers, and - for an injectable HTML
* response - renders and splices the bar in. `handle()` does the same work with
* no event.
*
* @phpstan-import-type request_context from DebugBarTypes
* @phpstan-import-type payload from DebugBarTypes
Expand Down
Loading
Loading