Pressready logo

Pressready

Is your WordPress site ready for the next PHP or WordPress version? Scan the whole installed stack and find out before you upgrade.

Every plugin · every theme · every mu-plugin — not just your own code

CI PHP >= 7.4 WordPress compatible License GPL-2.0-or-later

The problem

Upgrading PHP or WordPress on a site with dozens of third-party plugins is a gamble. There is no way to know what breaks until it breaks — in production. Pressready changes that.

Whole-stack scan

Scans every plugin, theme, and mu-plugin in wp-content — not just your own code. Third-party code breaks too.

PHP + WordPress in one pass

Two engines, one command. PHPCompatibility handles the PHP axis; a custom sniff handles the WordPress deprecation axis.

Honest severity model

Fatals (white screen) are separated from risky behaviour changes and mere deprecations — no false alarms, no hiding real problems.

Per-component attribution

Findings group by the plugin or theme that owns them, sorted fatals-first, so you know exactly which package to fix or replace.

Baseline for legacy sites

Snapshot today's known issues and fail CI only on new findings — adopt on a site with pre-existing tech debt without drowning in noise.

WP-CLI native

Run wp pressready scan from the WordPress root on a live site. No database needed — readiness scanning is entirely static.

How it works

One scan, two engines. Pressready runs PHPCompatibility and its own WordPress sniff in a single PHP_CodeSniffer pass.

PHP axis — PHPCompatibility

The battle-tested community standard. Catches removed functions, deprecated language features, and behavioural changes for any PHP version from 5.3 to 8.4. Runs via the phpcompatibility/phpcompatibility-wp wrapper that skips functions WordPress itself polyfills — no false positives.

WP axis — custom sniff + dataset

A custom PHPCS sniff driven by an authoritative, regenerable JSON dataset of every WordPress core deprecation — extracted straight from core's own _deprecated_* annotations and @deprecated docblocks. Hundreds of unique entries covering functions, methods, classes, hooks, and files back to WordPress 1.5.

The dataset is regenerable against any wordpress-develop checkout: php bin/gen-wp-deprecations.php --src=/path/to/wordpress-develop/src --out=data/wp-deprecations.json. Re-run each WordPress release to refresh for free.

Severity model

Every finding is classified into one tier. The verdict and --fail-on key on true fatals only, so the report never cries wolf.

LevelMeaningSource
fatalA symbol removed by the target version — a PHP symbol gone, or a WordPress core API removed by the target WP version → call to undefined → white screenPHPCompatibility · Pressready sniff (message says "removed since")
riskyBehavioural / correctness change that still runs but may produce wrong resultsOther PHPCompatibility errors
phpPHP feature deprecated (not yet removed) by the target versionPHPCompatibility warnings
wpWordPress core API deprecated (but still shimmed) by the target WP versionPressready sniff

The WP dataset records the WordPress version it was generated from. Target a --wp newer than the dataset and Pressready warns on stderr (results may be incomplete — regenerate) rather than imply a clean bill of health. Point a run at a custom or newer dataset with PRESSREADY_DATASET=/path/to/wp-deprecations.json.

Install

Run the single-file PHAR for zero-setup use anywhere, or install via Composer to pin it per project.

Standalone PHAR — no Composer

A single self-contained file that bundles the whole PHPCS toolchain — nothing to composer install, and no chance of clashing with a project's own pinned squizlabs/php_codesniffer. Ideal for CI, a server, or a project you don't want to add a dev dependency to.

# Download the latest release and run it
curl -L https://github.com/itzmekhokan/pressready/releases/latest/download/pressready.phar -o pressready.phar
chmod +x pressready.phar
./pressready.phar --php=8.4 --wp=6.9 --path=wp-content

On first run it extracts its bundled toolchain to a cache dir; later runs reuse it. Every flag works exactly as the Composer-installed CLI.

Homebrew — macOS / Linux

Installs the released PHAR and runs it with a Homebrew-managed php (pulled in as a dependency) — nothing else to set up. Upgrade with brew upgrade pressready.

brew tap itzmekhokan/pressready
brew install pressready

pressready --php=8.4 --wp=6.9 --path=wp-content

Composer

Installs the Pressready PHPCS standard automatically via dealerdirect/phpcodesniffer-composer-installer, in two tiers depending on how new a PHP version you need to target.

Default — no config (PHP ≤ 8.1)

The standard install needs no stability config. It pulls the stable PHPCompatibility engine, which detects everything up to PHP 8.1 plus the full WordPress deprecations axis. Most sites can stop here.

# Add to your project as a dev dependency
composer require --dev itzmekhokan/pressready

Targeting PHP 8.2–8.4 — opt in to the pre-release engine

PHP 8.2–8.4 detection needs the pre-release PHPCompatibility 10 engine. Allow dev stability and request the pre-release engine explicitly (Composer prefers stable otherwise, so you must name it):

# Configure your project once
composer config minimum-stability dev
composer config prefer-stable true
composer config --no-plugins allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
composer config --no-plugins allow-plugins.phpcsstandards/phpcsutils true

# Add Pressready and pin the pre-release engine
composer require --dev itzmekhokan/pressready phpcompatibility/phpcompatibility-wp:3.0.0-alpha2

# Verify the standards registered
vendor/bin/phpcs -i   # lists "Pressready" and "PHPCompatibilityWP"

Requires PHP 7.4+. The PHP 8.2–8.4 engine (PHPCompatibility 10.x) is pre-release — this opt-in step goes away once it ships stable.

Global install — scan many sites from one place

Prefer one shared install for ad-hoc scans across all your local sites? Install it globally. Configure global Composer once (same stability rules — drop the minimum-stability/prefer-stable lines if you only target PHP ≤ 8.1), then require it:

# Configure global Composer once (the dev-stability lines are only needed for PHP 8.2–8.4)
composer global config minimum-stability dev
composer global config prefer-stable true
composer global config --no-plugins allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
composer global config --no-plugins allow-plugins.phpcsstandards/phpcsutils true

# Install globally (drop the explicit engine pin if you only target PHP ≤ 8.1)
composer global require itzmekhokan/pressready phpcompatibility/phpcompatibility-wp:3.0.0-alpha2

# Put the global bin on your PATH (zsh; use ~/.bashrc for bash)
echo 'export PATH="$PATH:$(composer global config home)/vendor/bin"' >> ~/.zshrc && source ~/.zshrc

# Verify, then scan any site by path
pressready --php=8.2 --path=/path/to/site/wp-content

Global is ideal for a developer's local sweeps across many sites. For CI and team reproducibility, still use composer require --dev per project so the engine version is pinned in each composer.lock.

Quick start

Run the standalone CLI directly after composer install.

# Both axes — the headline question.
vendor/bin/pressready --php=8.4 --wp=6.9 --path=wp-content

# One axis at a time.
vendor/bin/pressready --php=8.4 --path=.
vendor/bin/pressready --wp=6.9  --path=.

# Scan just your own code — repeat --path (theme + brand mu-plugins).
vendor/bin/pressready --php=8.4 --wp=6.9 --path=wp-content/themes/acme --path=wp-content/mu-plugins/acme

# Delta: only what newly breaks upgrading FROM 6.4 TO 6.9.
vendor/bin/pressready --wp=6.9 --since=6.4 --path=.

# CI gate: fail the build only on real fatals.
vendor/bin/pressready --php=8.4 --wp=6.9 --fail-on=fatal --path=.

# Machine-readable output.
vendor/bin/pressready --php=8.4 --wp=6.9 --format=json --path=.

# GitHub PR inline annotations.
vendor/bin/pressready --php=8.4 --wp=6.9 --format=github --path=.

# SARIF for code-scanning dashboards (GitHub Advanced Security).
vendor/bin/pressready --php=8.4 --wp=6.9 --format=sarif --path=. > pressready.sarif

# GitLab Code Quality report — inline MR annotations.
vendor/bin/pressready --php=8.4 --wp=6.9 --format=gitlab --path=. > gl-code-quality-report.json

# A PHP upgrade path — everything that breaks across 8.1 → 8.4.
vendor/bin/pressready --php=8.1-8.4 --path=.

# Compatibility matrix — how far can I safely upgrade, and what breaks first?
vendor/bin/pressready --php=7.4-8.4 --matrix --path=.

# Deprecations are hidden by default — add --all to list the full tail.
vendor/bin/pressready --php=8.4 --wp=6.9 --all --path=.

# Drop deprecations from the report and exit code entirely.
vendor/bin/pressready --php=8.4 --wp=6.9 --ignore-on=deprecated --path=.

--format: grouped (default) · table · summary · json · sarif · github · gitlab
--fail-on: fatal · risky · deprecated  ·  --ignore-on: hide findings at/below a level (the inverse)
--path is repeatable (or a comma list) to scan several targets in one pass · -h/--help prints the full flag reference · -V/--version prints the version.
Exit codes: 0 clean · 1 findings at/above --fail-on · 2 usage/config error (bad flag, missing path, or zero files scanned — a gate fails loudly instead of passing vacuously).

Compatibility matrix

Instead of "does this one target break my site?", --matrix answers the question every site owner actually has — how far can I safely upgrade, and what's the first thing that breaks? It grades each version in the requested span from a single scan.

vendor/bin/pressready --php=7.4-8.4 --matrix --path=wp-content

  Pressready — compatibility matrix — PHP 7.4–8.4
  Scanned: wp-content

  PHP
    7.4    ✓ clear
    8.0    ✗ 1 fatal
    8.1    ✗ 1 fatal
    8.2    ✗ 1 fatal
    8.3    ✗ 1 fatal
    8.4    ✗ 1 fatal

  ✓ Safe through PHP 7.4.
  ✗ First break: PHP 8.0 (1 fatal).

Pass a range (--php=7.4-8.4) to grade across it; a single --php=8.4 grades just that version. --format=json emits the per-version grid plus safe_through / first_break for each axis, so CI can consume the ceiling. --fail-on is honoured — a span containing a fatal exits non-zero. The WordPress axis is graded too, and lights up as the dataset gains removal data.

Large stacks & performance

Built to scan a full enterprise wp-content — hundreds of plugins, tens of thousands of files — without crawling.

Speed

# Parallel is ON by default (auto-detects CPU cores).
vendor/bin/pressready --php=8.4 --wp=6.9 \
  --path=wp-content --parallel=8

# Cache results — re-scans only reprocess changed files.
vendor/bin/pressready --php=8.4 --wp=6.9 \
  --path=wp-content --cache

Focus & signal

# The 10 worst components, or just one.
vendor/bin/pressready --php=8.4 --wp=6.9 \
  --path=wp-content --top=10
vendor/bin/pressready --php=8.4 --wp=6.9 \
  --path=wp-content --only=woocommerce

Un-fixable / generated code is skipped by default (vendor, node_modules, build, dist, *.min.php, tests, *.blade.php). Add your own with --ignore=<patterns> or turn the built-ins off with --no-default-ignore. Repeated identical findings collapse into one line with an (×N) count. The long deprecation tail is hidden by default — each component header keeps its deprecation count, and --all lists them in full.

Config file

Commit shared defaults to a .pressready.json in your project root — CLI flags always win.

{
  "php": "8.4",
  "wp": "6.9",
  "fail-on": "fatal",
  "paths": ["wp-content/themes/acme", "wp-content/mu-plugins/acme"],
  "ignore": ["*/cache/*", "*/languages/*"],
  "top": 20
}

Use paths: [] for multiple scan targets. Unknown keys are reported as a warning so a typo never silently changes your scan scope. Any --path on the command line replaces the file's path/paths entirely.

WP-CLI — wp pressready scan

Same engine, run from the WordPress root. Defaults to the site's wp-content. Runs before WordPress boots — no database required.

Register the command

Add to your project's wp-cli.yml:

require:
  - vendor/itzmekhokan/pressready/wp-cli.php

Or pass --require inline:

wp --require=vendor/itzmekhokan/pressready/wp-cli.php pressready scan --php=8.4 --wp=6.9

Usage

# Scan with both axes — the main question.
wp pressready scan --php=8.4 --wp=6.9

# Scan a specific directory instead of the default wp-content.
wp pressready scan wp-content/plugins --php=8.4

# WordPress axis only, JSON output.
wp pressready scan --wp=6.9 --format=json

# CI gate: exit non-zero when fatals are present.
wp pressready scan --php=8.4 --wp=6.9 --fail-on=fatal

# Delta: only what newly deprecates upgrading from 6.4.
wp pressready scan --wp=6.9 --since=6.4

# Compatibility matrix: how far can I upgrade, and what breaks first?
wp pressready scan --php=7.4-8.4 --matrix

# Table format (default), summary, CSV, or count.
wp pressready scan --php=8.4 --format=table
wp pressready scan --php=8.4 --format=summary
wp pressready scan --php=8.4 --format=csv

Options

scan [<path>]Directory to scan. Default: this site's wp-content. Positional — WP-CLI reserves --path for the install dir.
--php=<ver>Target PHP version (e.g. 8.4). Reports removed/deprecated PHP features.
--wp=<ver>Target WordPress version (e.g. 6.9). Reports deprecated core APIs.
--since=<ver>With --wp, only what newly deprecates upgrading from this version.
--format=<fmt>table (default) · summary · json · csv · yaml · count
--fail-on=<lvl>Exit non-zero when findings at/above this level exist: fatal · risky · deprecated
--ignore-on=<lvl>Hide findings at/below this level (the inverse of --fail-on).
--parallel=<n>phpcs worker processes. Default: auto (CPU cores); 1 to disable.
--cache[=<file>]Cache results so re-scans only reprocess changed files.
--ignore=<patterns>Extra comma-separated path patterns to skip (--no-default-ignore drops the built-ins).
--only / --topFocus the report: one component by name, or the N worst.
--allList every deprecation inline. By default the tail is hidden so fatals/risky stay scannable (--verbose is an alias).
--config=<file>Load defaults from a JSON config file (CLI flags still win).

Baseline & CI

Adopt Pressready on a legacy site without drowning in pre-existing findings — snapshot today's known issues, then fail CI only on new ones.

GitHub Action recommended

The published Docker action bakes Pressready and its PHPCS 4.x toolchain into the image, so your repo carries no Composer boilerplate and can't clash with a project's own squizlabs/php_codesniffer ^3.x pin. A whole CI gate becomes two lines — .pressready.json and the baseline stay repo-local.

- uses: actions/checkout@v4
- uses: itzmekhokan/pressready@v1
  with:
    baseline: .pressready-baseline.json
    fail-on: fatal
    format: github

Inputs: config (default .pressready.json) · baseline · fail-on (fatal·risky·deprecated) · format · php · wp · working-directory. Output: exit-code. Pin @v1 for the moving major tag or @v1.6.0 for an immutable release; the image is pulled pre-built from ghcr.io/itzmekhokan/pressready.

Run anywhere with Docker

The image is dual-mode: a GitHub Action when GitHub runs it, a plain CLI runner everywhere else — no Composer, no local PHP, no toolchain clash. Mirrored to Docker Hub (docker.io/itzmekhokan/pressready) for environments that can't pull GHCR.

docker run --rm -v "$PWD":/src -w /src ghcr.io/itzmekhokan/pressready:1 \
  --php=8.4 --wp=6.9 --path=wp-content

GitLab CI

--format=gitlab emits a Code Quality (Code Climate JSON) report — publish it as a codequality artifact and GitLab annotates every finding inline on the merge request.

pressready:
  image: ghcr.io/itzmekhokan/pressready:1
  variables: { GITHUB_ACTIONS: "" }
  script:
    - pressready --php=8.4 --wp=6.9 --path=wp-content
        --format=gitlab > gl-code-quality-report.json
  artifacts:
    reports: { codequality: gl-code-quality-report.json }

Other CI & your editor

Any runner that can pull the image gates the same way — Bitbucket Pipelines (image: ghcr.io/itzmekhokan/pressready:1) and Jenkins (agent { docker { image '…' } }) both run pressready … --fail-on=fatal and honour the exit codes. Pressready is also a phpcs standard, so VS Code / PhpStorm can surface findings inline — point the phpcs inspection at Pressready/ruleset.xml. See the README for copy-paste recipes.

Pre-commit hook optional

Entirely opt-in — a project adds it to its own .pre-commit-config.yaml to scan staged PHP before each commit. Runs via the image (Docker only) or a repo: local hook pointing at vendor/bin/pressready / pressready.phar (no Docker).

repos:
  - repo: https://github.com/itzmekhokan/pressready
    rev: v1.6.0
    hooks:
      - id: pressready
        args: [--php=8.4, --wp=6.9, --path=wp-content]

Or run the binary yourself

Standalone CLI

# Snapshot existing findings.
vendor/bin/pressready \
  --php=8.4 --wp=6.9 \
  --generate-baseline

# From now on, fail only on NEW findings.
vendor/bin/pressready \
  --php=8.4 --wp=6.9 \
  --baseline --fail-on=fatal

GitHub Actions

- name: Pressready scan
  run: |
    php bin/pressready \
      --php=8.4 --wp=6.9 \
      --baseline \
      --fail-on=fatal \
      --format=github \
      --path=wp-content

Findings are keyed by path → signature (sniff code + message with line numbers neutralised), so the baseline survives line shifts and reordering. Paths normalise relative to the directory you run from (your repo root), so a baseline generated locally matches one generated in CI regardless of whether --path was absolute, relative, or scoped to a subdirectory. --baseline=<file> / --generate-baseline=<file> accept a custom path.

Per-finding suppression

Uses native PHPCS inline comments — no custom syntax to learn.

// Suppress everything on this line.
create_function( '$x', 'return $x;' ); // phpcs:ignore

// Suppress a specific sniff.
get_postdata( 1 ); // phpcs:ignore Pressready.WordPress.Deprecated.DeprecatedFunction

Roadmap

The core scanning engine and WP-CLI integration are complete and production-ready.

PhaseScopeStatus
1WP-deprecations dataset generator + authoritative JSON datasetdone
2Custom PHPCS sniff + reporter (grouped / summary / json), version gate, exit codesdone
3--php= via bundled PHPCompatibility; unified severity model (fatal / risky / php / wp)done
4Per-component attribution, baseline, --format=github, inline suppressiondone
5wp pressready scan WP-CLI command (before_wp_load, all formats, --fail-on)done
6Enterprise scale: parallel scanning, result cache, path ignores, duplicate collapsing, --only/--top, .pressready.json config, SARIF output, PHP version ranges, docblock-driven dataset coveragedone
7Docker GitHub Action (ghcr.io image, @v1) — one-line CI adoption with the PHPCS toolchain baked into the imagedone
8v1.5: compatibility matrix (--matrix); WordPress removed-by-target findings are fatal + dataset-version awareness; single-file pressready.phar distributabledone
9Integration tooling: docker run CLI mode, opt-in pre-commit hook & GitLab CI Code Quality report (--format=gitlab)done
10+Cross-version removed data; DataViews admin report; WP.org distributionplanned

Found Pressready useful? A ⭐ helps other WordPress teams find it before their next upgrade — and shapes what ships next.

Star on GitHub