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)
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
require_once <PATH_CONSTANT> . 'includes/class-<updater-file>.php';
add_action( 'plugins_loaded', function () {
new <UpdaterClass>( [
'api_url' => '<the licensing server's API base>',
'plugin_file' => __FILE__,
'plugin_slug' => '<the confirmed catalog slug>',
'version' => <VERSION_CONSTANT>,
'license_option' => '<newprefix>_<name>_license_key',
] );
}, 5 );
```
- **If the plugin is flat/procedural** (no singleton class hooked to
`plugins_loaded` itself — the 5 original iWP forks are this shape), a
top-level call in the main file is safe:
```php
require_once <PATH_CONSTANT> . 'includes/class-<updater-file>.php';
add_action( 'plugins_loaded', function () {
new <UpdaterClass>( [ /* ... */ ] );
}, 5 );
```
This works because the file itself loads (and this `add_action` call
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
`license_option` above) into wherever the plugin already has an admin