Guide

PHP deprecation warnings after PHP 8.x upgrade — what to fix first

You upgraded to PHP 8.x and the site still loads, but wp-content/debug.log is now filling with "Deprecated:" notices. None of them are fatal yet. The real question is which ones will become fatals under a stricter PHP, and which you can safely ignore.

1. Problem

The host bumped PHP, maybe from 7.4 to 8.1, maybe from 8.0 to 8.2, and now every page load appends a dozen new lines to wp-content/debug.log. Almost all of them start with PHP Deprecated:. The site still works. Nothing is down. But the log that used to be short and skimmable is now thousands of lines a day, and somewhere in that flood is the one warning that actually matters.

If you searched "wordpress php deprecated notices after upgrade" or "how to hide php deprecation warnings", most of what comes back tells you to set error_reporting to exclude E_DEPRECATED, or to drop display_errors and move on. That makes the log quiet again, which feels like the problem is solved. It is not. Suppressing the message does not change what PHP does with the deprecated code path, and it throws away the one piece of information that told you where the fragile spots in your stack are.

The real question is not "how do I stop seeing these." It is "which of these will actually break something, and in what order should I fix them." Some of what is filling the log is WordPress core code that will be patched upstream without you doing anything. Some of it is a plugin that already shipped a compatible release you have not installed. Some of it is your own custom code, and nobody else is going to fix that one for you.

The flood of deprecation notices surfaces as the wp_php_warnings_total signal. This guide covers reading the log to separate the three ownership categories, prioritizing by frequency and by whether the deprecated path is hit on every request or once a month, and understanding why "harmless now" does not mean "harmless on the next PHP upgrade."

2. Impact

A deprecation notice does not abort the request, so it is tempting to treat the whole flood as cosmetic. It is not free, and ignoring it has a cost that compounds every time the runtime gets stricter.

  • It buries the warnings that predict fatals. A genuine PHP Warning: naming a symbol that is about to vanish looks identical, at a glance, to a routine PHP Deprecated: notice from a plugin you already know is fine. Once the log is thousands of lines a day, the one line that mattered is statistically invisible.
  • Today's notice is tomorrow's fatal. PHP does not keep deprecated behavior forever. A pattern that logs a notice on 8.1 raises a fatal type error on 8.2 or 8.3. The code that is "fine for now" is fine only until the next runtime bump, which on managed hosting can happen without your say-so.
  • A deprecation on a hot path costs real overhead on every request. A notice fired once during a rare admin action is noise. The same notice fired from a hook that runs on every front-end page view is thousands of error-handler calls and log writes a day, on every single request your visitors make.
  • Blanket suppression trades visibility for silence. Turning off E_DEPRECATED reporting hides the plugin-specific and your-own-code notices along with the harmless core ones. When one of those turns fatal on the next PHP bump, you have no log trail leading back to it.

3. Why It’s Hard to Spot

WordPress's debug log treats every PHP error level the same way: it appends the message to wp-content/debug.log with a timestamp and nothing else. A PHP Deprecated: line from WordPress core sits next to a PHP Warning: from a plugin, next to a PHP Notice: you wrote yourself, in the order they fired, with no ranking and no grouping.

Nothing in default WordPress tells you whether a given line is core, a plugin, a theme, or your own functions.php. The log line names a file path, and reading that path tells you the answer, but only if you check every distinct line by hand. On a site with 25 active plugins and a debug log growing by a few thousand lines a day, doing that manually does not happen. Most sites either read the log once, panic at the volume, and suppress the whole category, or never read it at all.

There is also no default way to tell how often a given deprecation actually fires. A notice that logs once during a monthly maintenance script and a notice that logs on every single front-end request look exactly the same as a single line in the file. Without a count, you cannot tell the one that matters from the one that does not.

Site Health, the built-in WordPress diagnostic page, does not help here either. It reports the current PHP version and flags a handful of known-incompatible plugins from a curated list, but it does not read your error log, does not tell you which of your specific plugins are emitting deprecations right now, and says nothing about your own theme code. The gap between "PHP was upgraded" and "here is what that broke or is about to break" is not covered by anything WordPress ships by default.

4. Cause

The wp_php_warnings_total signal fires whenever the Logystera WordPress plugin's error handler catches a PHP-level warning, notice, or deprecation on a request. A deprecation notice increments the same counter as a warning, tagged with an error_type label so the two are distinguishable after the fact, alongside the file and line the message named.

The trigger for the flood is almost always one event: the PHP runtime moved. That move lands as its own signal, and the causes underneath the flood split into three ownership categories, each with a different fix:

  • The PHP version changed under the site. A host-driven upgrade or a manual bump moves the runtime to a version that deprecates syntax the previous version tolerated silently. This is the event that starts everything else and it is recorded as wp_environment_changes_total{field="php_version", direction="upgrade"}.
  • WordPress core hit a deprecated pattern. Core itself sometimes lags a PHP release, and a small number of its own functions log deprecations on the newest PHP versions until the next core release fixes them. This is wp_php_warnings_total with a file path under wp-includes or wp-admin.
  • A plugin or theme has not caught up to the new PHP version. The most common source by far. The plugin's maintainer either already shipped a PHP-8-ready release you have not installed, or has not shipped one yet. This is wp_php_warnings_total with a file path under wp-content/plugins or wp-content/themes.
  • Your own theme or a custom must-use plugin uses an old pattern. Code your team wrote, in a child theme's functions.php or a bespoke plugin, that predates the PHP version now running. Nobody upstream is going to patch this one. This is wp_php_warnings_total with a file path under your own theme or a plugin folder your team maintains.

Logystera emits the environment change and every warning after it on the same timeline, labeled by file and line, which is what turns "the log grew" into "here are the 6 distinct sources, ranked by how often each one fires."

5. Solution

5.1 Diagnose (logs first)

Do not suppress error reporting first. Read the log, group by source, and rank by frequency. That ranking is the prioritization, and it takes minutes.

1. Confirm the trigger and its timing. Check when the PHP version actually moved, so you know whether today's flood lines up with it.

# Current runtime, straight from the host
php -v

# WordPress's own view of the same thing
wp eval 'echo phpversion();' --allow-root

A typical line in the debug log after the bump looks like this. Note the file and line: those two fields are what you group by next.

[14-Jul-2026 09:12:44 UTC] PHP Deprecated: Creation of dynamic property Legacy_Widget::$cache_key is deprecated in /wp-content/plugins/legacy-widget/class-legacy-widget.php on line 91

2. Pull every distinct deprecation and count them. The count is the prioritization signal. A line that fires once a day is background noise. A line that fires on every request needs to move to the top of the list regardless of which category it falls in.

# Distinct deprecation messages, ranked by how often each fires
grep -oE "PHP Deprecated:.*on line [0-9]+" /var/www/wp-content/debug.log \
  | sort | uniq -c | sort -rn | head -n 20

3. Split the ranked list by ownership. For each of the top lines, the file path tells you which category it belongs to. That decides the fix, not the message text.

# Core deprecations
grep "PHP Deprecated:" /var/www/wp-content/debug.log | grep -E "/wp-includes/|/wp-admin/"

# Plugin/theme deprecations, broken out per plugin folder
grep "PHP Deprecated:" /var/www/wp-content/debug.log | grep "/wp-content/plugins/" \
  | grep -oE "/wp-content/plugins/[^/]+" | sort | uniq -c | sort -rn

# Your own code (adjust the path to your child theme or custom mu-plugin)
grep "PHP Deprecated:" /var/www/wp-content/debug.log | grep -E "/wp-content/(themes/your-child-theme|mu-plugins)/"

In Logystera, the same three steps collapse into one query: wp_environment_changes_total anchors the timestamp the PHP version moved, and wp_php_warnings_total broken out by file and line gives you the ranked, ownership-tagged list without touching SSH.

5.2 Root Causes

The message text and the file path together tell you which of the three buckets a given line falls into, and how urgent it is.

  • A path under wp-includes or wp-admin means WordPress core itself. Cause: core has not yet caught up to the newest PHP version. You do not fix this, core does, on its own release schedule. Signal: wp_php_warnings_total on a core file path.
  • Creation of dynamic property ... is deprecated from a plugin or theme path means it still relies on untyped-property behavior PHP 8.2 stopped tolerating silently. Cause: the plugin has not shipped, or you have not installed, a PHP-8.2-ready release. Signal pair: wp_environment_changes_total then wp_php_warnings_total.
  • implicit conversion from float ... to int, strlen(): Passing null to parameter, or a deprecated each() call from your own theme or mu-plugin path is code your team owns. Cause: a pattern your code used that PHP 8.1+ now flags. Nobody else will fix it. Signal: wp_php_warnings_total on your own file path.
  • The same file and line appearing on nearly every request, rather than intermittently, means the deprecated call sits on a hot code path: a hook that fires on every page load rather than an admin-only screen. Cause is unchanged, but priority goes up regardless of which of the three buckets it falls in.

5.3 Fix

The fix depends entirely on who owns the code. Matching the fix to the wrong bucket wastes effort: you cannot patch WordPress core, and you should not try to work around a plugin's internals when an update already exists.

Core deprecations: wait them out. Do not patch core files directly, changes are lost on the next update and core will resolve its own deprecations on its release cadence. Keep WordPress itself current so you get the fix as soon as it ships.

Plugin and theme deprecations: update to a PHP-8-ready release. Check the plugin's changelog for a version that explicitly mentions PHP 8.1 or 8.2 compatibility, and update on staging first. If the maintainer has not shipped one, that is the signal to open a support request with them or start evaluating a maintained replacement, not to patch their code in place.

# Update a single plugin once you've confirmed a PHP-8-ready release exists
wp plugin update legacy-widget --allow-root

# Confirm the installed version afterward
wp plugin get legacy-widget --field=version --allow-root

Your own code: patch it. These are the only ones you can and should fix directly, since no update from a third party is coming. Declare the property, add the null check, or replace the deprecated function with its modern equivalent.

// Before: triggers "Creation of dynamic property" on PHP 8.2
class Site_Cache {
    public function set_key( $key ) {
        $this->cache_key = $key; // never declared
    }
}

// After: declare the property explicitly
class Site_Cache {
    private string $cache_key = '';

    public function set_key( $key ) {
        $this->cache_key = $key;
    }
}

Do not blanket-suppress E_DEPRECATED. Turning the error level off in error_reporting hides your own code's deprecations along with the harmless core ones, and removes the log trail you will want the next time PHP tightens the rule further.

5.4 Verify

Prioritization is done when the count for each fixed source drops to zero, not when one manual page load looks clean. A plugin update can leave the deprecation in a code path you did not happen to visit.

# Re-run the same ranked count and confirm the fixed lines dropped off the top
grep -oE "PHP Deprecated:.*on line [0-9]+" /var/www/wp-content/debug.log \
  | sort | uniq -c | sort -rn | head -n 20

# Confirm the specific symbol you patched or updated is gone
grep -c "Legacy_Widget::\$cache_key" /var/www/wp-content/debug.log

In the metric, watch wp_php_warnings_total for the fixed file and line specifically, not the aggregate total. The aggregate can stay elevated because of an unrelated plugin you have not gotten to yet, which is expected: the goal of this pass is the ranked list trending down item by item, not the whole log going silent in one step. Watch wp_php_fatals_total over the following days too. It should stay flat. That is the actual confirmation that clearing the noise did not just hide a warning that was about to escalate.

6. How to Catch This Early

The deprecation flood itself is not the danger. Losing track of which lines in it are drifting toward a fatal is.

This issue surfaces as wp_php_warnings_total.

Nothing in default WordPress alerts you when a deprecation count jumps after a PHP bump, or tells you which of the new lines is core, which is a plugin with a pending update, and which is code your own team wrote. Site Health shows the current PHP version on a page nobody checks daily. The debug log grows quietly in the background until either disk space becomes a problem or, months later, a future PHP upgrade turns one specific line into a fatal, and by then nobody remembers it was ever a deprecation notice.

Reading the log and counting each distinct message as a signal, grouped by file and correlated against the PHP version change that triggered it, is what turns "the log is noisy again" into a ranked, ownership-tagged list you can work down in an afternoon instead of a flood you learn to ignore until it bites you on the next upgrade.

7. Related Silent Failures

  • wp_php_warnings_total spike: a sudden jump rather than a planned-upgrade flood, usually a plugin update rather than a PHP bump. Covered in the PHP warning spike guide.
  • wp_environment_changes_total on php_version: the event that starts this entire chain. See environment drift for how the before/after PHP values get recorded.
  • wp_php_fatals_total after an unattended deprecation: the escalation this guide exists to prevent. Full trace in the PHP fatal causal chain guide.
  • wp_php_fatals_total on a live site: what a missed deprecation looks like once it stops being a warning. See the fatal error guide for diagnosing it once it has already fired.

See what's actually happening in your WordPress system

Connect your site. Logystera starts monitoring within minutes.

Copyright © 2026 Logystera. Operated by 1969730 Ontario Inc., an Ontario, Canada corporation. All rights reserved.