PHP version fatals Plugin Check will never catch
Your zip passes Plugin Check on PHP 8.2. A host still runs 8.3 with different deprecations — or 7.4 without the extension you assumed. Lint cannot simulate that matrix.
Plugin Check and the WordPress.org upload pipeline run against one PHP environment at a time. Your laptop might be 8.2. The directory scanner might be 8.1. Your customer’s managed host might still default to 7.4, or jump to 8.3 on the next panel toggle.
Static checks answer: does this code look acceptable on the runtime we chose today? They do not answer: does this plugin survive activation and the first admin click on every PHP version your readme claims to support? Those diverge constantly — and the failure mode is often a white screen, not a friendly notice.
What “PHP version” failures actually look like
Not every mismatch is “function mysql_connect doesn’t exist.” Common ship killers:
- Parse errors on older PHP. Typed properties, union types,
match, or trailing commas in the wrong place — code that never loads on 7.4. - Deprecations that become fatals on the next minor. Passing null to internal parameters, dynamic properties, implicit nullable types — fine on 8.1, noisy on 8.2, broken on 8.3 when error handling is strict.
- Extensions present on your machine, missing on the host.
intl,mbstring,imagick, Redis — your dev box has them; production does not until someone reads the error log. - Different
memory_limitandmax_execution_time. Not a “version” issue on paper, but the same symptom: plugin dies on import on shared hosting. - Autoload paths that only work in your build. Composer dev dependencies shipped by mistake, or a path that assumes a local monorepo layout.
Plugin Check might flag some deprecated calls statically. It will not run your activation hook on PHP 7.4 and 8.3 in the same afternoon and compare the results.
Why authors discover this after release
Three habits stack the deck against you:
- One PHP version in dev. Docker pinned to 8.2 means you never see 8.3 deprecations until a user emails.
- “Requires PHP: 7.4” in the header without testing 7.4. The header is a promise. WordPress.org and users treat it as support, not marketing.
- Testing only wp-admin on your version. CLI tools, cron, and REST endpoints often boot different code paths — same PHP mismatch, different entry point.
Support tickets that say “white screen after update” or “works on my server” are often this class of bug. Plugin Check green does not close that ticket.
What Plugin Check still does (and stops)
Keep running it for directory hygiene: headers, escaping smells, discouraged APIs, readme shape. Fix what it finds. Then add a runtime matrix pass Plugin Check cannot substitute:
- List the PHP versions you claim in
Requires PHP— usually two targets: oldest supported and current stable. - On a clean WordPress install per version: upload zip, activate, open the primary wp-admin screen.
- Run one observable mission — save a setting, create the entity your plugin owns, hit the REST route if that is how the product works.
- Capture fatals from the log when the screen goes blank — not “I refreshed and it worked.”
That is environment QA, not static QA. Different job, different brief.
How to brief testers for PHP/runtime risk
Do not say “test on all PHP versions.” Name the matrix and the observable failure:
- Environment: WordPress version + PHP version (sandbox label in the brief).
- Steps: activate → open screen X → perform action Y.
- Success: screen loads, no fatal, success state visible.
- Form: activated / blocked; PHP fatal or white screen? (yes/no); log excerpt or recording at the stall.
Isolated sandboxes per tester matter here: you are not poisoning a shared staging site when 8.3 blows up on activation. Each tester gets a fresh install with the version label in the mission.
When to stop widening the matrix
You do not need twelve PHP builds before every dot release. For most plugin ships:
- Oldest PHP you still claim in the header
- One modern PHP (what most new hosts default to)
- Re-run the same brief after you fix a fatal — do not invent a new study
Pattern on two versions beats theater on six. If both pass the same mission, ship or move to the next risk (conflicts, uninstall, etc.).
On QATested
WordPress plugin testing on QATested provisions an isolated sandbox per tester — upload the zip, each person gets wp-admin on a clean install. Brief for PHP/runtime risk: name the version in the mission, one activation + admin path, recording + form. Setup: WordPress plugin sandboxes.
Related: what Plugin Check can’t see in wp-admin · how to test with real users (no staging) · sandboxes vs handing out your staging login.