PHP / Magento Dev Blog

  • Home

PHP 8.3 – readonly classes, json_validate(), typed constants, #[Override]

by Henryk Tews / Wednesday, 05 August 2026 / Published in PHP

PHP 8.3 doprecyzował i rozszerzył kilka funkcji które były już w 8.1 i 8.2. Readonly properties dostały nowe możliwości, json_validate() zastąpiło powszechny hack z json_decode + sprawdzeniem błędu, a nowe funkcje tablic i typowanie klas wbudowanych zamknęły kilka długo otwartych luk. Pokażę zmiany które mają realny wpływ na codzienne pisanie kodu w Magento 2.

Readonly classes – uproszczona składnia

<?php

declare(strict_types=1);

// PHP 8.2: readonly class - wszystkie właściwości są automatycznie readonly
readonly class Money
{
    public function __construct(
        public float $amount,
        public string $currency,
    ) {}

    // Readonly class może mieć metody
    public function add(Money $other): static
    {
        if ($this->currency !== $other->currency) {
            throw new \InvalidArgumentException('Currency mismatch');
        }
        // Musi zwrócić nową instancję - nie może mutować
        return new static($this->amount + $other->amount, $this->currency);
    }

    public function format(): string
    {
        return number_format($this->amount, 2) . ' ' . $this->currency;
    }
}

// PHP 8.3: readonly property można reinicjalizować w clone
readonly class ProductPrice
{
    public function __construct(
        public float $net,
        public float $tax,
        public string $currency = 'PLN',
    ) {}

    // PHP 8.3: clone with - zmień wybrane pola w kopii
    public function withTax(float $taxRate): static
    {
        return new static(
            net: $this->net,
            tax: round($this->net * $taxRate, 2),
            currency: $this->currency,
        );
    }
}

$price = new ProductPrice(net: 100.0, tax: 23.0);
$reduced = new ProductPrice(net: 80.0, tax: 18.40);
echo $price->net; // 100.0 - nie można zmienić

json_validate() – wreszcie natywna walidacja

<?php

// Przed PHP 8.3 - powszechny hack:
function isValidJson(string $json): bool
{
    json_decode($json);
    return json_last_error() === JSON_ERROR_NONE;
}
// Problem: parsuje cały JSON do pamięci tylko po to żeby sprawdzić poprawność

// PHP 8.3: json_validate() - waliduje bez parsowania
$payload = '{"order_id": 123, "status": "complete"}';

if (json_validate($payload)) {
    $data = json_decode($payload, true);
    // bezpieczne - wiemy że JSON jest poprawny
}

// Użycie w Magento 2 - walidacja webhooków
class WebhookValidator
{
    public function validatePayload(string $rawBody): array
    {
        if (!json_validate($rawBody)) {
            throw new \Magento\Framework\Exception\LocalizedException(
                __('Invalid JSON payload received from webhook')
            );
        }

        return json_decode($rawBody, true);
    }
}

// json_validate() jest ~2x szybsze niż json_decode() dla dużych payloadów
// bo nie tworzy struktury PHP - tylko weryfikuje składnię

Typed class constants

<?php

// PHP 8.3: stałe klasy mogą mieć typ
class OrderStatus
{
    const string PENDING    = 'pending';
    const string PROCESSING = 'processing';
    const string COMPLETE   = 'complete';
    const string CANCELLED  = 'cancelled';

    const int    MAX_ITEMS  = 100;
    const float  MIN_AMOUNT = 0.01;

    // PHP sprawdzi typ przy definicji - błąd kompilacji przy niezgodności
    // const int WRONG = 'not_an_int'; // Fatal error
}

// W interfejsach też działa:
interface HasStatusInterface
{
    const string DEFAULT_STATUS = 'pending';
}

// Praktyczne zastosowanie - zamiast stałych bez typów które mogą być czymkolwiek:
class ShippingMethod
{
    const string FLAT_RATE  = 'flatrate_flatrate';
    const string FREE       = 'freeshipping_freeshipping';
    const string TABLE_RATE = 'tablerate_bestway';

    // PHP 8.3 gwarantuje że stałe są string - IDE i static analysis mogą na tym polegać
}

Nowe funkcje tablic

<?php

// array_find() - znajdź pierwszy element spełniający warunek (PHP 8.4 preview w 8.3)
// Uwaga: array_find() jest w PHP 8.4, nie 8.3 - tu pokazuję polyfill

// PHP 8.3: str_contains(), str_starts_with(), str_ends_with() - już od 8.0
// Ale PHP 8.3 dodał: mb_str_split() jako wsparcie dla multibyte

// Praktyczna zmiana: #[\Override] atrybut
class BaseProduct
{
    public function getPrice(): float
    {
        return 0.0;
    }
}

class ConfigurableProduct extends BaseProduct
{
    // PHP 8.3: #[\Override] - kompilator sprawdzi czy metoda istnieje w rodzicu
    // Jeśli nie - błąd. Chroni przed literówkami i refaktoringiem.
    #[\Override]
    public function getPrice(): float
    {
        return $this->calculateConfigurablePrice();
    }

    private function calculateConfigurablePrice(): float
    {
        return 99.99;
    }
}

// Bez #[\Override] - jeśli zmienisz nazwę getPrice() w rodzicu,
// metoda dziecka stanie się nową metodą bez ostrzeżenia.
// Z #[\Override] - PHP rzuci błąd przy kompilacji.

Magento 2 i PHP 8.3 – kompatybilność

# Magento 2.4.7+ wspiera PHP 8.3
# Sprawdź wymagania swojej wersji:
composer show magento/product-community-edition | grep php

# Migracja kodu na PHP 8.3:
# 1. Uruchom PHPStan z php8.3 baseline
vendor/bin/phpstan analyse \
    --level=8 \
    --configuration=phpstan.neon \
    app/code/

# 2. Szukaj deprecated: dynamic properties (usunięte w 8.2+)
# Klasy bez declare(strict_types) z dynamicznymi właściwościami rzucają deprecation

# 3. Sprawdź użycie stałych bez typów - rozważ dodanie typów
grep -r "const [A-Z]" app/code/ | grep -v "const string\|const int\|const float\|const array\|const bool"

Podsumowanie

PHP 8.3 to ewolucja, nie rewolucja. Typed constants eliminują klasę bugów gdzie stała miała nieoczekiwany typ. json_validate() zastępuje wieloletni hack z json_decode. Readonly classes upraszczają Value Objects – w Magento 2 idealnie nadają się do Data Transfer Objects, konfiguracji i Money types. Atrybut #[\Override] to siatka bezpieczeństwa przy refactoringu hierarchii klas.

About Henryk Tews

What you can read next

PHP 7.2 – object type hint, sodium instead of mcrypt, deprecations

© 2026 Created by

TOP
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 Always active
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.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Zobacz preferencje
  • {title}
  • {title}
  • {title}