Skip to content

12D: Never let a hook throw, whatever the host's code does - #25

Open
nikolaystrikhar wants to merge 1 commit into
12C-registry-readerfrom
12D-hook-guard
Open

12D: Never let a hook throw, whatever the host's code does#25
nikolaystrikhar wants to merge 1 commit into
12C-registry-readerfrom
12D-hook-guard

Conversation

@nikolaystrikhar

Copy link
Copy Markdown
Contributor

What: the plugins_loaded step in Boot\Scheduler, Absorber::render_notices() on all_admin_notices, and Loader::load_all() per sub-plugin all catch Throwable, report with _doing_it_wrong() and abandon that step alone.

Usage:

Absorber::register( [
    'slug'    => 'give-recurring',
    'enabled' => static fn() => give_licence_is_active(), // May throw. The site stays up.
] );

Why this way:

Configuration still throws. register() rejects a bad config array on the spot, at a call in the developer's own stack trace, before anything is hooked. Past that point this library is code on somebody's live site, and a white screen is never the better answer.

The load pass catches per sub-plugin as well. Everything past that catch is host code — the enabled and dependency_check callables, the notice messages, should_load, and the bundled file a require runs top to bottom — and one sub-plugin's throw took the whole site down, front end included, along with every sub-plugin behind it in the registration order.

The reporter takes the step's name and what the site got instead, because the sequence gains a second step at the priority ahead of this one and both have to report the same way.

A re-declaration is the one failure none of this catches, since PHP does not raise it as a Throwable. The guard constant, checked before the require, is what has always prevented that one.

Configuration still throws -- register() rejects a bad config array where the
host can see its own call in the stack trace. Everything the library puts on a
hook now catches Throwable, reports it with _doing_it_wrong() and abandons that
step: the plugins_loaded step in Boot\Scheduler, and render_notices() on
all_admin_notices.

The load pass catches per sub-plugin as well. Past that catch is somebody else's
code -- the enabled and dependency_check callables, the conflict policy and its
filter, the notice messages, should_load, and the bundled file a require runs top
to bottom -- and a throw from one sub-plugin took the whole site down, front end
included, along with every sub-plugin behind it in the registration order.

The step reporter takes the step's name and what the site got instead, because
the sequence gains a second step at the priority ahead of this one and both have
to report the same way.

A re-declaration is the one failure none of this catches, because PHP does not
raise it as a Throwable. The guard constant, checked before the require, is what
has always prevented that one.

@d4mation d4mation left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nice! Especially if something like the enabled callback references something that suddenly stops existing this could be a big deal.

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.

2 participants