PHP 8.5 Deprecations Cheat Sheet — What to Fix Before Upgrading

5 min read

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.

php
php-8.5
deprecations
upgrade
rector
Steven Richardson

Steven is a software engineer with a passion for building scalable web applications. He enjoys sharing his knowledge through articles and tutorials.