PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

PHP 8.2 preview – readonly classes, DNF types, deprecacja dynamicznych właściwości

by Henryk Tews / wtorek, 12 lipca 2022 / Opublikowano w PHP

PHP 8.2 planowane jest na grudzień 2022. Najważniejsza nowość to readonly classes – możliwość oznaczenia całej klasy jako readonly bez dekorowania każdej właściwości z osobna. Do tego Disjunctive Normal Form types które rozszerzają system typów o bardziej złożone kombinacje. Przeglądam co wchodzi, co wypada i co to oznacza dla projektów Magento.

Readonly Classes

PHP 8.1 dało nam readonly properties – możliwość oznaczenia pojedynczej właściwości jako niezmienialnej po inicjalizacji. PHP 8.2 idzie krok dalej i pozwala oznaczyć całą klasę jako readonly. Wszystkie właściwości automatycznie stają się readonly bez konieczności dekorowania każdej z osobna:

<?php

declare(strict_types=1);

// PHP 8.1 - readonly per właściwość
class MoneyPhp81
{
    public function __construct(
        public readonly int $amount,
        public readonly string $currency,
        public readonly \DateTimeImmutable $createdAt
    ) {}
}

// PHP 8.2 - readonly class - wszystkie właściwości automatycznie readonly
readonly class Money
{
    public function __construct(
        public int $amount,
        public string $currency,
        public \DateTimeImmutable $createdAt
    ) {}

    // Metody działają normalnie - readonly dotyczy tylko właściwości
    public function add(self $other): self
    {
        if ($this->currency !== $other->currency) {
            throw new \InvalidArgumentException('Currency mismatch');
        }

        return new self(
            $this->amount + $other->amount,
            $this->currency,
            new \DateTimeImmutable()
        );
    }

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

$price = new Money(2999, 'PLN', new \DateTimeImmutable());
$tax   = new Money(690, 'PLN', new \DateTimeImmutable());
$total = $price->add($tax);

echo $total->format(); // 36,89 PLN

$price->amount = 100; // Fatal error: Cannot modify readonly property

Readonly class ma kilka ograniczeń: właściwości nie mogą mieć wartości domyślnych innych niż przez konstruktor, klasa nie może rozszerzać niereadonly klas (i odwrotnie – niereadonly nie może rozszerzać readonly), oraz właściwości statyczne nie są dozwolone w readonly class.

<?php

declare(strict_types=1);

// Readonly class świetna dla DTO i Value Objects
readonly class ProductDto
{
    public function __construct(
        public int $id,
        public string $sku,
        public string $name,
        public float $price,
        public bool $isActive,
        public array $categories
    ) {}

    public static function fromArray(array $data): self
    {
        return new self(
            id:         (int) $data['entity_id'],
            sku:        (string) $data['sku'],
            name:       (string) $data['name'],
            price:      (float) $data['price'],
            isActive:   (bool) $data['status'],
            categories: $data['categories'] ?? []
        );
    }
}

readonly class OrderItemDto
{
    public function __construct(
        public string $sku,
        public string $name,
        public int $quantity,
        public float $price,
        public float $rowTotal
    ) {}
}

// Klonowanie z modyfikacją - zamiast setterów
readonly class UserDto
{
    public function __construct(
        public int $id,
        public string $email,
        public string $name,
        public string $role
    ) {}

    public function withRole(string $role): self
    {
        return new self($this->id, $this->email, $this->name, $role);
    }

    public function withEmail(string $email): self
    {
        return new self($this->id, $email, $this->name, $this->role);
    }
}

$user    = new UserDto(1, 'jan@example.com', 'Jan Kowalski', 'user');
$admin   = $user->withRole('admin');
echo $admin->role;  // admin
echo $user->role;   // user - oryginalny niezmieniony

DNF Types – Disjunctive Normal Form

PHP 8.0 dał union types (A|B). PHP 8.1 dał intersection types (A&B). PHP 8.2 łączy oba w DNF (Disjunctive Normal Form) – możesz pisać (A&B)|C:

<?php

declare(strict_types=1);

interface Stringable
{
    public function __toString(): string;
}

interface Countable
{
    public function count(): int;
}

interface Serializable
{
    public function serialize(): string;
}

// DNF type - parametr musi być:
// (Countable i Stringable) LUB Serializable
function processCollection((Countable&Stringable)|Serializable $collection): string
{
    if ($collection instanceof Serializable) {
        return $collection->serialize();
    }

    // Tu wiemy że $collection implementuje Countable i Stringable
    return "Kolekcja '{$collection}' ma {$collection->count()} elementów";
}

// Praktyczne użycie - nullable intersection type
// Przed PHP 8.2 nie można było pisać ?(A&B)
// PHP 8.2 - (A&B)|null zamiast ?(A&B) - to samo, nowa składnia
function findItem((Countable&Stringable)|null $collection): ?string
{
    if ($collection === null) {
        return null;
    }

    return "Znaleziono kolekcję z {$collection->count()} elementami";
}

Deprecacje w PHP 8.2 – na co uważać w Magento

PHP 8.2 deprecjonuje kilka rzeczy które mogą boleć przy starszych projektach Magento:

<?php

// 1. Dynamiczne właściwości - deprecated w PHP 8.2, Error w PHP 9.0
class Product
{
    public string $name;
    // brak deklaracji $customAttribute
}

$product = new Product();
$product->customAttribute = 'wartość'; // PHP 8.2: Deprecated warning
                                        // PHP 9.0: Error

// Rozwiązanie - zadeklaruj właściwość lub użyj atrybutu #[AllowDynamicProperties]
#[\AllowDynamicProperties]
class LegacyModel
{
    // Klasy z tym atrybutem mogą nadal mieć dynamiczne właściwości
    // Użyteczne przy migracji starszego kodu Magento
}

// Magento\Framework\DataObject używa dynamicznych właściwości przez _data[]
// Core Magento ma już obsługę #[AllowDynamicProperties] dla PHP 8.2

// 2. Callables przez string - niektóre formy deprecated
$fn = 'strlen';
$fn('test'); // ok
call_user_func('strlen', 'test'); // ok

// Deprecated: tworzenie Closure przez nie-statyczne metody przez string
// $closure = Closure::fromCallable([$obj, 'method']); // ok
// 'ClassName::method' jako callable - deprecated w niektórych kontekstach

// 3. ${var} w stringach - deprecated
$var  = 'world';
echo "Hello ${var}"; // Deprecated - użyj {$var}
echo "Hello {$var}"; // ok
echo "Hello $var";   // ok

Nowe funkcje w PHP 8.2

<?php

// mysqli_execute_query() - prepared statement w jednej linii
$result = mysqli_execute_query($connection, 'SELECT * FROM users WHERE id = ?', [$id]);

// curl_upkeep() - utrzymanie połączenia curl
curl_upkeep($curlHandle);

// Random\Randomizer - OOP API dla generowania liczb losowych
$randomizer = new \Random\Randomizer();
echo $randomizer->getInt(1, 100);      // losowa liczba 1-100
echo $randomizer->shuffleArray([1,2,3,4,5])[0]; // przetasowana tablica

// Bardziej przewidywalne niż mt_rand() przy testach - można wstrzyknąć seed
$seededRandomizer = new \Random\Randomizer(new \Random\Engine\Mt19937(42));
echo $seededRandomizer->getInt(1, 100); // zawsze ta sama wartość dla seed=42

// fibers - ulepszenia API (nie nowy feature, ale poprawki)
// array_is_list() - dostępne od PHP 8.1, działa bez zmian
var_dump(array_is_list([1, 2, 3]));         // true
var_dump(array_is_list(['a' => 1, 'b' => 2])); // false

Kiedy przejść na PHP 8.2 w projekcie Magento?

Magento 2.4.6 (planowane Q1 2023) ma oficjalnie wspierać PHP 8.2. Przed migracją warto sprawdzić:

  • Zewnętrzne moduły – czy vendor obsługuje PHP 8.2
  • Dynamiczne właściwości – czy własny kod nie polega na nich bez #[AllowDynamicProperties]
  • Uruchomić vendor/bin/phpstan analyse --level=6 z regułami PHP 8.2
  • Sprawdzić PHP Compatibility Checker: vendor/bin/phpcs --standard=PHPCompatibility --runtime-set testVersion 8.2

Podsumowanie

PHP 8.2 to ewolucja, nie rewolucja. Readonly classes od razu wejdą do nowych Value Objects i DTO – eliminują powtarzanie readonly przy każdej właściwości. DNF types domkną system typów i pozwolą wyrażać złożone kombinacje typów które wcześniej wymagały komentarzy. Deprecacja dynamicznych właściwości to sygnał do uporządkowania starszego kodu – szczególnie w kontekście modeli opartych na DataObject w Magento.

About Henryk Tews

Co możesz przeczytać następne

PHP 8.1 w praktyce – enumy po miesiącach, serializacja do bazy, Money value object
PHP 8.3 preview – typed constants, json_validate(), array_find(), clone with
Typowanie w PHP 7.x, strict_types, PHPStan, przygotowanie na PHP 7.4
  • 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}