Diagnostic procedure

Preparing a legacy PHP codebase for a supported branch

A PHP version update is a package change; the code that calls removed functions does not care. Five steps for measuring a legacy codebase before the runtime moves — the dialect inventory, the removals that fail loudly and the changes that fail silently, the plugin metadata that cannot answer the question, and how to verify without a test suite.

Installing a supported branch is a package change and it takes minutes. The code that called a function the branch no longer has is a different problem: nothing fails until the runtime moves, and when it does the failure lands on whichever request reaches that call first. The pass below is what turns that from a risk into a list, and it is worth doing before the runtime changes rather than after.

Step 1: inventory the dialect, not the files

What matters is which constructs the code uses, counted per pattern with the file and line each appears on. The removal list is published per target branch, so the honest starting point is that list applied to the codebase rather than a grep of whatever you remember being removed.

The PHP 8.0 removals, counted per pattern over application, theme and plugin code
                      # Every hard removal in PHP 8.0, per pattern, with counts
for fn in create_function each hebrevc convert_cyr_string money_format \
          ezmlm_hash restore_include_path get_magic_quotes_gpc \
          get_magic_quotes_runtime fgetss; do
  printf '%-28s %s\n' "$fn" "$(grep -rInE "\b$fn[[:space:]]*\(" \
    --include='*.php' --include='*.inc' --include='*.phtml' . | wc -l)"
done

# Removed constant, and the mb-regex aliases that went with the extension change
grep -rIn 'FILTER_SANITIZE_MAGIC_QUOTES' --include='*.php' . | wc -l
grep -rInE '\bmb(ereg|eregi|ereg_replace|eregi_replace|split)[[:space:]]*\(' \
  --include='*.php' . | wc -l
                    

The counts are the deliverable, not the diagnosis. Zero on a pattern is information; a pattern with four hundred instances concentrated in two plugin directories is a different project from four hundred spread across application code, and which one it is decides the schedule. Run it over the themes and plugins as well, because that is where the concentration usually is.

Step 2: separate the removals that fail loudly from the changes that fail silently

The loud ones are easy and everyone plans for them: call a removed function and the request ends. The quiet ones are what outlive a migration. In the same PHP 8.0 guide, implode() stops accepting its parameters in reverse order, and the result of sorting functions can differ when elements compare as equal — neither raises anything, and both can change data that was written years ago and read now.

Step 3: do not trust the metadata — measure it

A plugin's declared minimum PHP version is a floor: it says what the plugin will install on. It does not say what has been tested, and it cannot, because there is no field for that. Querying the directory makes the gap visible rather than removing it.

The declared minimums of the 100 most popular plugins, read from the plugin directory API
                      curl -s "https://api.wordpress.org/plugins/info/1.2/?action=query_plugins&request%5Bper_page%5D=100" \
  | jq -r '.plugins[] | [(.requires_php // "undeclared"), .slug] | @tsv' \
  | cut -f1 | sort | uniq -c | sort -rn | head
                    

Read on 24 October 2026, that returns 93 plugins declaring a minimum and 7 declaring nothing; of the 93, 88 declare 7.4 or lower and none declares above 8.3. The conclusion is not that 88 plugins are broken — most of them probably run fine on a current branch. It is that the field cannot tell you which do, so the compatibility position has to come from running them, and the same applies to WordPress's own statements: the platform recommends PHP 8.3 or greater while its core update API reports a minimum of 7.4.

Step 4: make the deprecations visible on the branch you are already on

Most legacy estates suppress notices and warnings because they were noisy years ago, which means the evidence of the next branch's removals is already being generated and thrown away. Turn reporting up on a staging copy of the current branch and read the log: dynamic properties deprecated in 8.2 unless the class opts in, implicitly nullable parameters in 8.4, the removed error and session constants that keep appearing in configuration.

Expect two categories and treat them differently. Most items are a pattern repeated across years of templates, and one replacement fixes all of them. A minority are a symptom of something that should be fixed rather than silenced — a value being written to an object that was never declared, a type that only works because the engine is lenient — and those are the ones worth the reading time, because a deprecation is a schedule rather than a fault.

Step 5: verify without a test suite

Legacy estates rarely have one, and building a framework first is the wrong order. What the migration needs is coverage of the paths that carry traffic: the route inventory, the forms, the authenticated actions, the imports and the endpoints something else calls. Five assertions per path are usually enough — the request returns 200, it does not log a fatal, a known value appears in the response, the write reached the database, and nothing new appeared in the deprecation log.

A pass over the paths that matter, on the branch that is about to be replaced
                      while read -r path; do
  code=$(curl -s -o /dev/null -w '%{http_code}' "https://staging.example.com$path")
  printf '%s %s\n' "$code" "$path"
done < routes.txt | tee before.txt

# Read the application log for the same window, not only the status codes.
# A 200 with a fatal in the log is not a pass.
                    

Run it before the code changes to establish the baseline, after each batch to confirm nothing regressed, and again once the runtime has moved. The comparison is the evidence, and it is also what makes the next upgrade cheaper: the harness outlives the migration, and the estate that could not answer 'does this work?' in March can answer it in a minute afterwards.

Where this applies
Client proof

What Clients Say; And Where To Verify It

Our client reviews are published on our public Google Business Profile. We reproduce them verbatim, with attribution, so you can read the original feedback and verify it directly at the source. We believe reviews should speak for themselves.

5.05 out of 5 stars15 Google reviewsReviews are hosted on our Google Business Profile.
  • 5 out of 5 stars

    Soludome effortlessly migrated my large websites to a more cost-effective server while significantly boosting performance. They continue to provide exceptional ongoing support, including performance monitoring, SEO improvements, and timely updates. I highly recommend Soludome to anyone in need of a top-notch web development professional.

    Dominic Robinson
  • 5 out of 5 stars

    Soludome recently handled a forum upgrade and migration for me, and they delivered outstanding results. Communication was excellent, which was crucial as we worked through various tasks, testing, and post-launch issues. I highly recommend Soludome and will definitely rely on their expertise for future projects.

    Stacy Conaway

Read all client reviews

If this is the problem you are looking at

The procedure is more useful applied than read. Tell us what you are seeing and we will say whether it sounds like the same fault.