PHP 8.5 in Laravel-Apps: was seinen Platz verdient, was du zuerst prüfst
PHP 8.5 ist am 20. November 2025 erschienen, und eine Release-Ankündigung zählt Features auf, sagt dir aber nicht, welche davon in eine Laravel-Codebasis gehören. Einige verändern tatsächlich, wie sich Anwendungscode liest. Andere lösen ein Problem, das Laravel vor Jahren mit einem Helper gelöst hat. Und ein paar Deprecations solltest du suchen, bevor du ein Projekt umstellst. Hier ist diese Sortierung, Feature für Feature.
Was sich im Anwendungscode seinen Platz verdient
array_first() und array_last(). Sie liefern den ersten oder letzten Wert eines Arrays, oder null, wenn es leer ist, und lassen sich deshalb mit ?? kombinieren. Bisher holte man das letzte Event mit $events === [] ? null : $events[array_key_last($events)]. In einer Collection hast du schon first() und last(), und es gibt Arr::first(), aber viel Laravel-Code arbeitet mit einfachen Arrays: Config-Werten, dekodiertem JSON, Request-Input. Genau dort helfen die beiden.
Der Pipe-Operator. |> reicht einen Wert durch eine Kette von Callables, von links nach rechts, ohne Zwischenvariablen. Das Beispiel von php.net macht aus einem Titel einen Slug:
$slug = $title
|> trim(...)
|> (fn($s) => str_replace(' ', '-', $s))
|> (fn($s) => str_replace('.', '', $s))
|> strtolower(...);
// ' PHP 8.5 Released ' becomes 'php-85-released'
Ehrlich gesagt: In einer Laravel-App würdest du Str::slug($title) schreiben, und Fluent Strings sowie die Pipeline-Klasse decken viele solcher Ketten bereits ab. Die Pipe lohnt sich dort, wo es keinen Helper gibt, für eine Folge eigener kleiner Funktionen, die du sonst verschachteln oder auf vier Wegwerfvariablen verteilen würdest.
clone with. Das ist das wichtigste Feature für Code, der der Regel aus den eigenen Code-Standards dieser Website folgt: Value Objects statt Array-Taschen. Eine readonly-Klasse ist das richtige Zuhause für einen Wert, der Schichten durchquert, aber bis 8.5 hieß jede „with“-Methode, jede Property von Hand in einen new self(...)-Aufruf zu kopieren oder mit get_object_vars() zu jonglieren. Jetzt nimmt clone die zu ersetzenden Properties entgegen:
final readonly class Color
{
public function __construct(
public int $red, public int $green, public int $blue,
public float $alpha = 1.0,
) {}
public function withAlpha(float $alpha): self
{
return clone($this, ['alpha' => $alpha]);
}
}
#[\NoDiscard]. An einer Funktion sorgt es dafür, dass PHP warnt, wenn der Rückgabewert ignoriert wird; ein (void)-Cast markiert einen Aufruf als absichtlich ungenutzt. Das passt gut zu Result-Objekten: Eine Methode, die Erfolg oder Fehlschlag zurückgibt, statt eine Exception zu werfen, ist genau die, die jemand aufruft und dann vergisst zu prüfen.
Die URI-Extension. Sie ist eingebaut, immer verfügbar und parst URLs nach RFC 3986 oder dem WHATWG-URL-Standard, auf Basis von uriparser und Lexbor. (new Uri\Rfc3986\Uri($url))->getHost() ersetzt parse_url($url)['host'], und du bekommst einen Parser, der einem benannten Standard folgt statt den eigenen Regeln von parse_url(). Überall, wo du vor einem Redirect einen Host prüfen oder eine Webhook-URL mit einer Allowlist vergleichen, lohnt sich der Wechsel.
Der Rest der Liste, persistente cURL-Share-Handles, Closures in konstanten Ausdrücken, Backtraces bei fatalen Fehlern wie einem Timeout, Attribute an Konstanten, ist nützlich, verändert aber seltener den alltäglichen Code in Controllern und Services.
Was du vor dem Upgrade prüfen solltest
Nichts davon ist ein Grund zu warten; es ist eine Suchliste. null als Array-Schlüssel oder in array_key_exists() zu verwenden, ist deprecated, und in Laravel-Code versteckt sich das oft in einem Lookup, dessen Schlüssel null sein kann. Die nicht kanonischen Casts (integer), (boolean), (double) und (binary) sind deprecated, ebenso der Backtick-Operator und ein case, das mit einem Semikolon endet. __sleep() und __wakeup() sind soft-deprecated zugunsten von __serialize() und __unserialize(): Prüfe deine eigenen Klassen, die in Queue-Payloads oder in den Cache serialisiert werden. PHP warnt jetzt, wenn ein nicht darstellbarer Float nach int gecastet wird, wenn NAN gecastet wird und wenn du etwas destrukturierst, das weder ein Array noch null ist. Auf Serverseite ist die ini-Einstellung disable_classes entfallen.
Diese Website ist ein gutes Beispiel dafür, wo die meisten Projekte stehen. Sie läuft auf Laravel 13.35.0, und ihre composer.json verlangt PHP ^8.4.1. PHP 8.5 erfüllt diese Bedingung, 8.4 aber ebenfalls, also kann keine der 8.5-Syntaxen oben in diesen Code, bevor die Bedingung angehoben wird.
Ein konkreter erster Schritt für ein Laravel-Projekt: Durchsuche die Codebasis nach __sleep, __wakeup, (integer), (boolean) und Backticks und lass dann die Tests auf 8.5 laufen. Danach ist die erste readonly-Klasse, die du anfasst, ein guter Ort, um clone with auszuprobieren. Quelle: php.net, Release-Ankündigung von PHP 8.5.
Notizen der Leser