Skip to content

fix: the CLI must attach the event bus - #865

Merged
blaipr merged 1 commit into
mainfrom
fix/the-cli-must-attach-the-event-bus
Aug 23, 2026
Merged

fix: the CLI must attach the event bus#865
blaipr merged 1 commit into
mainfrom
fix/the-cli-must-attach-the-event-bus

Conversation

@blaipr

@blaipr blaipr commented Aug 23, 2026

Copy link
Copy Markdown
Member

ModuleBase::initEventHandlers() is what attaches the four receivers that make the event
bus do anything — the log handler, the database handler that writes the Eventlog table
the security log reads, the mail alerts and the in-app notifications. Web/Init and
Api/Init each call it from their own initialize(). Cli/Init never did.

EventDispatcher is a container singleton and notify() is a plain loop over the attached
receivers, so with none attached it is a silent no-op for the whole process. Nothing
throws, nothing logs that an event was dropped. A master password rotation or a backup
run from bin/cli.php therefore left no trace of having happened, while the identical
operation through the web or the API was fully recorded — including run.backup.start,
the rotation's per-stage events, and the exception events fired when one of them fails.
An operator reading the security log to find out what had been done to the instance was
reading a log with the CLI's work missing from it.

That it is an oversight rather than a decision is visible in the constructor: Cli\Init
already takes ProvidersHelper and hands it to ModuleBase, and nothing in the CLI ever
used it — initEventHandlers() is its only consumer.

It goes before initCli(), which is what runs the command. initEventHandlers() returns
early after attaching the log handler when the instance is not installed, so sp:install
is unaffected: it still gets file logging and nothing that would need a database.

The test builds each module's real container and asserts the dispatcher has the log
handler afterwards. For the CLI it calls the real initialize() (with an inert console
application, so no command is dispatched); for web and api it invokes the shared
initEventHandlers() directly, because reaching it through their initialize() means a real
PHP session for one and a fully installed config with a live database for the other.
Checked by removing the line: only the CLI test fails, and it fails because the handler
is absent.

`ModuleBase::initEventHandlers()` is what attaches the four receivers that make the event
bus do anything — the log handler, the database handler that writes the Eventlog table
the security log reads, the mail alerts and the in-app notifications. Web/Init and
Api/Init each call it from their own initialize(). Cli/Init never did.

EventDispatcher is a container singleton and notify() is a plain loop over the attached
receivers, so with none attached it is a silent no-op for the whole process. Nothing
throws, nothing logs that an event was dropped. A master password rotation or a backup
run from bin/cli.php therefore left no trace of having happened, while the identical
operation through the web or the API was fully recorded — including run.backup.start,
the rotation's per-stage events, and the exception events fired when one of them fails.
An operator reading the security log to find out what had been done to the instance was
reading a log with the CLI's work missing from it.

That it is an oversight rather than a decision is visible in the constructor: Cli\Init
already takes ProvidersHelper and hands it to ModuleBase, and nothing in the CLI ever
used it — initEventHandlers() is its only consumer.

It goes before initCli(), which is what runs the command. initEventHandlers() returns
early after attaching the log handler when the instance is not installed, so sp:install
is unaffected: it still gets file logging and nothing that would need a database.

The test builds each module's real container and asserts the dispatcher has the log
handler afterwards. For the CLI it calls the real initialize() (with an inert console
application, so no command is dispatched); for web and api it invokes the shared
initEventHandlers() directly, because reaching it through their initialize() means a real
PHP session for one and a fully installed config with a live database for the other.
Checked by removing the line: only the CLI test fails, and it fails because the handler
is absent.
@blaipr
blaipr merged commit 788f8c7 into main Aug 23, 2026
8 checks passed
@blaipr
blaipr deleted the fix/the-cli-must-attach-the-event-bus branch August 23, 2026 23:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant