PHP 8.5 Deprecations Cheat Sheet — What to Fix Before Upgrading
Your PHP 8.5 upgrade went fine — until the logs started filling up with E_DEPRECATED notices. Most are trivial fixes. A handful will require a read-through. All of them become hard errors in PHP 9.0, so it's worth an hour now.
This is a cheat sheet. Every PHP 8.5 deprecation, the exact replacement, and the Rector command to automate the scan.
PHP 8.5 Deprecations at a Glance
Here's the full list before we dive in:
| Deprecated | Replace With |
|---|---|
(boolean) cast |
(bool) |
(integer) cast |
(int) |
(double) cast |
(float) |
(binary) cast |
(string) |
Backtick operator `cmd` |
shell_exec('cmd') |
__sleep() / __wakeup() |
__serialize() / __unserialize() |
curl_close() |
Remove it |
curl_share_close() |
Remove it |
xml_parser_free() |
Remove it |
case 'x'; semicolon |
case 'x': colon |
null as array offset |
"" empty string |
| Incrementing non-numeric strings | str_increment() |
mysqli_execute() |
mysqli_stmt_execute() |
socket_set_timeout() |
stream_set_timeout() |
$http_response_header |
http_get_last_response_headers() |
Now the detail on the ones that actually catch people off guard.
Non-Canonical Cast Names
The most common source of deprecation notices on legacy codebases. PHP has always accepted verbose aliases for scalar casts — PHP 8.5 stops tolerating them:
// Deprecated in PHP 8.5
$active = (boolean) $value;
$count = (integer) $value;
$price = (double) $value;
$data = (binary) $value;
Switch to the canonical short forms:
// Correct — use these
$active = (bool) $value;
$count = (int) $value;
$price = (float) $value;
$data = (string) $value;
These appear most often in older codebases and generated code. Rector handles them automatically.
The Backtick Operator
PHP's backtick syntax executes a shell command and returns the output. It's always been a thin alias for shell_exec(). PHP 8.5 deprecates the alias:
// Deprecated
$files = `ls -la /var/www`;
$output = `git log --oneline -5`;
Replace with the explicit function call:
// PHP 8.5 forward
$files = shell_exec('ls -la /var/www');
$output = shell_exec('git log --oneline -5');
The behaviour is identical — shell_exec() returns a string, or null on failure. If you need a non-null fallback, pair it with the null coalescing operator: shell_exec('cmd') ?? ''.
__sleep() and __wakeup() Magic Methods
PHP 8.5 soft-deprecates the old serialisation magic methods in favour of the pair introduced in PHP 7.4:
// Deprecated
class User
{
public function __sleep(): array
{
// return property names to serialise
return ['id', 'email'];
}
public function __wakeup(): void
{
// reconnect resources after deserialisation
}
}
Switch to __serialize() and __unserialize():
// PHP 8.5 forward
class User
{
public function __serialize(): array
{
// return a key => value array
return ['id' => $this->id, 'email' => $this->email];
}
public function __unserialize(array $data): void
{
$this->id = $data['id'];
$this->email = $data['email'];
}
}
Note: __sleep() returned property names; __serialize() returns a key-value array. They're not drop-in replacements — read the body before renaming. If you still support PHP 7.x, implement both pairs simultaneously; PHP will prefer the newer methods when available.
curl_close(), curl_share_close(), and xml_parser_free()
These have been no-ops since PHP 8.0, when CurlHandle, CurlShareHandle, and XMLParser became proper objects freed automatically by the garbage collector. PHP 8.5 makes the deprecation official:
// Deprecated — these do nothing in PHP 8.0+
$ch = curl_init('https://example.com');
curl_exec($ch);
curl_close($ch); // Remove this line
Remove the close calls. That's it.
Other PHP 8.5 Deprecations Worth Flagging
Case statement semicolons. PHP accepted semicolons as terminators for years. That stops now:
// Deprecated — semicolon terminates the case
switch ($status) {
case 'active';
break;
}
// Correct — colon
switch ($status) {
case 'active':
break;
}
null as array offset. Using null as an array key or passing it to array_key_exists() is now deprecated. Replace with an empty string "".
Incrementing non-numeric strings. The behaviour of $str++ on non-numeric strings was always ambiguous. PHP 8.5 deprecates it — use str_increment() instead.
mysqli_execute(). Always an alias for mysqli_stmt_execute(). Use the canonical name.
socket_set_timeout(). Use stream_set_timeout() — they have been identical since PHP 4.
$http_response_header. This magic variable populated after file_get_contents() HTTP calls is now deprecated. Use the new http_get_last_response_headers() function instead.
Scan Your Codebase with Rector
Don't fix these manually in a large codebase. Rector 2.x handles most of them automatically:
composer require --dev rector/rector
Create a rector.php config at the project root targeting PHP 8.5:
<?php
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([
__DIR__ . '/app',
__DIR__ . '/tests',
])
->withPhpSets(php85: true);
Dry-run first to see what Rector would change without touching any files:
php vendor/bin/rector --dry-run
Apply the changes:
php vendor/bin/rector
Rector catches the cast renames, curl_close() removals, __sleep()/__wakeup() conversions, and the backtick operator. For the remaining items — null array offsets, case semicolons — a quick grep -rn is usually faster than writing a custom Rector rule.
Pairing this with PHPStan at level max in your Laravel app catches the type-related issues Rector won't touch. If you want these fixes committed automatically before every push, the same pattern from running Laravel Pint with Git pre-commit hooks works for any CLI tool you want to wire into your commit workflow.
Gotchas and Edge Cases
__serialize() and __sleep() have different return types. __sleep() returned an array of property names. __serialize() returns an array of key-value pairs. Blindly renaming the method breaks serialisation — read the body first.
shell_exec() and backticks are identical in behaviour. Both return null on failure, both swallow stderr. If you need stderr, switch to proc_open() rather than shell_exec().
Rector's --dry-run is not exhaustive. Dynamic array operations using variables that might be null won't all be flagged. A manual review of your array handling is a good idea alongside the automated scan.
Nothing is removed yet. PHP 8.5 deprecates — it doesn't remove. All of these still work and just emit E_DEPRECATED. The hard removals are PHP 9.0. You have time, but cleaning up while the diff is small beats a large migration later.
Wrapping Up
Most PHP 8.5 deprecations are mechanical find-and-replace. Run Rector, check the output, commit. The __sleep()/__wakeup() migration needs a careful read since the method contract changed.
If you're taking stock of PHP 8.5 as a whole, the PHP 8.5 pipe operator and the new array_first() and array_last() functions are the additions most worth picking up at the same time — both replace patterns that have existed unchanged in codebases for years.
Steven is a software engineer with a passion for building scalable web applications. He enjoys sharing his knowledge through articles and tutorials.