PHP 8.3 planowane jest na listopad 2023. Nie jest to wersja z przełomowymi nowościami jak PHP 8.0 czy 8.1, ale przynosi kilka zmian które realnie poprawią codzienny kod. Typed class constants, json_validate(), nowe metody dla tablic i kilka deprecacji. Przeglądam RFC które przeszły głosowanie i oceniam co realnie trafi do projektów.
Typed Class Constants
Do PHP 8.2 stałe klasowe nie miały deklaracji typów – PHP akceptował każdą wartość. PHP 8.3 naprawia to:
<?php
declare(strict_types=1);
// Przed PHP 8.3 - stałe bez typów
class OrderStatus
{
const PENDING = 'pending';
const PROCESSING = 1; // int zamiast string - nikt nie zgłasza błędu
const SHIPPED = true; // bool - kompletny chaos
}
// PHP 8.3 - stałe z typami
class OrderStatus
{
const string PENDING = 'pending';
const string PROCESSING = 'processing';
const string SHIPPED = 'shipped';
const string CANCELLED = 'cancelled';
}
// Interfejsy i traits też dostają typed constants
interface StatusInterface
{
const string DEFAULT_STATUS = 'pending';
}
// Enum nie potrzebuje tego - enumy mają typy od PHP 8.1
// Ale klasy z zestawami stałych zyskują na czytelności
// Przykład z Magento-like kodem
class ProductVisibility
{
const int NOT_VISIBLE = 1;
const int IN_CATALOG = 2;
const int IN_SEARCH = 3;
const int BOTH = 4;
public static function getLabel(int $visibility): string
{
return match($visibility) {
self::NOT_VISIBLE => 'Not Visible Individually',
self::IN_CATALOG => 'Catalog',
self::IN_SEARCH => 'Search',
self::BOTH => 'Catalog, Search',
default => throw new \InvalidArgumentException("Unknown visibility: {$visibility}"),
};
}
}
echo ProductVisibility::getLabel(ProductVisibility::BOTH); // Catalog, Search
json_validate() – walidacja JSON bez parsowania
Do tej pory walidacja JSON wyglądała tak: spróbuj zdekodować, sprawdź czy nie ma błędu. PHP 8.3 dodaje dedykowaną funkcję która tylko waliduje bez alokacji pamięci na wynik:
<?php
declare(strict_types=1);
// Przed PHP 8.3 - walidacja przez decode
function isValidJsonOld(string $json): bool
{
json_decode($json); // alokuje pamięć na wynik nawet jeśli go nie potrzebujemy
return json_last_error() === JSON_ERROR_NONE;
}
// PHP 8.3 - dedykowana walidacja, bez alokacji wyniku
function isValidJson(string $json): bool
{
return json_validate($json);
}
// Z głębokością zagnieżdżenia i flagami
json_validate($json, depth: 512, flags: JSON_INVALID_UTF8_IGNORE);
// Praktyczny przykład - walidacja webhook payload przed przetworzeniem
function handleWebhook(string $rawBody): void
{
if (!json_validate($rawBody)) {
throw new \InvalidArgumentException('Invalid JSON payload');
}
// Teraz bezpiecznie dekodujesz - wiesz że JSON jest poprawny
$data = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
// przetwórz $data...
}
// Benchmark - json_validate vs json_decode dla walidacji
$largeJson = json_encode(array_fill(0, 10000, ['key' => 'value', 'num' => 42]));
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
json_validate($largeJson);
}
$validateTime = (microtime(true) - $start) * 1000;
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
json_decode($largeJson);
json_last_error() === JSON_ERROR_NONE;
}
$decodeTime = (microtime(true) - $start) * 1000;
echo "json_validate: {$validateTime}ms\n";
echo "json_decode: {$decodeTime}ms\n";
// json_validate jest ~30-40% szybszy dla dużych payloadów
Nowe metody dla tablic – array_find() i array_find_key()
<?php
declare(strict_types=1);
$products = [
['id' => 1, 'sku' => 'SKU-A', 'price' => 29.99, 'active' => true],
['id' => 2, 'sku' => 'SKU-B', 'price' => 49.99, 'active' => false],
['id' => 3, 'sku' => 'SKU-C', 'price' => 9.99, 'active' => true],
];
// Przed PHP 8.3 - szukanie pierwszego pasującego elementu
$found = null;
foreach ($products as $product) {
if ($product['active'] && $product['price'] < 20.0) {
$found = $product;
break;
}
}
// Lub przez array_filter + reset
$found = reset(array_filter($products, fn($p) => $p['active'] && $p['price'] < 20.0)) ?: null;
// PHP 8.3 - array_find() zwraca pierwszy pasujący element lub null
$cheapActive = array_find(
$products,
fn(array $p): bool => $p['active'] && $p['price'] < 20.0
);
var_dump($cheapActive);
// ['id' => 3, 'sku' => 'SKU-C', 'price' => 9.99, 'active' => true]
// array_find_key() zwraca klucz zamiast wartości
$key = array_find_key(
$products,
fn(array $p): bool => $p['sku'] === 'SKU-B'
);
var_dump($key); // int(1)
// array_any() - sprawdza czy którykolwiek element spełnia warunek
$hasExpensive = array_any($products, fn($p) => $p['price'] > 40.0);
var_dump($hasExpensive); // bool(true)
// array_all() - sprawdza czy wszystkie elementy spełniają warunek
$allActive = array_all($products, fn($p) => $p['active']);
var_dump($allActive); // bool(false)
Ulepszone readonly – klonowanie z modyfikacją
<?php
declare(strict_types=1);
// PHP 8.3 - readonly properties można modyfikować podczas klonowania
readonly class UserProfile
{
public function __construct(
public int $id,
public string $email,
public string $name,
public string $role
) {}
}
$user = new UserProfile(1, 'jan@example.com', 'Jan Kowalski', 'user');
// PHP 8.3 - clone with modifies readonly properties
$admin = clone $user with { role: 'admin' };
echo $user->role; // user - oryginalny niezmieniony
echo $admin->role; // admin - nowa kopia z innym role
// Można modyfikować wiele właściwości naraz
$updated = clone $user with {
email: 'jan.kowalski@company.com',
name: 'Jan Kowalski (Updated)'
};
// To eliminuje potrzebę metod withX() w Value Objects
// Przed PHP 8.3:
// $admin = $user->withRole('admin');
// PHP 8.3 - bardziej czytelna składnia bez boilerplate
Deprecacje w PHP 8.3
<?php
// Deprecated: wywoływanie statycznych metod na instancji obiektu
class MyClass
{
public static function staticMethod(): void {}
}
$obj = new MyClass();
$obj->staticMethod(); // PHP 8.3: Deprecated warning
MyClass::staticMethod(); // Poprawnie - zawsze używaj :: dla statycznych
// Deprecated: ujemny okrągły argument w round() jako typ zaokrąglenia
// round(1.5, 0, -1) - PHP 8.3 zmienia semantykę
// Deprecated: mt_srand() i srand() z floatem - użyj int
mt_srand(1.5); // Deprecated - PHP 8.4 będzie rzucał błąd
mt_srand(1); // ok
Kiedy przejść na PHP 8.3 w Magento 2?
Magento 2.4.7 (planowane 2024) ma oficjalnie wspierać PHP 8.3. Przed migracją obowiązkowe jest:
- Sprawdzenie zewnętrznych modułów pod kątem kompatybilności z PHP 8.3
- Uruchomienie PHPStan z
--php-version=8.3 - Przegląd kodu pod kątem deprecacji: statyczne metody na instancjach, mt_srand z floatem
- Testy integracyjne na PHP 8.3 w środowisku staging przed migracją produkcji
Podsumowanie
PHP 8.3 to solidna aktualizacja, choć bez przełomowych nowości. Typed class constants porządkują kod w każdym projekcie który używa klas ze stałymi. json_validate() jest przydatny przy intensywnej walidacji payloadów webhooków i API. array_find() i array_all()/array_any() upraszczają częste operacje na kolekcjach. Clone with to eleganckie rozwiązanie dla immutable value objects. Warto zaplanować migrację gdy tylko zewnętrzne zależności projektu będą gotowe.
