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.
