Nowe funkcje i ulepszenia w PHP 8.3: Kompletny przewodnik techniczny
PHP 8.3 to ważna wersja języka PHP, która wprowadza znaczące ulepszenia kompilatora JIT, systemu typów, właściwości readonly oraz podstawowych funkcji tablicowych i stringowych. Wydana 23 listopada 2023 roku, wprowadza typowane stałe klasowe, json_validate(), udoskonalenia array_is_list(), dodatki Randomizer oraz głębokie klonowanie właściwości readonly — zmiany, które bezpośrednio wpływają na wydajność aplikacji, poprawność kodu i łatwość utrzymania na serwerach produkcyjnych.
Jeśli uruchamiasz obciążenia oparte na PHP w środowisku Hostingu VPS lub na Serwerze Dedykowanym, zrozumienie każdej zmiany w PHP 8.3 nie jest opcjonalne — jest warunkiem koniecznym do podejmowania świadomych decyzji o aktualizacji, unikania cichych regresji i osiągania mierzalnych zysków wydajnościowych.
Co zmieniło się między PHP 8.2 a PHP 8.3
Zanim przejdziemy do poszczególnych funkcji, warto określić zakres tej wersji. PHP 8.3 nie jest przełomowym przepisaniem. To precyzyjna aktualizacja, która zamyka długo istniejące luki w systemie typów, wzmacnia potok JIT i dodaje funkcje narzędziowe, które wcześniej wymagały obejść w kodzie użytkownika. Poniższa tabela zestawia najważniejsze zmiany z ich odpowiednikami w PHP 8.2.
| Funkcja / Zachowanie | PHP 8.2 | PHP 8.3 |
|---|---|---|
| Typowane stałe klasowe | Nieobsługiwane | W pełni obsługiwane |
json_validate() | Niedostępne | Dostępne natywnie |
| Klonowanie właściwości readonly | Niemożliwe | Obsługiwane przez clone |
array_is_list() | Dostępne | Zachowanie niezmienione, ale szersze wzorce adopcji |
| Dynamiczne pobieranie stałych klasowych | Błąd składni | Obsługiwane przez ClassName::{$const} |
Randomizer::getBytesFromString() | Niedostępne | Dostępne |
Randomizer::getFloat() / nextFloat() | Niedostępne | Dostępne |
Atrybut #[Override] | Niedostępny | Dostępny |
Deprecacja: niejawne seedowanie mt_rand | Nie zdeprecjonowane | Zdeprecjonowane |
| Dyrektywa ini rozmiaru stosu Fiber | Niekonfigurowalne | Dodano fiber.stack_size |
| Ulepszenia śledzenia JIT | Podstawowe śledzenie | Ulepszona IR i obsługa pętli |
str_contains z tablicami | Nieobsługiwane | Nadal nieobsługiwane (błąd w artykule źródłowym — patrz poniżej) |
> Krytyczna korekta: Artykuł źródłowy błędnie stwierdza, że str_contains() przyjmuje tablicę stringów w PHP 8.3. Jest to faktycznie nieprawdziwe. str_contains() przyjmuje tylko dwa argumenty stringowe. Przekazanie tablicy wywołuje TypeError. Prawidłowym podejściem do wyszukiwania podstringu w wielu stringach jest array_filter() w połączeniu z str_contains(), lub in_array() dla dokładnych dopasowań.
Kompilacja JIT w PHP 8.3: Co faktycznie się zmieniło
Tło: Jak działa PHP JIT
Kompilator JIT PHP, wprowadzony eksperymentalnie w PHP 8.0, działa jako rozszerzenie podsystemu OPcache. Kompiluje gorące ścieżki kodu bajtowego do natywnego kodu maszynowego w czasie wykonywania, omijając interpreter Zend VM dla tych ścieżek. PHP 8.3 jest dostarczany ze znacznie zrewidowanym backendem JIT, który ulepsza reprezentację pośrednią (IR) używaną podczas kompilacji.
Nowy JIT oparty na IR w PHP 8.3 (opracowany przez Dmitry’ego Stogova) zastępuje warstwę generowania kodu starszego JIT śledzącego właściwą reprezentacją pośrednią w formie SSA. Umożliwia to lepszą alokację rejestrów, eliminację martwego kodu i wynoszenie niezmienników pętli — optymalizacje, które były strukturalnie niemożliwe w poprzedniej architekturze.
Prawidłowe włączanie JIT
Oryginalny artykuł pokazuje php -d jit=on script.php, co jest niekompletne. JIT wymaga aktywnego OPcache. Prawidłowa minimalna konfiguracja dla benchmarku CLI lub produkcyjnego php.ini to:
; php.ini
opcache.enable=1
opcache.enable_cli=1
opcache.jit_buffer_size=128M
opcache.jit=tracingW kontekście serwera WWW (FPM lub Apache mod_php), opcache.enable_cli jest nieistotne, ale opcache.jit_buffer_size musi być niezerowe, inaczej JIT cicho się wyłącza. Częstą pułapką produkcyjną jest ustawienie jit_buffer_size=0 we współdzielonym php.ini i zastanawianie się, dlaczego JIT nie ma żadnego efektu.
Kiedy JIT przynosi mierzalne zyski
JIT nie jest uniwersalnie korzystny. Jego zyski koncentrują się w obciążeniach związanych z CPU:
- Cele wysokiej wartości: Obliczenia matematyczne, przetwarzanie obrazów, wnioskowanie uczenia maszynowego, logika gier, pętle parsowania CSV/danych, operacje kryptograficzne w kodzie użytkownika.
- Cele niskiej wartości: Typowe aplikacje webowe CRUD, gdzie wąskim gardłem jest I/O (zapytania do bazy danych, system plików, sieć). W tych przypadkach narzut JIT wynikający z kompilacji może nieznacznie zwiększyć zużycie pamięci przy zaniedbywalnej poprawie przepustowości.
- Przypadki negatywne: Aplikacje z bardzo zróżnicowanymi ścieżkami kodu (duże frameworki z intensywną refleksją) mogą powodować, że JIT będzie przeciążał bufor, powodując narzut deoptymalizacji.
Praktyczna zasada: najpierw wykonaj benchmark z opcache.jit=tracing. Jeśli widzisz mniej niż 3% poprawy na swoim rzeczywistym obciążeniu, wyłącz JIT, aby odzyskać pamięć bufora dla pamięci podręcznej opcode OPcache, która przynosi korzyści wszystkim aplikacjom PHP równomiernie.
Typowane stałe klasowe
Jest to prawdopodobnie najbardziej wpływowy dodatek do systemu typów w PHP 8.3 dla dużych baz kodu.
Problem, który rozwiązuje
Przed PHP 8.3 stałe klasowe nie miały wymuszonego typu. Klasa potomna mogła przedefiniować stałą z całkowicie niekompatybilnym typem, a PHP nie zgłaszał błędu do czasu wykonania — lub wcale, w zależności od tego, jak stała była używana.
// PHP 8.2 — no type enforcement
interface StatusCode {
const SUCCESS = 200; // implicitly int
}
class BrokenStatus implements StatusCode {
const SUCCESS = "two hundred"; // silently accepted — a maintenance nightmare
}Rozwiązanie PHP 8.3
// PHP 8.3 — type is enforced at definition and inheritance
interface StatusCode {
const int SUCCESS = 200;
}
class BrokenStatus implements StatusCode {
const int SUCCESS = "two hundred";
// Fatal error: Cannot use string as value for typed class constant
// BrokenStatus::SUCCESS of type int
}Wszystkie typy skalarne (int, float, string, bool), array, null, typy unii i typy przecięcia są prawidłowe dla deklaracji typów stałych. Typy never i void nie są dozwolone. Ta funkcja integruje się płynnie z narzędziami do analizy statycznej, takimi jak PHPStan i Psalm, umożliwiając ściślejsze kontrakty interfejsów bez narzutu w czasie wykonywania.
Dynamiczne pobieranie stałych klasowych i członków Enum
PHP 8.3 umożliwia pobieranie stałych klasowych i członków enum przy użyciu wyrażenia czasu wykonywania w składni ::{}.
class Direction {
const string NORTH = 'north';
const string SOUTH = 'south';
}
$direction = 'NORTH';
echo Direction::{$direction}; // outputs: northWcześniej wymagało to constant() lub wyrażenia match, które są zarówno rozwlekłe, jak i podatne na błędy. Nowa składnia działa również z enumami:
enum Color {
case Red;
case Blue;
}
$name = 'Red';
$color = Color::{$name}; // Color::RedPrzypadek brzegowy do obserwowania: Jeśli zmienna zawiera nazwę, która nie odpowiada zdefiniowanej stałej lub przypadkowi enum, PHP rzuca wyjątek Error — nie ostrzeżenie. Opakuj dynamiczne pobieranie w blok try/catch lub zweryfikuj za pomocą defined() / enum_exists() przed użyciem.
Funkcja json_validate()
Dlaczego ma to znaczenie w produkcji
Przed PHP 8.3 idiomatycznym sposobem walidacji stringa JSON bez jego dekodowania było:
json_decode($input);
$isValid = json_last_error() === JSON_ERROR_NONE;To podejście w pełni dekoduje JSON do struktury PHP, alokując pamięć proporcjonalną do rozmiaru ładunku. Dla potoków tylko do walidacji — bram API, konsumentów kolejek wiadomości, odbiorników webhooków — jest to marnotrawstwo.
Natywna walidacja PHP 8.3
$payload = '{"user": "alex", "role": "admin"}';
if (json_validate($payload)) {
// safe to decode
$data = json_decode($payload, true);
}
// Invalid JSON
var_dump(json_validate('{invalid}')); // bool(false)json_validate() parsuje strukturę JSON bez konstruowania drzewa wartości PHP. Zużycie pamięci wynosi O(głębokość) zamiast O(rozmiar), co czyni go znacznie bardziej wydajnym dla dużych ładunków. Przyjmuje również parametry $depth i $flags zgodne z json_decode().
Przypadek użycia w rzeczywistości: Odbiornik webhooków przetwarzający 50 000 żądań na minutę może używać json_validate() do odrzucania zniekształconych ładunków na brzegu przed jakąkolwiek deserializacją, znacznie zmniejszając obciążenie CPU i pamięci.
Właściwości Readonly: Obsługa głębokiego klonowania
PHP 8.2 wprowadził właściwości readonly, ale uniemożliwił ich modyfikację nawet podczas klonowania obiektów. Zmuszało to programistów do niezręcznych obejść — metod fabrycznych, hacków serializacji lub całkowitego porzucenia readonly dla obiektów wartości.
PHP 8.3 rozwiązuje to, zezwalając magicznej metodzie __clone() na ponowne przypisywanie właściwości readonly w kontekście klonowania.
class ImmutablePoint {
public function __construct(
public readonly float $x,
public readonly float $y,
) {}
public function withX(float $x): static {
$clone = clone $this;
$clone->x = $x; // Legal in PHP 8.3 within __clone context
return $clone;
}
}
$point = new ImmutablePoint(1.0, 2.0);
$moved = $point->withX(5.0);
echo $moved->x; // 5.0
echo $point->x; // 1.0 — original unchangedTen wzorzec jest fundamentalny dla niezmiennych obiektów wartości w Domain-Driven Design. Bez niego readonly było w dużej mierze dekoracyjne dla złożonych modeli domenowych.
Atrybut #[Override]
Atrybut #[Override] sygnalizuje PHP (i narzędziom do analizy statycznej), że metoda jest przeznaczona do nadpisania metody klasy nadrzędnej lub interfejsu. Jeśli metoda nadrzędna nie istnieje, PHP rzuca błąd czasu kompilacji.
class Base {
public function process(): void {}
}
class Child extends Base {
#[Override]
public function process(): void {
// If Base::process() is renamed or removed, this becomes a fatal error
}
}Jest to szczególnie cenne w dużych zespołach, gdzie refaktoryzacja klasy bazowej może po cichu zepsuć nadpisania klas potomnych. Atrybut działa jako kontrakt czasu kompilacji, wychwytując kategorię błędów, która wcześniej ujawniała się tylko w czasie wykonywania lub przez analizę statyczną.
array_is_list() i prawidłowa klasyfikacja tablic
array_is_list() został wprowadzony w PHP 8.1, nie 8.3. Jednak jego prawidłowe wzorce użycia zasługują na precyzyjną dokumentację, ponieważ funkcja jest często źle rozumiana.
Tablica PHP jest listą wtedy i tylko wtedy, gdy:
- Jest pusta, lub
- Jej klucze są kolejnymi liczbami całkowitymi zaczynającymi się od
0bez przerw.
var_dump(array_is_list([])); // bool(true)
var_dump(array_is_list([0 => 'a', 1 => 'b'])); // bool(true)
var_dump(array_is_list(['a', 'b', 'c'])); // bool(true)
var_dump(array_is_list([1 => 'a', 0 => 'b'])); // bool(false) — wrong order
var_dump(array_is_list([0 => 'a', 2 => 'b'])); // bool(false) — gap at index 1
var_dump(array_is_list(['key' => 'value'])); // bool(false) — string keyPraktyczne zastosowanie: Podczas serializacji danych do JSON, array_is_list() określa, czy wynik powinien być tablicą JSON ([]) czy obiektem JSON ({}). Użycie go przed json_encode() zapobiega przypadkowej serializacji obiektów numerycznie indeksowanych tablic, z których usunięto elementy.
Nowe metody Randomizer w PHP 8.3
Klasa RandomRandomizer wprowadzona w PHP 8.2 otrzymuje trzy ważne dodatki:
getBytesFromString()
$randomizer = new RandomRandomizer();
$token = $randomizer->getBytesFromString('abcdefghijklmnopqrstuvwxyz0123456789', 16);
echo $token; // e.g., "k3mz9xqp1wvn7yt2"Generuje to kryptograficznie bezpieczny losowy string pobrany z określonego alfabetu — wzorzec wymagany do generowania tokenów, kodów OTP i tworzenia slugów. Wcześniej wymagało to ręcznej pętli z random_int().
getFloat() i nextFloat()
$randomizer = new RandomRandomizer();
// Returns a float in [0.0, 1.0)
$value = $randomizer->nextFloat();
// Returns a float in a specified closed or half-open interval
$scaled = $randomizer->getFloat(1.5, 9.5, RandomIntervalBoundary::ClosedOpen);getFloat() używa algorytmu γ-section do generowania równomiernie rozłożonych liczb zmiennoprzecinkowych bez odchylenia modulo, które wpływa na naiwne implementacje. Jest to krytyczne dla symulacji, algorytmów probabilistycznych i frameworków testów A/B, gdzie ważna jest jednolitość rozkładu.
Deprecacje i usunięcia w PHP 8.3
Zrozumienie tego, co jest wycofywane, jest równie ważne jak wiedza o tym, co jest dodawane. Ignorowanie deprecacji teraz oznacza fatalne błędy w PHP 9.0.
| Zdeprecjonowana funkcja | Powód | Ścieżka migracji |
|---|---|---|
Wywoływanie mt_rand() bez jawnego seeda w niektórych kontekstach | Niespójność zachowania niejawnego seedowania | Użyj RandomRandomizer |
ReflectionProperty::setValue() bez obiektu na niestatycznym | Niejednoznaczne zachowanie | Przekaż docelowy obiekt jawnie |
Przekazywanie ujemnego $widths do mb_strimwidth() | Niezdefiniowane zachowanie | Zweryfikuj dane wejściowe przed wywołaniem |
ldap_connect() z oddzielnymi argumentami host/port | Zdeprecjonowane na rzecz formy URI | Użyj stringa URI ldap://host:port |
range() z nieintegerowym krokiem generującym liczby zmiennoprzecinkowe | Zaskakująca niejawna koercja typów | Jawnie rzutuj krok na float |
Benchmarki wydajności: PHP 8.3 vs. poprzednie wersje
Na podstawie opublikowanych benchmarków od Kinsta, Phoronix i zespołu wewnętrznego PHP przy użyciu Symfony Demo, WordPress i surowych obciążeń Fibonacci/sortowanie:
| Benchmark | PHP 8.1 | PHP 8.2 | PHP 8.3 |
|---|---|---|---|
| Symfony Demo (żądania/sek) | ~1 450 | ~1 520 | ~1 610 |
| WordPress (żądania/sek) | ~1 180 | ~1 240 | ~1 290 |
| Fibonacci (JIT, ms) | ~48 | ~44 | ~38 |
| Mandelbrot (JIT, ms) | ~210 | ~195 | ~170 |
| Tylko OPcache (bez JIT) | Punkt odniesienia | +5% | +8% |
Zyski są spójne, ale nie dramatyczne dla aplikacji związanych z I/O. Ulepszenia JIT w PHP 8.3 pokazują najbardziej znaczącą różnicę w obciążeniach czystych obliczeń — do 18% szybciej niż PHP 8.2 na benchmarku Mandelbrot.
Aktualizacja do PHP 8.3: Praktyczna lista kontrolna po stronie serwera
Jeśli zarządzasz własną infrastrukturą serwerową — czy to na VPS z cPanel czy na bare-metal Serwerze Dedykowanym — postępuj zgodnie z tą sekwencją przed aktualizacją środowisk produkcyjnych.
Kroki przed aktualizacją
- Uruchom
composer outdatedi zaktualizuj wszystkie zależności do wersji z zadeklarowaną kompatybilnością z PHP 8.3. - Wykonaj
php -d error_reporting=E_ALL your_app_entrypoint.phppod PHP 8.3 CLI, aby wykryć powiadomienia o deprecacji zanim staną się fatalnymi błędami. - Sprawdź każdy kod, który wywołuje
str_contains(),str_starts_with()lubstr_ends_with()z argumentami niebędącymi stringami — teraz rzucająTypeErrorw ścisłych kontekstach. - Przejrzyj wszystkie stałe klasowe, które klasy potomne nadpisują — typowane stałe spowodują fatalne błędy, jeśli typy są niekompatybilne.
- Sprawdź wynik
phpinfo()po aktualizacji, aby potwierdzić, że OPcache i JIT są aktywne z oczekiwaną konfiguracją.
Dostrajanie php.ini dla PHP 8.3 w produkcji
; Recommended production baseline for PHP 8.3
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.jit=tracing
opcache.jit_buffer_size=64MUstawienie validate_timestamps=0 wyłącza sprawdzanie modyfikacji plików przy każdym żądaniu — niezbędne dla wdrożeń o dużym ruchu, gdzie narzut unieważniania OPcache jest mierzalny. Zamiast tego używaj hooków wdrożeniowych do wywoływania opcache_reset() po wdrożeniu kodu.
Walidacja po aktualizacji
# Verify active PHP version
php -v
# Confirm JIT is compiled in and active
php -r "var_dump(opcache_get_status()['jit']);"
# Check for deprecation notices in error log
tail -f /var/log/php-fpm/error.log | grep -i deprecatPHP 8.3 i kwestie bezpieczeństwa aplikacji webowych
PHP 8.3 nie wprowadza nowych prymitywów bezpieczeństwa, ale kilka zmian ma pośrednie implikacje dla bezpieczeństwa:
json_validate()zmniejsza powierzchnię ataku w potokach walidacji danych wejściowych, zapobiegając dotarciu zniekształconego JSON do logiki deserializacji.- Typowane stałe klasowe zapobiegają atakom na konfuzję typów, gdzie podklasa podstawia nieoczekiwany typ dla stałej istotnej dla bezpieczeństwa (np. poziom uprawnień lub wartość limitu czasu).
- Atrybut
#[Override]zapobiega cichemu cieniowaniu metod w klasach bazowych krytycznych dla bezpieczeństwa, co jest wektorem subtelnych błędów eskalacji uprawnień w architekturach wtyczek. - Dodatki
RandomRandomizerzastępują niebezpieczne wzorce, takie jaksubstr(str_shuffle(implode(range('a','z'))), 0, 16)do generowania tokenów.
Dla aplikacji obsługujących wrażliwe dane, połączenie PHP 8.3 z prawidłowo skonfigurowanym stosem TLS jest niezbędne. Jeśli Twoje środowisko hostingowe nie ma jeszcze wdrożonych aktualnych Certyfikatów SSL, zajmij się tym przed jakąkolwiek aktualizacją PHP.
Wybór odpowiedniego środowiska hostingowego dla PHP 8.3
Kompilator JIT PHP 8.3 i zwiększone wymagania pamięciowe dla rozwiązywania typowanych stałych oznaczają, że środowiska z ograniczonymi zasobami mogą nie w pełni skorzystać z aktualizacji.
- Hosting współdzielony: Dostępność wersji PHP zależy całkowicie od dostawcy. Jeśli potrzebujesz PHP 8.3 natychmiast, plany Współdzielonego Hostingu WWW z przełączaniem wersji PHP dają Ci elastyczność bez narzutu zarządzania serwerem.
- VPS: Pełna kontrola nad
php.ini, konfiguracją puli PHP-FPM, dostrajaniem OPcache i rozmiarem bufora JIT. To minimalne zalecane środowisko dla produkcyjnych wdrożeń PHP 8.3 z włączonym JIT. - Serwery dedykowane: Wymagane dla aplikacji o dużym ruchu, gdzie rywalizacja o bufor JIT między wieloma workerami PHP-FPM staje się wąskim gardłem. Dedykowane środowisko umożliwia również alokację pamięci z uwzględnieniem NUMA dla OPcache.
- Hosting GPU: Istotne, jeśli Twoja aplikacja PHP orkiestruje obciążenia akcelerowane przez GPU (np. wywoływanie usług wnioskowania ML w Pythonie). Środowiska Hostingu GPU korzystają z ulepszonego FFI i zarządzania procesami PHP 8.3.
Kluczowe wnioski techniczne i macierz decyzyjna
Używaj typowanych stałych klasowych natychmiast, jeśli:
- Twoja baza kodu używa interfejsów lub klas abstrakcyjnych ze stałymi, które klasy potomne nadpisują.
- Używasz PHPStan poziom 8 lub Psalm — typowane stałe odblokowują ściślejszą analizę.
Włącz JIT, jeśli:
- Twój profiler pokazuje, że czas CPU przekracza 40% czasu żądania.
- Uruchamiasz przetwarzanie wsadowe, transformację danych lub obciążenia matematyczne w PHP.
- Masz co najmniej 64 MB dedykowanego bufora JIT OPcache dostępnego na pulę PHP-FPM.
Nie włączaj JIT, jeśli:
- Twoja aplikacja jest związana z I/O (baza danych, cache, system plików, wywołania API dominują w opóźnieniach).
- Jesteś na hostingu współdzielonym z ograniczoną pamięcią OPcache.
- Nie wykonałeś benchmarku swojego konkretnego obciążenia — nie zakładaj niczego.
Przyjmij json_validate(), jeśli:
- Walidуjesz JSON przed dekodowaniem w dowolnym miejscu swojej bazy kodu.
- Przetwarzasz ładunki webhooków lub kolejek wiadomości o dużym wolumenie.
Dodaj #[Override] do:
- Każdej metody w klasie potomnej, która celowo nadpisuje metodę nadrzędną.
- Nadpisań metod krytycznych dla bezpieczeństwa w architekturach wtyczek lub rozszerzeń.
Przejdź na RandomRandomizer, jeśli:
- Jakakolwiek część Twojego kodu używa
rand(),mt_rand(),array_rand()lubstr_shuffle()do generowania tokenów lub kluczy wrażliwych na bezpieczeństwo.
FAQ
Czy PHP 8.3 narusza kompatybilność wsteczną z kodem PHP 8.2?
W większości przypadków nie. PHP 8.3 to wersja minor bez usuniętych funkcji ze standardowej biblioteki, które nie były już zdeprecjonowane w 8.2. Jednak nowo zdeprecjonowane zachowania będą emitować powiadomienia E_DEPRECATED, a każdy kod polegający na niejawnej koercji typów w stałych lub range() z krokami zmiennoprzecinkowymi może zachowywać się inaczej. Zawsze uruchamiaj swój zestaw testów pod PHP 8.3 przed wdrożeniem.
Czy JIT jest domyślnie włączony w PHP 8.3?
Nie. JIT wymaga włączonego OPcache i ustawienia opcache.jit_buffer_size na wartość niezerową. Domyślny php.ini dostarczany z większością dystrybucji ustawia opcache.jit_buffer_size=0, co skutecznie wyłącza JIT. Musisz go jawnie skonfigurować.
Czy mogę używać typowanych stałych klasowych z typami unii w PHP 8.3?
Tak. const int|string VERSION = 8; jest prawidłowe. Typy przecięcia są również dozwolone dla stałych typu obiektowego. Jedyne zabronione typy to void i never.
Jaka jest różnica między json_validate() a json_decode() do celów walidacji?
json_validate() parsuje strukturę JSON bez konstruowania wartości PHP w pamięci. Jest znacznie bardziej wydajny pamięciowo dla dużych ładunków i szybszy, gdy potrzebujesz tylko potwierdzić ważność strukturalną. json_decode() musi być używany, gdy faktycznie potrzebujesz zdekodowanych danych — nie wywołuj obu w sekwencji; wywołuj json_validate() tylko wtedy, gdy zamierzasz odrzucić wynik.
Czy PHP 8.3 obsługuje klasy readonly wprowadzone w PHP 8.2?
Tak, i rozszerza je. PHP 8.3 pozwala na ponowne przypisywanie właściwości readonly w __clone(), co było głównym ograniczeniem klas readonly w PHP 8.2. Dzięki temu wzorce niezmiennych obiektów wartości są w pełni wykonalne bez obejść.
