PHP 8.5: tutte le novità dell’ultima versione con esempi

Il 20 novembre 2025 è stato rilasciato PHP 8.5, l’ultimo major update del linguaggio che alimenta gran parte del web moderno. Come sviluppatore fullstack specializzato in Laravel, Vue.js e Inertia.js, seguo da vicino ogni evoluzione dell’ecosistema PHP. Questa release porta con sé novità che incidono concretamente sulla qualità del codice che scriviamo ogni giorno: un nuovo operatore pipe, un’estensione URI nativa, il supporto alla clonazione con modifica delle proprietà e molto altro. Vediamole insieme, con esempi pratici.

1. Pipe Operator |> — Addio alle funzioni annidate

Questa è probabilmente la novità più attesa dalla community. Il pipe operator (|>) permette di concatenare l’output di una funzione come input della successiva, leggendo il codice da sinistra a destra anziché dall’interno verso l’esterno.

Prima (PHP 8.4 e precedenti):

$title = ' PHP 8.5 Released ';

$slug = strtolower(
    str_replace('.', '',
        str_replace(' ', '-',
            trim($title)
        )
    )
);

// Output: "php-85-released"

Dopo (PHP 8.5):

$title = ' PHP 8.5 Released ';

$slug = $title
    |> trim(...)
    |> (fn($s) => str_replace(' ', '-', $s))
    |> (fn($s) => str_replace('.', '', $s))
    |> strtolower(...);

// Output: "php-85-released"

L’operatore valuta da sinistra a destra: il lato destro accetta un callable con un singolo parametro e riceve il valore dal lato sinistro. Per le funzioni built-in che accettano un solo argomento si usa la sintassi trim(...); per funzioni con più argomenti si wrappa in una arrow function tra parentesi.

Perché è rilevante in Laravel? Pensate a quante volte nei controller o nei service manipolate stringhe, array o trasformazioni di dati passando il risultato da una funzione all’altra. Con il pipe operator il codice diventa lineare e dichiarativo, perfettamente in linea con la filosofia di Laravel.

2. Nuova estensione URI — Parsing moderno degli URL

Per anni ci siamo affidati a parse_url(), una funzione nota per i suoi limiti con input malformati e per la mancanza di aderenza agli standard. PHP 8.5 introduce un’estensione URI nativa e sempre disponibile, basata sulle librerie uriparser (RFC 3986) e Lexbor (WHATWG URL).

Prima (PHP 8.4):

$components = parse_url('https://esempio.it/blog/php-85');

echo $components['host'];   // "esempio.it"
echo $components['path'];   // "/blog/php-85"
// Nessuna validazione, nessuna normalizzazione

Dopo (PHP 8.5 — RFC 3986):

use Uri\Rfc3986\Uri;

$uri = new Uri('https://esempio.it/blog/php-85');

echo $uri->getScheme();  // "https"
echo $uri->getHost();    // "esempio.it"
echo $uri->getPath();    // "/blog/php-85"

// Oggetti immutabili con wither pattern
$newUri = $uri->withPath('/blog/laravel-12');
echo $newUri->toString();
// "https://esempio.it/blog/laravel-12"

Con lo standard WHATWG URL:

use Uri\WhatWg\Url;

$url = new Url('https://esempio.it/blog/php-85');

echo $url->getScheme();       // "https"
echo $url->getUnicodeHost();  // "esempio.it"
echo $url->getAsciiHost();    // "esempio.it"

Le due classi sono readonly, supportano il parsing tramite costruttore o metodo factory parse(), e lanciano eccezioni su input non validi. L’estensione RFC 3986 supporta anche la rappresentazione “raw” e “normalized-decoded”, utile quando lavorate con URL percent-encoded. Questa novità è particolarmente interessante per chi sviluppa API RESTful con Laravel, dove la manipolazione corretta degli URI è fondamentale.

3. Clone With — Clonazione con modifica delle proprietà

Con l’introduzione delle readonly properties in PHP 8.1, la clonazione degli oggetti è diventata macchinosa: non era possibile modificare direttamente una proprietà readonly sul clone. PHP 8.5 risolve il problema trasformando clone in una funzione che accetta un array associativo di proprietà da sovrascrivere.

Prima (PHP 8.4):

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
    {
        // Workaround necessario
        $values = get_object_vars($this);
        $values['alpha'] = $alpha;
        return new self(...$values);
    }
}

Dopo (PHP 8.5):

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]);
    }
}

$blue = new Color(79, 91, 147);
$transparentBlue = $blue->withAlpha(128);

echo $transparentBlue->alpha; // 128

Se lavorate con DTO (Data Transfer Objects) o Value Objects nei vostri progetti Laravel, questa feature vi semplificherà enormemente la vita. Pensate ai DTO usati con Inertia.js per passare dati strutturati ai componenti Vue: ora il pattern “wither” diventa nativo e pulito.

4. Attributo #[\NoDiscard] — Non ignorare i valori di ritorno

Quante volte avete chiamato una funzione dimenticandovi di usare il valore restituito? Con #[\NoDiscard], PHP 8.5 emetterà un warning quando il valore di ritorno viene ignorato.

#[\NoDiscard("Il risultato contiene eventuali errori")]
function processOrders(array $orders): array
{
    $results = [];
    foreach ($orders as $order) {
        $results[] = $order->process();
    }
    return $results;
}

// ⚠️ Warning: il valore di ritorno non viene utilizzato
processOrders($pendingOrders);

// ✅ Corretto
$results = processOrders($pendingOrders);

// ✅ Ignora intenzionalmente con cast (void)
(void) processOrders($pendingOrders);

Il cast (void) è una novità di PHP 8.5 che permette di indicare esplicitamente che state ignorando il risultato. Se sviluppate pacchetti Laravel o librerie condivise, aggiungere #[\NoDiscard] ai metodi critici è un ottimo modo per rendere le vostre API più sicure.

5. Closure nelle espressioni costanti — Più potere agli attributi

Le closure statiche e i first-class callable possono ora essere usati nelle espressioni costanti. Questo apre scenari molto interessanti, specialmente con gli attributi PHP.

// Prima: serviva un oggetto Expression
#[AccessControl(
    new Expression('request.user === post.getAuthor()')
)]
public function update(Request $request, Post $post): Response
{
    // ...
}

// Dopo (PHP 8.5): closure direttamente nell'attributo
#[AccessControl(static function (
    Request $request,
    Post $post,
): bool {
    return $request->user === $post->getAuthor();
})]
public function update(Request $request, Post $post): Response
{
    // ...
}

In un contesto Laravel, pensate alla possibilità di definire logiche di autorizzazione, validazione o configurazione direttamente negli attributi dei controller, senza dover creare classi dedicate per ogni regola semplice.

6. array_first() e array_last() — Finalmente!

Due funzioni richieste dalla community da anni. Restituiscono il primo o l’ultimo valore di un array, oppure null se l’array è vuoto.

// Prima (PHP 8.4)
$lastEvent = $events === []
    ? null
    : $events[array_key_last($events)];

// Dopo (PHP 8.5)
$firstEvent = array_first($events);
$lastEvent = array_last($events);

// Funziona con array associativi
$config = ['db' => 'mysql', 'cache' => 'redis', 'queue' => 'sqs'];
echo array_first($config); // "mysql"
echo array_last($config);  // "sqs"

// Restituisce null per array vuoti
$empty = [];
var_dump(array_first($empty)); // null — perfetto con ??
$fallback = array_first($empty) ?? 'default';

Sì, Laravel ha Arr::first() e le collection hanno ->first(), ma avere queste funzioni nativamente nel linguaggio è un vantaggio in termini di performance e leggibilità, soprattutto quando lavorate al di fuori del contesto Eloquent.

7. cURL Share Handles persistenti

I nuovi handle condivisi persistenti evitano il costo di reinizializzazione delle connessioni cURL tra le richieste PHP. Questo è particolarmente utile per applicazioni ad alto throughput.

// PHP 8.5: handle condiviso persistente
$sh = curl_share_init_persistent([
    CURL_LOCK_DATA_DNS,
    CURL_LOCK_DATA_CONNECT,
]);

$ch = curl_init('https://api.esempio.it/v1/orders');
curl_setopt($ch, CURLOPT_SHARE, $sh);

// Può riutilizzare la connessione da una richiesta SAPI precedente
curl_exec($ch);

Se la vostra applicazione Laravel effettua molte chiamate HTTP esterne (API di terze parti, microservizi, webhook), questa ottimizzazione può ridurre sensibilmente la latenza complessiva.

8. Nuova direttiva INI max_memory_limit — Un tetto alla memoria

Chi sviluppa applicazioni PHP sa bene che è possibile sovrascrivere il memory_limit a runtime con ini_set(), arrivando persino a rimuovere ogni limite con il valore -1. Questo può essere rischioso, soprattutto in ambienti di hosting condiviso o quando non si ha piena visibilità sulle risorse del server.

PHP 8.5 introduce la direttiva max_memory_limit nel file php.ini, che agisce come un tetto massimo non sovrascrivibile dal codice applicativo. Anche se uno script tenta di alzare il limite, il valore non potrà mai superare quello definito in questa nuova direttiva.

Configurazione nel php.ini:

// php.ini
max_memory_limit = 256M
memory_limit = 128M

Comportamento nel codice PHP:

ini_set('memory_limit', '256M');  // ✅ OK, entro il limite
ini_set('memory_limit', '512M');  // ⚠️ Warning, viene impostato a 256M
ini_set('memory_limit', '-1');    // ⚠️ Warning, viene impostato a 256M

// Buona pratica: verificare il limite prima di agire
$maxLimit = ini_get('max_memory_limit');
if ($maxLimit !== false) {
    // Adatta il comportamento al tetto disponibile
}

Per chi lavora con Laravel, questa novità è particolarmente rilevante quando si gestiscono job pesanti nelle code (import CSV massivi, elaborazione immagini, generazione PDF) o quando si deploya su piattaforme cloud dove la memoria è una risorsa a consumo. Sapere che esiste un tetto invalicabile rende più prevedibile il comportamento dell’applicazione in produzione e previene crash inaspettati.

9. Altre novità degne di nota

PHP 8.5 porta con sé anche una serie di miglioramenti più contenuti ma comunque rilevanti per lo sviluppo quotidiano:

Backtrace nei Fatal Error: gli errori fatali (come il superamento del tempo massimo di esecuzione) ora includono un backtrace completo, rendendo il debug molto più rapido.

// Ora vedrete qualcosa come:
// Fatal error: Maximum execution time exceeded in example.php on line 6
// Stack trace:
// #0 example.php(6): heavyComputation()
// #1 example.php(10): processData()
// #2 {main}

Proprietà statiche con visibilità asimmetrica: potete ora avere una proprietà statica che è leggibile pubblicamente ma scrivibile solo privatamente.

Proprietà final con constructor promotion: utile per garantire che una proprietà non venga sovrascritta nelle classi figlie.

Closure::getCurrent(): un nuovo metodo che semplifica la ricorsione nelle funzioni anonime.

Cookie partitioned: setcookie() e setrawcookie() supportano ora la chiave “partitioned” (CHIPS), importante per la gestione della privacy nei browser moderni.

OPcache sempre compilato: OPcache non è più opzionale ed è ora compilato staticamente in PHP, come ext/date o ext/pcre. L’abilitazione resta controllata via INI.

Costante PHP_BUILD_DATE: una nuova costante che restituisce la data di compilazione del build PHP in uso, utile per il debugging in produzione.

echo PHP_BUILD_DATE; // "Nov 20 2025 10:30:45"

10. Deprecazioni da tenere d’occhio

Come ogni release, PHP 8.5 introduce alcune deprecazioni che è importante conoscere prima di aggiornare i vostri progetti:

Operatore backtick deprecato: l’uso dei backtick come alias di shell_exec() è ora deprecato. Usate esplicitamente shell_exec().

Cast non canonici deprecati: (boolean), (integer), (double) e (binary) sono deprecati. Usate (bool), (int), (float) e (string).

__sleep() e __wakeup() soft-deprecated: il consiglio è migrare verso __serialize() e __unserialize().

Case con punto e virgola: terminare uno statement case con ; anziché : è ora deprecato.

null come offset di array: usare null come chiave di array o con array_key_exists() è deprecato. Usate una stringa vuota.

Rimozione di disable_classes: l’impostazione INI disable_classes è stata rimossa.

11. PHP 8.5 e Laravel: compatibilità e consigli

Laravel 12, rilasciato a febbraio 2025, supporta ufficialmente PHP 8.2–8.4. Come di consueto, il supporto per la nuova versione GA viene aggiunto poco dopo il rilascio, una volta che le dipendenze del framework risultano compatibili. Se state valutando l’aggiornamento, ecco il mio consiglio pratico:

Se non siete ancora su PHP 8.4, partite da lì: è il trampolino più sicuro, dato che Laravel 12 lo supporta pienamente. Questo vi permette di individuare e aggiornare pacchetti ed estensioni datati, evitando di affrontare troppi cambiamenti in un colpo solo. Una volta stabilizzati su 8.4, testate la vostra applicazione su 8.5 in un ambiente di staging. La maggior parte delle novità di PHP 8.5 è additiva, ma le deprecazioni e i nuovi warning possono far emergere problemi in codice legacy o in pacchetti di terze parti non ancora aggiornati.

Concentrate i test sulle funzionalità critiche per i vostri utenti: tempi di risposta sulle pagine principali, flussi di autenticazione, code di job e — se usate Inertia.js — verificate che il passaggio dati tra Laravel e Vue funzioni correttamente sotto carico.

PHP 8.5 non è una release rivoluzionaria, ma è una release matura e pragmatica. Il pipe operator migliora la leggibilità, l’estensione URI modernizza un’area critica del linguaggio, il clone with semplifica i pattern immutabili e le piccole aggiunte come array_first()/array_last() eliminano boilerplate quotidiano. Per chi lavora con lo stack Laravel + Vue + Inertia.js, queste novità si traducono in codice più pulito, API più sicure e un’esperienza di sviluppo complessivamente migliore.

Se avete bisogno di supporto per aggiornare i vostri progetti a PHP 8.5, valutare la compatibilità del vostro stack o sviluppare nuove applicazioni sfruttando al meglio queste novità, contattatemi: sarò felice di aiutarvi.


Fonti: php.net — PHP 8.5 Release Announcement, PHP RFC Wiki