PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

PHP 8.2 preview – readonly classes, DNF types, dynamic properties deprecated

by Henryk Tews / Tuesday, 12 July 2022 / Published in PHP

PHP 8.2 arrives in November 2022 and its headline features are more conservative than 8.0 or 8.1, but each one plugs a real gap in the type system. Readonly classes eliminate boilerplate in immutable DTOs. DNF types allow complex type expressions that were impossible before. Deprecating dynamic properties starts enforcing something good OOP practice demanded for years.

Readonly classes – immutable DTOs without effort

<?php

declare(strict_types=1);

// PHP 8.1 - had to mark each property individually
class ProductDto
{
    public function __construct(
        public readonly int $id,
        public readonly string $sku,
        public readonly string $name,
        public readonly float $price,
        public readonly bool $isActive,
    ) {}
}

// PHP 8.2 - readonly on the class makes ALL properties readonly
readonly class ProductDto
{
    public function __construct(
        public int $id,
        public string $sku,
        public string $name,
        public float $price,
        public bool $isActive,
    ) {}
}

// Modification attempt throws Error
$dto = new ProductDto(42, 'SKU-001', 'Test', 99.99, true);
// $dto->price = 79.99; // Error: Cannot modify readonly property

// Create a modified copy using with-syntax (PHP 8.2)
$discounted = new ProductDto(
    id:       $dto->id,
    sku:      $dto->sku,
    name:     $dto->name,
    price:    $dto->price * 0.9,
    isActive: $dto->isActive,
);

Readonly classes with inheritance

<?php

declare(strict_types=1);

readonly class Money
{
    public function __construct(
        public int $amount,
        public string $currency,
    ) {}

    public function add(self $other): static
    {
        if ($this->currency !== $other->currency) {
            throw new \InvalidArgumentException('Currency mismatch');
        }
        return new static($this->amount + $other->amount, $this->currency);
    }
}

// PHP 8.2: readonly class can be extended by another readonly class
readonly class TaxedMoney extends Money
{
    public function __construct(
        int $amount,
        string $currency,
        public float $taxRate,
    ) {
        parent::__construct($amount, $currency);
    }

    public function netAmount(): int
    {
        return (int) round($this->amount / (1 + $this->taxRate));
    }
}

$price = new TaxedMoney(12300, 'PLN', 0.23); // 123.00 PLN gross
echo $price->netAmount(); // 10000 (100.00 PLN net)

DNF Types – Disjunctive Normal Form

<?php

interface Stringable  { public function __toString(): string; }
interface Countable   { public function count(): int; }
interface Serialisable { public function serialise(): string; }

// PHP 8.1 could do intersection: A&B
// PHP 8.1 could do union: A|B
// PHP 8.2 can combine both: (A&B)|C - DNF type

// Accepts: something that is BOTH Countable AND Stringable, OR a plain string
function processCollection((Countable&Stringable)|string $data): string
{
    if (is_string($data)) {
        return $data;
    }
    return "Collection with {$data->count()} items: {$data}";
}

// Accept null OR (Countable AND Stringable)
function maybeProcess((Countable&Stringable)|null $data): string
{
    if ($data === null) return 'empty';
    return "Count: {$data->count()}";
}

Dynamic properties deprecated

<?php

class Product
{
    public string $name = '';
    public float $price = 0.0;
}

$product = new Product();
$product->name  = 'Widget'; // OK - declared property
$product->color = 'red';    // PHP 8.2: Deprecated: Creation of dynamic property
                             // PHP 9.0: Fatal Error

// Fix 1: Declare the property
class Product
{
    public string $name  = '';
    public float $price  = 0.0;
    public string $color = ''; // declare it
}

// Fix 2: Allow dynamic properties explicitly (for legacy code)
#[\AllowDynamicProperties]
class LegacyProduct
{
    public string $name = '';
}

$product = new LegacyProduct();
$product->anything = 'value'; // OK with the attribute

Impact on Magento 2 – what to check

# Find dynamic property usage in your modules
grep -rn "\$this->" app/code/ --include="*.php" | \
    grep -v "public\|protected\|private" | \
    grep -v "//" | head -50

# PHPStan catches this at level 2+
vendor/bin/phpstan analyse app/code/ --level=2 --php-version=8.2

# Rector can add #[AllowDynamicProperties] automatically to classes that need it
vendor/bin/rector process app/code/ \
    --only=\Rector\Php82\Rector\Class_\AllowDynamicPropertiesAttributeRector \
    --dry-run

Summary

PHP 8.2 is an evolutionary release that tightens the screws. Readonly classes make immutable DTOs the zero-cost default. DNF types complete the type algebra. Dynamic property deprecation enforces 20 years of OOP best practice at the language level. For Magento 2 projects the dynamic properties deprecation is the most pressing item – run PHPStan and Rector now to find and fix the affected classes before upgrading.

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}