Repository navigation
Description
Summary
Take the debugbar out of the app flow and out of the packages that feed it: no web stage, no view, no logger and no mailer calls the debugger any more. Each package keeps its own state, and the debugger collects what it shows when the toolbar renders: the registered event listeners, the matched route together with the rendered view, the database query log, the log messages and the mailer transport log.
It also brings back three tabs that lost their data: the routes tab has shown only the view path since 3.0.0, the queries tab has been empty since 2.6.0, and the SMTP exchange never reaches the mails tab because SmtpAdapter checks a config key that is never set.
Why
The App package runs the boot and the request, the View package renders templates, the Logger writes logs and the Mailer sends mail; none of them should know about one optional integration. Today two web stages, View, the debug-mode logger and the mailer call or target the debugger directly, so the debugbar is wired into every request, every rendered view, every log line and every mail whether a project uses it or not.
Before the 3.0 routing rewrite, the router wrote the matched route (method, pattern, module, controller, action, middlewares, params) into the routes tab, and View added the view path to that entry. The rewrite dropped the router's write, so the tab now holds only View => <module>/Views/<file>.
The queries tab was filled with Database::queryLog() next to the route data until the 2.6.0 hooks rework removed that write. Since then nothing writes to it, although Idiorm still logs the queries whenever app.debug is on.
Current state (checked on master)
| Place | What it does |
|---|---|
InitDebuggerStage (web boot) |
calls debugbar()->initStore(), which creates the five tabs as empty arrays |
PrepareRequestStage (web runtime, matched routes only) |
writes event()->getRegistered() into the events tab when the debug bar is enabled |
View (gets the Debugger injected by ViewFactory) |
after rendering a view, merges View => <module>/Views/<file> into the routes tab |
MessageAdapter (the logger in debug mode) |
calls debugbar() and writes each log line into messages, or into the tab named by $context['tab'] |
MailerTrait::send() |
logs the transport errors of a failed send with warning(..., ['tab' => Debugger::MAILS]) |
SmtpAdapter |
sends the SMTP exchange to the same tab, but only when config()->get('debug') is set, a key that does not exist (app.debug is the debug flag), so it never does |
| nothing | writes to the queries tab |
- The store does not need
initStore():DebuggerStore::set()creates a missing tab on the first write, andget()andrender()read a missing tab as[]. Nothing insrcrelies on the tabs existing beforehand. - The toolbar is rendered by the module layouts (
debugbar()->render()), while the request is still being dispatched, so the matched route is still on the request at that point.
Proposed change
-
Remove
InitDebuggerStagefrom the web boot, with no replacement. -
Remove the debugbar write from
PrepareRequestStage. -
Viewno longer depends on the debugger: it keeps the name of the last rendered view (getViewFile(), likegetLayout()), andViewFactoryno longer passesdebugbar(). -
MessageAdapterbecomes an in-memory logger:report()keeps[level => message]andgetMessages()returns them. It no longer imports or calls the debugger. -
The mailer keeps its own transport log:
MailerInterface::getTransportLog(), implemented inMailerTrait, returns the transport errors of failed sends and, in debug mode, the SMTP exchange. The failed-send warning stays in the log, without the tab.SmtpAdapterchecksis_debug_mode(). -
Debugger::render(), when the debug bar is enabled, fills these tabs itself right before it builds them:events: the listeners fromevent()->getRegistered()routes: fromrequest()->getMatchedRoute()the methods, pattern, module, controller and action (or a closure), name, group, prefix, middlewares and params, and the view path from the existingView(<module>/Views/<file>); with no matched route and no rendered view the tab stays emptyqueries: each query fromDatabase::queryLog(), only when the request already used the database (db()was resolved) with a relational adapter; the debugbar never opens a connection itself, and SleekDB has no query logmessages: the messages of the debug-mode logger (the boot always builds the logger for the error handler)mails: the transport log ofmailer(), only when the request used the mailer (like themessagestab readslogger())
The collected entries go straight to the tab's collector, after the tab's stored entries. The store keeps only what code writes into it directly (
debugbar()->addToStoreCell()), so those writes are never cleared or mixed with collected data. The debugger never builds a mailer or database connection the request did not use.
After this, no package outside src/Debugger references Quantum\Debugger; only the module layout templates call debugbar()->render().
No event listener is involved. A listener would need a place to be registered: the framework helper files load in alphabetical order (Debugger before Event) and only once per process, and a boot stage that registers the listener would be the same hardcoding in another form. Reading the state at render time needs none.
Behavior changes
| Case | Today | After |
|---|---|---|
routes tab |
only View => <module>/Views/<file> |
the matched route's data and the view path |
queries tab |
always empty | the queries the request ran |
events tab |
the listeners registered when the request was prepared | the listeners registered when the page renders, so listeners added later (for example in a controller) are listed too |
messages tab |
each log line written to the store as it is logged | the logger keeps the lines; the tab reads them when the toolbar renders |
DebuggerStore after rendering |
holds the events, routes and log lines | holds only the direct writes |
mails tab |
transport errors of failed sends; never the SMTP exchange | transport errors and, in debug mode, the SMTP exchange |
messages tagged with another tab (['tab' => ...]) |
written to that tab | listed in the messages tab |
DebuggerStore before any write |
the five tabs exist as empty arrays | a tab exists once something writes to it; reading or rendering is the same |
| a request that renders no layout | the events tab is filled but never shown |
nothing is written |
Breaking changes
InitDebuggerStageis removed (in 3.0.3 it wasQuantum\App\Stages\InitDebuggerStage). Code that ran it in its own boot can calldebugbar()->initStore()instead;Debugger::initStore()stays.View::__construct()no longer takes aDebugger: it is(Renderer, AssetManager, ViewCache). Code that gets the view throughview()orViewFactoryis unaffected; code that creates aViewitself drops that argument.MailerInterfacegainsgetTransportLog(): array. The built-in adapters get it fromMailerTrait; a custom adapter that implements the interface without the trait adds the method.MessageAdapterno longer reads$context['tab']: code that logged into another debugbar tab that way now lands inmessages.
Tests
Debugger::render()with the debug bar enabled:- lists the registered event listeners in the
eventstab - fills the
routestab for a matched controller route, for a closure route, and with the rendered view path - leaves the
routestab empty without a matched route and without a rendered view - lists the queries the request ran in the
queriestab, and leaves it empty, without connecting, when the database was not used or the adapter has no query log - lists the logger's messages in the
messagestab, and the mailer's transport log in themailstab, without building a mailer the request did not use - passes a tab's stored entries before the collected ones, and leaves the store unchanged
- passes nothing to the tabs when the debug bar is disabled
- lists the registered event listeners in the
View::getViewFile()returns the last rendered view.MessageAdapter::getMessages()returns the reported messages.getTransportLog()returns the transport errors of a failed send, and the SMTP exchange in debug mode.
Acceptance criteria
- no package outside
src/DebuggerreferencesQuantum\Debuggeror callsdebugbar(), except the module layout templates that render the toolbar - the
routestab shows the matched route and the view path, thequeriestab the queries the request ran, themessagestab the log messages, themailstab the mailer transport log, and theeventstab the listeners registered when the toolbar renders composer test,composer cs:check,composer phpstanandcomposer rector:checkpass
Notes
Relevant code:
src/App/Adapters/Web/WebAppAdapter.phpsrc/App/Adapters/Web/Stages/Boot/InitDebuggerStage.phpsrc/App/Adapters/Web/Stages/Runtime/PrepareRequestStage.phpsrc/View/View.php,src/View/Factories/ViewFactory.phpsrc/Logger/Adapters/MessageAdapter.php,src/Logger/Factories/LoggerFactory.phpsrc/Mailer/Contracts/MailerInterface.php,src/Mailer/Traits/MailerTrait.php,src/Mailer/Adapters/SmtpAdapter.php,src/Mailer/Factories/MailerFactory.phpsrc/Debugger/Debugger.php,src/Debugger/DebuggerStore.phpsrc/Database/Database.php,src/Database/Contracts/RelationalInterface.php
Depends on:
#536#538
Activity
[-]Refactor debugbar into an event-driven optional integration and remove hardcoded framework coupling[/-][+]Take the debugbar out of the app flow[/+][-]Take the debugbar out of the app flow[/-][+]Take the debugbar out of the app flow and the view[/+][-]Take the debugbar out of the app flow and the view[/-][+]Take the debugbar out of the app flow and the packages that feed it[/+][#539] Fill the debugbar events tab when the toolbar renders
[#539] Remove InitDebuggerStage from the web boot
[#539] Add the changelog entry for taking the debugbar out of the app…
[#539] Fill the debugbar routes tab when the toolbar renders and drop…
[#539] Fill the debugbar queries tab when the toolbar renders
[#539] Remove the tests that only prove the debugger is gone
[#539] Keep log messages in the logger and pass collected data straig…
[#539] Keep the mailer transport log in the mailer and read it into t…
[#539] Describe taking the debugbar out of the app flow and the packa…
[#539] Read the query log only from a started and connected database,…