fix: wordpress-plugin-rebrand's updater-wiring example could reintroduce the dead-updater bug on singleton-class plugins

This commit is contained in:
2026-08-03 06:25:28 +02:00
parent 3242dadc47
commit c868eae35f
+37 -13
View File
@@ -80,20 +80,44 @@ renames were consistent.
## Step 5 — Bundle the updater / license client (if applicable) ## Step 5 — Bundle the updater / license client (if applicable)
Copy the shared updater class unchanged into the plugin's `includes/` Copy the shared updater class unchanged into the plugin's `includes/`
directory. Wire it in the main file (near the top, after constants): directory. **Where you wire it in depends on the plugin's own
architecture — get this wrong and license/update-checking silently never
runs, with no error anywhere** (see the full incident writeup in
`wordpress-plugin-conventions`'s "Never add_action at a lower priority
from inside a callback on the same hook" section):
```php - **If the plugin is flat/procedural** (no singleton class hooked to
require_once <PATH_CONSTANT> . 'includes/class-<updater-file>.php'; `plugins_loaded` itself — the 5 original iWP forks are this shape), a
add_action( 'plugins_loaded', function () { top-level call in the main file is safe:
new <UpdaterClass>( [ ```php
'api_url' => '<the licensing server's API base>', require_once <PATH_CONSTANT> . 'includes/class-<updater-file>.php';
'plugin_file' => __FILE__, add_action( 'plugins_loaded', function () {
'plugin_slug' => '<the confirmed catalog slug>', new <UpdaterClass>( [ /* ... */ ] );
'version' => <VERSION_CONSTANT>, }, 5 );
'license_option' => '<newprefix>_<name>_license_key', ```
] ); This works because the file itself loads (and this `add_action` call
}, 5 ); runs) BEFORE `plugins_loaded` ever fires — it's not nested inside
``` another callback already running on that same hook.
- **If the plugin uses a singleton class already hooked to
`add_action('plugins_loaded', ['ClassName', 'instance'])`** (the
iWP Cache / iWP Elementor Addons / iWP Image Optimize shape, and
likely InformatiQ Toolkit's shape too — check for a
`public static function instance()` pattern), do **NOT** add a nested
`add_action('plugins_loaded', ..., 5)` inside that class's
constructor — confirmed dead code in exactly this shape, three times,
in one session. Instead call it **directly, immediately**, inside the
constructor:
```php
private function __construct() {
new IWP_Updater( [ /* ... */ ] ); // direct call, no nested add_action
// ...rest of the constructor's hook registrations...
}
```
`IWP_Updater`'s own constructor only registers filters on OTHER hooks
(`pre_set_site_transient_update_plugins`, `plugins_api`,
`upgrader_process_complete`) — never `plugins_loaded` itself — so there
is no ordering reason to defer construction at all.
Then add ONE settings field for the license key (option name matching Then add ONE settings field for the license key (option name matching
`license_option` above) into wherever the plugin already has an admin `license_option` above) into wherever the plugin already has an admin