feat: capture staging-verification lessons from iWP Cache deployment
New skill wordpress-plugin-staging-verification: use the persistent staging-1 jail (real-world plugin set left active) instead of a disposable clean-room jail, full verification checklist, and the cosmetic-proc_open-error-vs-real-fatal distinction. Extended bastille-jail-provisioning: bastille0-loopback-missing gotcha (pass the interface explicitly), IP-alias-can-silently-fail-after- recreate gotcha, host-reverse-proxy-vs-jail-IP curl mixup, Valkey as part of the stock stack (correct FreeBSD package name, ACL auth requirement). Extended wordpress-plugin-conventions: drop-in source files (object-cache.php/advanced-cache.php logic) must be excluded from any glob-based plugin autoloader, or they redeclare WP core's own wp_cache_*() functions and fatal -- real bug found and fixed live during iWP Cache's first staging activation attempt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -112,6 +112,36 @@ needs `str_contains`/`str_starts_with`/`str_ends_with` (PHP 8.0+) on an
|
||||
environment that might run 7.4, guard with `function_exists()`
|
||||
polyfills rather than assuming they exist.
|
||||
|
||||
## Drop-in source files (`advanced-cache.php`, `object-cache.php`) must be excluded from any normal plugin autoload/glob
|
||||
|
||||
If a plugin ships an `object-cache.php` or `advanced-cache.php` drop-in
|
||||
(installed by copying a stub into `wp-content/` on activation), the PHP
|
||||
file containing the *real logic* for that drop-in must **never** also be
|
||||
loaded as a normal plugin include — not via an explicit `require`, and
|
||||
not via a glob-based auto-loader that doesn't know to skip it. WordPress
|
||||
core's own `wp-includes/cache.php` declares the same global `wp_cache_*()`
|
||||
function names as the fallback used when no `object-cache.php` drop-in is
|
||||
active; if the plugin's own copy of those functions loads a second time in
|
||||
the same request (as a normal plugin file, in addition to — or instead
|
||||
of — the standalone drop-in load), PHP fatals with "Cannot redeclare
|
||||
function". Confirmed live during iWP Cache staging verification
|
||||
(2026-08-02): a glob-based module auto-loader's exclusion list was written
|
||||
before the object-cache class file existed, so it got swept in and
|
||||
silently broke every activation. **If a plugin has any glob-based
|
||||
autoload for its `includes/` directory, explicitly exclude every
|
||||
drop-in-logic file by name** — don't rely on "it'll only load once" being
|
||||
obviously true just because the code looks like a normal class file.
|
||||
|
||||
## `object-cache.php`/`advanced-cache.php` code runs standalone, before ABSPATH-based guards mean what they normally mean
|
||||
|
||||
The usual `if (!defined('ABSPATH')) exit;` guard doesn't prevent a
|
||||
drop-in-logic file from loading twice in the same request, because by the
|
||||
time it's `require`'d a second time (as a stray plugin include), ABSPATH
|
||||
*is* already defined — WordPress has fully booted. A guard meant to stop
|
||||
"direct access over HTTP" does nothing to stop "accidentally required
|
||||
twice from two different code paths." Don't assume that guard is doing
|
||||
more than it actually does.
|
||||
|
||||
## Don't build what WordPress core already gives you
|
||||
|
||||
Before writing custom code for: cron scheduling (`wp_schedule_event`),
|
||||
|
||||
Reference in New Issue
Block a user