PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

PHP 8.3 preview – typed constants, json_validate(), array_find(), clone with

by Henryk Tews / wtorek, 09 maja 2023 / Opublikowano w PHP

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.

About Henryk Tews

Co możesz przeczytać następne

Trendy 2026 – PHP 8.5 w Magento, PHP 9.0 sygnały, FrankenPHP, AI tooling Q1
AI-assisted optymalizacja SQL – LLM + EXPLAIN + Blackfire, 5x przyspieszenie
PHP 7.3 – JSON_THROW_ON_ERROR, elastyczny Heredoc, array_key_first/last
  • Publikacje
  • O autorze
  • Kontakt

© 2026 Created by

GÓRA
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Zawsze aktywne
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Zarządzaj opcjami
  • Zarządzaj serwisami
  • Zarządzaj {vendor_count} dostawcami
  • Przeczytaj więcej o tych celach
Zobacz preferencje
  • {title}
  • {title}
  • {title}