Listopad to tradycyjnie święto dla programistów PHP. Wersja 8.5 nie jest rewolucją na miarę JIT (PHP 8.0) czy Fiberów (PHP 8.1), ale śmiało można ją nazwać „wydaniem Quality of Life”. PHP 8.5 skupia się na usunięciu najbardziej irytujących „boilerplate’ów” (nadmiarowego kodu), z którymi walczyliśmy od lat, zbliżając język do nowoczesnych standardów funkcyjnych i immutable.
Oto 4 kluczowe zmiany, które zmienią Twój kod już dzisiaj.
1. array_first() i array_last() – Koniec z akrobatyką
Przez lata pobranie pierwszego elementu tablicy wymagało użycia funkcji reset(), która modyfikowała wewnętrzny wskaźnik tablicy, lub brzydkiej konstrukcji $array[array_key_first($array)]. W PHP 8.5 funkcje helperów wchodzą do rdzenia języka.
Stary sposób (PHP < 8.5)
$users = ['admin', 'editor', 'subscriber'];
// Sposób 1: Modyfikuje wskaźnik
$first = reset($users);
// Sposób 2: Bezpieczniejszy, ale długi
$key = array_key_first($users);
$first = $users[$key] ?? null;
Nowy sposób (PHP 8.5)
Jest czysto, czytelnie i nie wymaga zewnętrznych bibliotek typu Illuminate/Support.
$users = ['admin', 'editor', 'subscriber'];
// Zwraca pierwszy element lub null, jeśli tablica pusta
$first = array_first($users);
// Możesz też podać callback filtrujący!
$firstAdmin = array_first($users, fn($u) => str_starts_with($u, 'adm'));
echo $first; // 'admin'
2. Składnia clone with – Natywne wsparcie dla Immutability
Wzorce obiektów niemutowalnych (Immutable Objects) stały się standardem (np. w architekturze DDD). Do tej pory zmiana jednej właściwości w takim obiekcie wymagała tworzenia metod typu withName(), które klonowały obiekt, zmieniały wartość i zwracały nową instancję. PHP 8.5 wprowadza operator clone with.
Stary sposób (Wither methods)
class User {
public function __construct(public string $name, public string $role) {}
public function withRole(string $newRole): self {
$clone = clone $this;
$clone->role = $newRole;
return $clone;
}
}
$user = new User('Jan', 'User');
$admin = $user->withRole('Admin');
Nowy sposób (PHP 8.5)
Teraz możemy to zrobić „w locie”, bez pisania dedykowanych metod w klasie.
class User {
public function __construct(
public readonly string $name,
public readonly string $role
) {}
}
$user = new User('Jan', 'User');
// Klonuje obiekt i nadpisuje tylko właściwość 'role'
$admin = clone $user with {
role: 'Admin'
};
// $user pozostaje bez zmian (Immutability)
echo $user->role; // 'User'
echo $admin->role; // 'Admin'
3. Operator Pipe |> – Kod czytany jak książka
To zmiana, która może podzielić programistów, ale fani programowania funkcyjnego ją pokochają. Operator Pipe pozwala przekazywać wynik jednego wyrażenia jako pierwszy argument kolejnego, eliminując „piekło zagnieżdżonych nawiasów”.
Problem (Zagnieżdżenie)
Czytamy to od środka na zewnątrz, co jest nienaturalne.
$slug = strtolower(trim(str_replace('_', '-', $input)));
Rozwiązanie (PHP 8.5 Pipe)
Czytamy od lewej do prawej, zgodnie z przepływem danych.
$input = " Hello_World_2025 ";
$slug = $input
|> str_replace('_', '-', $$) // $$ to placeholder dla wartości z rury
|> trim($$)
|> strtolower($$);
echo $slug; // "hello-world-2025"
Uwaga: W finalnej wersji PHP 8.5 składnia placeholdera to $$ lub domyślne przekazanie jako pierwszy argument (zależnie od ostatecznej implementacji RFC).
4. Atrybut #[NoDiscard]
PHP coraz mocniej stawia na bezpieczeństwo typów i logiki. Nowy atrybut mówi IDE i statycznym analizatorom (jak PHPStan): „Hej, jeśli wywołasz tę funkcję i nie przypiszesz jej wyniku do zmiennej, to robisz błąd”. Jest to kluczowe dla metod, które nie modyfikują obiektu, ale zwracają nowy.
class Wallet {
#[NoDiscard]
public function addMoney(float $amount): self {
return clone $this with { balance: $this->balance + $amount };
}
}
$myWallet = new Wallet(100);
// BŁĄD w IDE / PHPStan: Wynik operacji jest ignorowany!
// Stary portfel nadal ma 100, a nowy zniknął w nicość.
$myWallet->addMoney(50);
// POPRAWNIE:
$richerWallet = $myWallet->addMoney(50);