Skip to content

Fix correctness issues in the showcase plugin code#39

Merged
GaryJones merged 7 commits into
mainfrom
GaryJones/plugin-correctness
Jun 11, 2026
Merged

Fix correctness issues in the showcase plugin code#39
GaryJones merged 7 commits into
mainfrom
GaryJones/plugin-correctness

Conversation

@GaryJones

@GaryJones GaryJones commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

As a reference repository, the boilerplate should not teach patterns that are subtly wrong, and a few had crept in.

The plugin headers used Requires WP, which WordPress core does not recognise, so the WordPress 6.9 floor was never enforced; it is now Requires at least. The Plugin URI and GitHub Plugin URI placeholders point at the real repository (the latter annotated as a Git Updater header), and the License URI uses the canonical HTTPS GPL-2.0 page.

The bootstrap built the Config and assigned a Plugin instance to $GLOBALS at file include time — exactly the side-effect-on-load anti-pattern the plugin standards forbid — and the __() calls in config/defaults.php ran before init, triggering the _load_textdomain_just_in_time notice on WordPress 6.7+. The plugin now boots on plugins_loaded behind a plugin_slug() accessor, with translatable labels held as closures that only resolve inside admin_menu and admin_init callbacks.

Plugin::run() previously did nothing, leaving most of the config dead and the class carrying an import for an uninstalled package, a never-assigned $instance property, and a docblock for a get_instance() that did not exist. It now genuinely registers the options page, setting, section and field from the config, so every remaining config key is consumed by real code; dead entries (js/admin-page.js, languages_dir) are gone. Each field passes a label_for matching the input id in its view, so the rendered title accessibly labels the control, and the hard-coded English in the section and field views is now translatable.

Rounding out the housekeeping: a new guarded uninstall.php removes the plugin's option on deletion, Foo gains its missing @since and @return tags, and the never-referenced cedaro/wp-plugin Composer dependency is removed (verified by grep; brightnucleus/config remains in use).

Verified with composer lint, a full vendor/bin/phpcs run, the unit test suite, and a stubbed-WordPress smoke test confirming the boot path registers the page, setting, section and field and renders the views correctly.

Since run() now does real work, a new PluginTest covers the registration end to end — exact arguments to the WordPress registration functions, and every captured render closure invoked against view fixtures — so the 100% mutation score gate arriving in #40 holds once both PRs land.

@GaryJones GaryJones force-pushed the GaryJones/plugin-correctness branch from fda2e06 to 9c01267 Compare June 11, 2026 14:43
WordPress core only recognises 'Requires at least' for the minimum
WordPress version, so the non-standard 'Requires WP' header left the
6.9 floor silently unenforced. Rename it, fill the Plugin URI and
GitHub Plugin URI placeholders with the real repository URL, note that
the latter is read by Git Updater (formerly GitHub Updater), and point
the License URI at the canonical HTTPS GPL-2.0 page.
Building the Config and assigning a Plugin instance to $GLOBALS at
file load ran code as a side effect of inclusion, and the __() calls
in config/defaults.php executed before init, which triggers the
_load_textdomain_just_in_time notice on WordPress 6.7+. The plugin now
boots on plugins_loaded behind a plugin_slug() accessor, and
translatable labels are closures resolved during the admin_menu and
admin_init callbacks.

Plugin::run() previously did nothing, so most of the config was dead:
it now registers the admin page, setting, section and field from the
config, meaning every remaining key is consumed by real code. The
unused js/admin-page.js and languages_dir entries go away (no js/ or
languages/ directory exists), along with the import of the
uninstalled BrightNucleus Settings package, the never-assigned static
$instance property, and the docblock advertising a non-existent
get_instance() method. The now-unused PLUGIN_SLUG_URL constant is
dropped too.

Each field passes label_for matching the input id in its view, so the
field title labels the control, and the previously hard-coded English
strings in the section and field views are now translatable.
Add the missing @SInCE and @return tags so the example class
demonstrates fully documented methods.
Without an uninstall handler, the plugin left its setting behind in
the options table after deletion. Guarded on WP_UNINSTALL_PLUGIN so it
only runs as part of a genuine uninstall.
Nothing in the codebase references the package, so requiring it only
added install weight and sent a misleading signal about how the plugin
is architected.
The Rector php84 set arriving in the code-standards branch converts
array callables to the native form, so adopt it here and keep the
combined rector dry-run clean.
The testing branch gates Infection at 100% MSI, measured before run()
gained real behaviour. Exact-argument expectations and invoked render
closures kill every mutant the new registration code introduces, and
double as a worked example of testing hooked methods with Brain Monkey.
@GaryJones GaryJones force-pushed the GaryJones/plugin-correctness branch from 9c01267 to c2f3e02 Compare June 11, 2026 16:58
@GaryJones GaryJones merged commit a992aeb into main Jun 11, 2026
3 checks passed
@GaryJones GaryJones deleted the GaryJones/plugin-correctness branch June 11, 2026 18:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant