Skip to content

Refactor debugbar into an event-driven optional integration and remove hardcoded framework coupling #539

Description

@armanist

Summary

Refactor debugbar so it becomes a true optional integration built on top of the Event/lifecycle system.

This should remove hardcoded debugger integration from core boot/runtime flow and move framework-owned integration onto lifecycle and package event boundaries, while handling recursion and logger coupling carefully.

Why

Debugbar is currently coupled to the framework at multiple levels.

Hardcoded core integration exists through:

  • InitDebuggerStage in the web boot pipeline

Framework-owned runtime integration also exists in places such as:

  • WebAppTrait pushing hook/debug metadata into the debugger store

In addition, there are direct debugger dependencies in framework packages such as:

  • View
  • logger message reporting through MessageAdapter
  • mailer-related reporting

If the goal is to make debugbar truly optional and lifecycle-driven, then only moving boot initialization to hooks is not enough.

At the same time, this refactor must be careful about two real risks:

  • debugger listeners will themselves participate in the event system they observe, which can create self-reference and recursion problems if not handled carefully
  • MessageAdapter is the default logger adapter in debug mode, so blindly converting logger/debugger integration into events can create circular reporting paths

Goal

Make debugbar a true optional event-driven integration by:

  • removing hardcoded core debugger wiring
  • moving framework-owned debugbar integration onto lifecycle and event subscriptions
  • avoiding recursive or circular integration paths

Scope

This ticket should focus on framework integration boundaries and optional integration registration.

It should:

  • remove hardcoded debugbar boot/runtime wiring from core
  • use external optional-integration listener registration for debugbar
  • move framework-owned debugbar integration onto lifecycle hooks
  • invert direct debugger dependencies where that boundary is safe and justified
  • explicitly avoid recursion-prone or circular event/reporting paths

It should not force a broad event rewrite where the dependency boundary is not yet safe.

Proposed Direction

Remove hardcoded core integration

Remove InitDebuggerStage from the web boot pipeline.

Core should only define and dispatch lifecycle/event boundaries. Core should not call debugbar() directly for framework integration.

Register debugbar listeners externally

Debugbar should subscribe through the external Event/lifecycle listener registration mechanism rather than through hardcoded core wiring.

Move framework-owned integration to listeners

Framework-owned debugbar integration should move to lifecycle-driven listeners, including areas such as:

  • debugger store initialization
  • framework lifecycle/debug metadata collection
  • other runtime/route/response integration owned by framework flow

Invert package dependencies only where safe

Direct debugger dependencies should be reduced where event emission is the better architectural boundary, but this should be done selectively.

Candidate areas include:

  • view/render-related debug data
  • mailer-related debug data

Treat logger/debugger coupling as a special case

MessageAdapter is the debug-mode default logger adapter today.

That means logger/debugger integration must be handled carefully to avoid circular paths such as:

  • package emits log event
  • debugbar listener consumes it
  • listener logs again
  • event loop repeats

For that reason, logger/debugger integration should either:

  • remain direct in this refactor, or
  • be changed only with an explicitly recursion-safe design

Enforce non-recursive debugger listeners

Debugger listeners should not rely on event-producing logging/reporting APIs while handling debugger-related events.

They should write directly to debugger storage/rendering concerns instead of routing back through logger/event producers.

Example Direction

Core lifecycle dispatch

event()->dispatch(LoadModulesStage::BEFORE, [
    'context' => $context,
]);

Debugbar subscribes externally

event()->listen(LoadModulesStage::BEFORE, function (array $payload): void {
    $debugger = debugbar();

    if ($debugger->isEnabled()) {
        $debugger->initStore();
    }
});

Mailer emits an event

event()->dispatch('mailer.warning', [
    'message' => $message,
    'transport' => 'smtp',
]);

Debugbar listens and writes directly to its store

event()->listen('mailer.warning', function (array $payload): void {
    $debugger = debugbar();

    if ($debugger->isEnabled()) {
        $debugger->addToStoreCell(Debugger::MAILS, 'warning', $payload['message']);
    }
});

Acceptance Criteria

  • InitDebuggerStage is removed from hardcoded core integration
  • debugbar lifecycle listeners are registered externally rather than inside core boot/runtime flow
  • framework-owned debugbar integration is moved onto lifecycle/event listeners
  • recursion-prone debugger listener behavior is explicitly avoided
  • logger/debugger integration is either left direct or changed only through a justified recursion-safe design
  • debugbar remains optional and is not hardcoded as a core framework concern
  • tests are updated as needed

Notes

Relevant code:

  • src/App/Stages/InitDebuggerStage.php
  • src/App/Traits/WebAppTrait.php
  • src/View/View.php
  • src/Logger/Adapters/MessageAdapter.php
  • src/Logger/Factories/LoggerFactory.php
  • src/Mailer/Traits/MailerTrait.php
  • src/Mailer/Adapters/SmtpAdapter.php
  • src/Debugger/Debugger.php
  • src/Debugger/Helpers/debugger.php

Depends on:

  • #536
  • #537
  • #538

Metadata

Metadata

Assignees

No one assigned

    Labels

    debugbarDebugbar and debugger integrationhooksHook and event system

    Type

    No fields configured for Task.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions