Guide
PHP deprecation warnings after PHP 8.x upgrade — what to fix first
1. Problem
You upgraded WordPress to PHP 8.x. The site loads. Then you open the homepage and the top of the page is a wall of yellow text:
Deprecated: Creation of dynamic property My_Plugin_Class::$cache_key is deprecated in /wp-content/plugins/some-plugin/class-cache.php on line 142
Deprecated: strpos(): Passing null to parameter #1 ($haystack) of type string is deprecated in /wp-content/themes/your-theme/functions.php on line 88
Deprecated: Return type of WP_Block_List::current() should either be compatible with Iterator::current(): mixed, or the #[\ReturnTypeWillChange] attribute should be used in /wp-includes/class-wp-block-list.php on line 130
You're searching "wordpress site flooded with deprecation warnings" because the admin bar is hidden under the noise, the checkout page leaks PHP errors above , and a customer just emailed a screenshot. Nothing is technically broken — pages render, orders go through — but the site looks like a development server in production.
Everything looked fine on PHP 7.4. The host pushed PHP 8.1 (or 8.2, or 8.3) overnight, and now half your stack is shouting about types it never had to think about.
2. Impact
Deprecation warnings are not "just warnings."
- They render to the page. If
display_errors=On(the default on many shared hosts), warnings appear above the doctype. That breaksheader()calls, sets cookies after output, and can cause "headers already sent" follow-on failures during checkout, login, and REST writes. - They poison caches. Page caches and CDN edges store the polluted HTML. The error stays visible to anonymous visitors long after the underlying request finished.
- They bloat error logs. A single deprecated call inside a loop can write tens of thousands of lines per minute. Disk fills, log rotation thrashes, and real fatal errors get buried.
- They preview the next outage. In a future PHP version, today's deprecation is tomorrow's
E_ERROR. PHP 8.4 already promotes several PHP 8.0 deprecations to fatals. The warning is the early signal — when it becomes aphp.fatal, the page is white. - They break SEO and trust. Googlebot indexes pages with PHP error text. Customers screenshot it.
This is why "wordpress deprecation notice fix" is a panic search. The site is technically up, but visibly degraded, and getting closer to a hard break with every minor PHP release.
3. Why It’s Hard to Spot
Three reasons it slips through:
- WordPress hides them by default in some configs.
WP_DEBUG=falsesuppresses display, buterror_logstill fills. If you only watch the dashboard, the noise is invisible — until a customer reports it or the disk fills. - Uptime checks and HTTP status are useless here. A page returning 200 OK with deprecation text smeared across the header is "up" by every standard monitor. New Relic, UptimeRobot, Pingdom — all green.
- Volume hides priority. Once one plugin is noisy, it produces tens of thousands of identical warnings per hour. The handful of different warnings — the ones from less-used code paths, the ones that will become fatal first — are invisible in the flood. You can't fix them in priority order if you can't see them.
This is the silent-failure pattern: the system reports OK, the user sees broken, and the logs hold the truth nobody is reading.
4. Cause
A php.warning signal with error_type=deprecation represents a single emission of an E_DEPRECATED or E_USER_DEPRECATED notice during PHP request execution. PHP 8 introduced — and PHP 8.1, 8.2, 8.3 each tightened — a long list of language behaviors that 7.x silently tolerated:
- Dynamic properties (
$obj->foo = 1on a class without a declared$fooor#[AllowDynamicProperties]) — deprecated in 8.2, fatal in 9.0. - Passing
nullto non-nullable internal function parameters (e.g.strpos(null, '/'),trim(null),explode(',', null)) — deprecated in 8.1. - Implicit float-to-int conversions that lose precision — deprecated in 8.1.
- Return type covariance violations — Iterator and ArrayAccess implementations that don't match the new core signatures.
utf8_encode/utf8_decode— deprecated in 8.2.- Optional parameters declared before required ones — deprecated in 8.0.
${var}string interpolation (the curly form without$) — deprecated in 8.2.
Each of these triggers a php.warning signal at the moment WordPress, your theme, or a plugin executes the offending code path. The signal carries the file, line, message, and error_type (for this guide: deprecation).
A second signal matters here: wp.environment. The plugin emits this when PHP version, WordPress version, or active theme/plugin set changes. The day of your upgrade, you'll see one wp.environment event, immediately followed by a sustained burst of php.warning signals with error_type=deprecation. That's the fingerprint of a PHP version bump exposing latent code.
When a deprecation has already been promoted to a hard error in your PHP version (or when output buffering breaks because warnings printed first), the same code path produces php.fatal instead. Same root cause, different signal — and now the page is broken, not just ugly.
5. Solution
5.1 Diagnose (logs first)
Stop guessing. Read the right logs in the right order.
Step 1 — Confirm the PHP version actually changed.
php -v
# PHP 8.2.18 (cli) (built: ...)
In WordPress logs, this corresponds to a wp.environment signal. If you ingest into Logystera, filter for event_type=wp.environment around the suspected upgrade time and confirm the php_version field flipped.
Step 2 — Locate the PHP error log.
The path varies by host:
# Common locations
tail -f /var/log/php-fpm/error.log
tail -f /var/log/php8.2-fpm.log
tail -f /var/www/html/wp-content/debug.log # if WP_DEBUG_LOG=true
tail -f /var/log/nginx/error.log # FPM relays here on some stacks
Or ask PHP directly:
php -i | grep error_log
Step 3 — Count and rank the deprecations. This is the only step that matters for prioritization.
grep -c "PHP Deprecated" /var/log/php-fpm/error.log
# 184321
Now group by source file and message:
grep "PHP Deprecated" /var/log/php-fpm/error.log \
| sed -E 's/.*PHP Deprecated:\s+(.*)\s+in\s+(\S+)\s+on line.*/\2 :: \1/' \
| sort | uniq -c | sort -rn | head -20
Output looks like:
92118 /wp-content/plugins/old-cache/class-cache.php :: Creation of dynamic property OldCache::$ttl is deprecated
41203 /wp-content/themes/legacy/functions.php :: strpos(): Passing null to parameter #1 ($haystack) of type string is deprecated
8104 /wp-content/plugins/forms/forms.php :: Return type of FormsIterator::current() should either be compatible...
412 /wp-content/plugins/seo-plugin/lib/parser.php :: utf8_encode() is deprecated
Each of those rows is a php.warning signal with error_type=deprecation. The top entry is producing 92,118 signals — that's where you start.
Step 4 — Correlate with wp.environment. Every plugin or theme update triggers wp.environment. If a deprecation began after a specific update, the offender is named in the diff.
grep "wp.environment" /var/www/html/wp-content/debug.log | tail -20
Step 5 — Watch for promotion to fatal. Some deprecations have already been escalated in your PHP minor version. Those produce php.fatal, not php.warning:
grep "PHP Fatal error" /var/log/php-fpm/error.log | tail
# PHP Fatal error: Uncaught TypeError: strlen(): Argument #1 ($string) must be of type string, null given
A php.fatal after a PHP upgrade is almost always a deprecation that crossed the line — fix it first, before more code paths hit it.
This ranked list — most-frequent deprecations by file, plus any fatals — is your fix queue. Don't chase warnings in the order they appear in the log. Chase them in the order they're firing.
5.2 Root Causes
(see root causes inline in 5.3 Fix)
5.3 Fix
Fix in this order. Each cause maps to a specific signal pattern.
Cause 1 — Dynamic property deprecations from a single plugin or theme (php.warning, message contains "Creation of dynamic property", file under wp-content/plugins/ or wp-content/themes/).
This is almost always the top entry in your ranked grep output. Two paths:
- Update the plugin/theme. Most active plugins fixed dynamic-property warnings in 2023. Check WordPress.org for a release dated after PHP 8.2 (Dec 2022).
- If unmaintained, patch with
#[AllowDynamicProperties]. Add the attribute above the class declaration as a temporary buffer. Open an issue with the author. - If abandoned, replace it. A plugin that hasn't been updated for PHP 8.2 in two years won't survive PHP 9.
Cause 2 — null passed to internal string functions (php.warning, message contains "Passing null to parameter").
Common in older themes that read $_GET, $_POST, or get_post_meta() without coalescing. Fix the call site:
// Before
$slug = strtolower(trim($_GET['slug']));
// After
$slug = strtolower(trim($_GET['slug'] ?? ''));
If you can't edit the file (managed theme), update the parent or switch to a child theme override. This deprecation will be a fatal in a future PHP minor — it's not optional.
Cause 3 — Return type covariance (php.warning, message contains "Return type of ... should either be compatible with").
Iterator, ArrayAccess, Countable implementations from older code. Fix:
public function current(): mixed { ... }
// Or, if you need to keep the old signature for back-compat:
#[\ReturnTypeWillChange]
public function current() { ... }
Cause 4 — utf8_encode / utf8_decode (php.warning, message contains "utf8_encode() is deprecated").
Replace with mb_convert_encoding($s, 'UTF-8', 'ISO-8859-1'). These functions are removed in PHP 9.
Cause 5 — Output already broken because warnings printed first (php.fatal "headers already sent", or login/REST endpoints returning corrupt JSON).
Once warnings are emitted before headers, secondary fatals appear. Fix the underlying deprecation first; the cascade clears. As a stopgap, in wp-config.php:
@ini_set('display_errors', 0);
define('WP_DEBUG_DISPLAY', false);
define('WP_DEBUG_LOG', true);
This stops the visible bleed and keeps logs flowing. It is a tourniquet, not a fix — the deprecations still execute, still cost CPU, and still log.
Cause 6 — Latent issue exposed by a wp.environment change you didn't make. Managed hosts auto-bump PHP minor versions. If you didn't change anything but deprecations appeared, check wp.environment events: a php_version field flip explains it.
5.4 Verify
You're looking for two outcomes:
- The
php.warningsignal volume forerror_type=deprecationdrops — ideally to zero, realistically to single digits per hour for the warnings you can't yet fix. - No new
php.fatalsignals appear from the same files.
Concrete checks:
# Snapshot before your fix
grep -c "PHP Deprecated" /var/log/php-fpm/error.log
# 184321
# Apply fix, restart php-fpm
sudo systemctl reload php8.2-fpm
# Truncate or rotate so you measure clean
sudo truncate -s 0 /var/log/php-fpm/error.log
# Wait 30 minutes under normal traffic, then re-count
grep -c "PHP Deprecated" /var/log/php-fpm/error.log
# 47 <-- only remaining low-frequency offenders
Healthy state in logs looks like:
- The top-ranked file from your earlier grep no longer appears in the count.
- No
PHP Fatal errorlines from the same path. - WordPress page source no longer contains the literal string
Deprecated:above the doctype:
curl -s https://your-site.example/ | head -5 | grep -i "Deprecated"
# (no output = healthy)
Timeframe. Give it 30 minutes of normal traffic for cached pages to expire and for cron-driven and REST-driven code paths to run. Some deprecations only fire during scheduled tasks or admin actions — wait at least one full hour before declaring it clean.
If the php.warning rate stays flat for 24 hours and no new php.fatal appears, the upgrade is stable.
6. How to Catch This Early
Fixing it is straightforward once you know the cause. The hard part is knowing it happened at all.
This issue surfaces as php.warning.
The real problem isn't fixing one deprecation. It's that the next one is already in your codebase, waiting for the next PHP minor version to expose it. By default, nothing alerts you. The page renders. The status checks pass. The host's auto-upgrade flips the version, and you find out from a customer.
This failure surfaces as php.warning with error_type=deprecation, which Logystera detects on the first emission. The flow:
- Baseline. Logystera tracks
php.warningrate per entity. A site running cleanly emits zero or near-zero deprecations. - PHP version change. The WordPress plugin emits
wp.environment. Logystera correlates: a suddenphp.warningspike within minutes of awp.environmentevent with a changedphp_versionfield is the textbook upgrade-broke-something pattern. - Promotion to fatal. When a deprecation crosses into
php.fatal, the rule fires immediately — not after the customer screenshots the checkout page. - Per-file ranking. Because every
php.warningcarries the source file, the dashboard shows you the top offenders ordered by volume — the same ranking you'd build manually withgrep | sort | uniq -c, but live.
Without this, the warnings exist only in a log file no one reads. With it, the upgrade window from "deprecated" to "fatal" is something you watch close, not something that surprises you.
7. Related Silent Failures
Deprecation noise rarely travels alone. If you're seeing php.warning error_type=deprecation, check for these adjacent patterns:
php.fatalafter PHP minor upgrade — the deprecation that finally crossed the line. Same root cause family, harder symptom.http.request5xx spikes correlated withphp.warning— when warnings break headers, REST writes and admin-ajax start failing. The 5xx is downstream of the deprecation.wp.environmentunexpected change — host auto-upgraded PHP without telling you. The first signal you'll see is a fresh wave ofphp.warningminutes later.memory_near_limitfrom log-file growth — a noisy deprecation in a hot path can produce gigabytes of error log per day, eventually starving disk and triggering memory pressure on shared hosts.wp.cronmissed_schedule — when warnings break output buffering, WP-Cron HTTP self-pings fail silently, and scheduled posts stop publishing.
Each of these is its own diagnostic path. They cluster around the same upgrade event because PHP version changes touch every code path at once. The cheapest place to catch the whole cluster is at the first php.warning — before the rest of them light up.
See what's actually happening in your WordPress system
Connect your site. Logystera starts monitoring within minutes.