Upgrading PHP is a bit like tidying a cupboard. You think you are doing a simple “version bump”, then you open the door and a small avalanche of old habits falls on your feet.
PHP 8.5 is a fun release because it gives you shiny new features, but it also nudges you toward cleaner, safer code. Here are practical tips that help you get the benefits without turning your upgrade into a month-long saga.
1) Start with a boring upgrade plan (it will save you later)
Do this in order:
- Upgrade locally or in a dev container first
- Run your full test suite
- Turn on deprecation warnings in CI for at least one run
- Deploy to a staging environment that looks like production
- Only then upgrade production
If you do nothing else, do this. PHP upgrades are rarely hard because of PHP. They are hard because of your dependencies and old code paths nobody tests.
2) Hunt the deprecations first, because they are easy wins
PHP 8.5 deprecates a couple of patterns that still show up in older codebases:
- Backticks as a shortcut for running shell commands
- Non-canonical cast names like
(integer)and(boolean)
These are great “search and replace” style fixes. Grep your codebase, fix them, move on.
3) Use the pipe operator for readability, not for showing off
The new pipe operator (|>) lets you pass a value through a chain of callables left to right.
It shines when you have those “nested function lasagna” blocks nobody wants to read.
$title = ' PHP 8.5 Released ';
$slug = $title
|> trim(...)
|> (fn($s) => str_replace(' ', '-', $s))
|> (fn($s) => str_replace('.', '', $s))
|> strtolower(...);
Tip: keep the chain short. If you need nine steps, it might be better as a named function. Pipes are best when they make code calmer, not cleverer.
4) Stop using parse_url() for anything serious
If you have ever had a URL edge case ruin your day, you know the feeling. PHP 8.5 introduces a built-in URI extension that gives you a more robust way to parse and work with URLs.
use Uri\Rfc3986\Uri;
$uri = new Uri('https://example.com/path?x=1');
$host = $uri->getHost();
Tip: this is especially useful in API gateways, redirect handlers, OAuth flows, and anything parsing user input. Less handmade URL logic is usually a security win.
5) Put #[NoDiscard] on the functions people love to misuse
This is one of those features that feels small but stops real bugs.
If a function returns a value that must be used, add the attribute. PHP will warn when the return value is ignored.
#[\NoDiscard]
function saveUser(array $data): bool {
// ...
return true;
}
Tip: use it on safety-critical methods like “verify token”, “save”, “write”, “commit”, “lock”, “begin transaction”. Then your codebase gently trains developers to do the right thing.
And if you intentionally do not care about the return value, use (void) to make it explicit.
6) If you like immutable objects, clone() just got way nicer
PHP 8.5 lets you update properties while cloning an object. This makes the “with” pattern much cleaner, especially for readonly classes.
readonly class Color {
public function __construct(
public int $red,
public int $green,
public int $blue,
public int $alpha = 255,
) {}
public function withAlpha(int $alpha): self {
return clone($this, ['alpha' => $alpha]);
}
}
Tip: this is great for DTOs, value objects, request models and anything you want to treat as “data you replace, not data you mutate”.
7) Replace your “array key gymnastics” with array_first() and array_last()
These are tiny additions that make code nicer immediately.
$lastEvent = array_last($events);
$firstUser = array_first($users);
Tip: pair them with ?? for clean fallbacks.
$lastEventName = array_last($events)['name'] ?? 'None yet';
8) Make attributes and configuration a bit less awkward
PHP 8.5 allows static closures and first-class callables in constant expressions. In plain terms, this can make attribute usage and configuration patterns cleaner because you can embed a bit more logic safely.
Tip: Use this for simple policies and checks you currently express as strings that nobody trusts.
9) Learn what changed in error output, then tune how you log
PHP 8.5 adds backtraces to certain fatal errors, like hitting the maximum execution time. This is great for debugging, but you still need to be thoughtful about what you display.
Tip: keep detailed errors in logs, not on screen in production. You want rich diagnostics for engineers and a calm “Something went wrong” for users.
10) If you do lots of HTTP calls, take a look at persistent cURL share handles
PHP 8.5 adds persistent cURL share handles, which can help reuse certain connection-related data across requests in some environments.
Tip: treat this like a performance tool, not a default. Measure before and after. If you are running lots of outbound API calls and are chasing latency, it might be worth experimenting.
The simplest way to “feel” PHP 8.5 benefits
If you want a quick checklist that makes the upgrade worth it:
- Fix the deprecations (backticks and cast names)
- Use the URI extension anywhere you parse important URLs
- Add
#[NoDiscard]to your most bug-prone APIs - Refactor one ugly nested transformation into a pipe chain
- Replace a couple of “array end” hacks with
array_last()
That is enough to make PHP 8.5 feel like more than a version bump.