Nowości w PHP 8.2 - Readonly Classes, DNF Types i Standalone Types

11.07.2025

Nowości w PHP 8.2 - Readonly Classes, DNF Types i Standalone Types

PHP 8.2, wydany 8 grudnia 2022 roku, wprowadza kolejne znaczące usprawnienia języka. Wśród najważniejszych nowości znajdują się klasy readonly, typy DNF (Disjunctive Normal Form) oraz standalone types null, false i true. Te dodatki czynią PHP jeszcze bardziej ekspresyjnym i bezpiecznym językiem.

Dlaczego PHP 8.2?

PHP 8.2 koncentruje się na uproszczeniu kodu i zwiększeniu bezpieczeństwa typów. Wprowadza funkcje, które eliminują powtarzalny kod i pozwalają na bardziej precyzyjne definiowanie kontraktów API.

1. Readonly Classes - Klasy Tylko do Odczytu

Readonly classes automatycznie oznaczają wszystkie właściwości jako readonly. To syntactic sugar dla klas immutable, szczególnie przydatny w DTO (Data Transfer Objects) i Value Objects.

<?php
// PHP 8.2 - Readonly class
readonly class UserDTO
{
    public function __construct(
        public string $name,
        public string $email,
        public int $age,
        public array $roles
    ) {}
}

// PHP 8.1 - każda właściwość readonly osobno
class UserDTO
{
    public function __construct(
        public readonly string $name,
        public readonly string $email,
        public readonly int $age,
        public readonly array $roles
    ) {}
}

// Użycie
$user = new UserDTO(
    name: 'Jan Kowalski',
    email: 'jan@example.com',
    age: 30,
    roles: ['admin', 'editor']
);

// OK - odczyt
echo $user->name;

// Error - próba modyfikacji
$user->age = 31; // Fatal error

2. DNF Types - Disjunctive Normal Form Types

DNF types pozwalają na łączenie union types i intersection types zgodnie ze standardem normalnej formy dysjunkcyjnej. Innymi słowy, możesz tworzyć typy w postaci OR-ów z AND-ów, gdzie intersection types muszą być w nawiasach.

<?php
interface Loggable
{
    public function log(): void;
}

interface Cacheable
{
    public function cache(): void;
}

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

// DNF Type: (Loggable & Cacheable) lub Serializable
function process((Loggable&Cacheable)|Serializable $object): void
{
    if ($object instanceof Serializable) {
        $data = $object->serialize();
    } else {
        $object->log();
        $object->cache();
    }
}

// Nullable intersection type - najważniejszy przypadek użycia
function save((Loggable&Cacheable)|null $object): void
{
    if ($object !== null) {
        $object->log();
        $object->cache();
    }
}

3. Standalone Types: null, false, true

PHP 8.2 pozwala używać null, false i true jako samodzielnych typów, nie tylko w kombinacji z innymi typami. To szczególnie przydatne przy definiowaniu precyzyjnych kontraktów funkcji.

<?php
// null jako standalone type
function getConfig(string $key): mixed
{
    return $this->config[$key] ?? null;
}

// false jako standalone type - funkcje walidacyjne
function validateEmail(string $email): false|array
{
    $parsed = filter_var($email, FILTER_VALIDATE_EMAIL);
    if ($parsed === false) {
        return false;
    }
    return ['email' => $parsed, 'valid' => true];
}

// true jako standalone type
function isAdmin(User $user): true
{
    if (!$user->hasRole('admin')) {
        throw new \Exception('User is not admin');
    }
    return true; // Zawsze zwraca true lub rzuca wyjątek
}

4. Random Extension - Nowe OOP API

PHP 8.2 wprowadza nowe, obiektowe API do generowania liczb losowych, które jest bardziej bezpieczne i łatwiejsze w użyciu niż stare funkcje rand() i mt_rand().

<?php
// Nowy Randomizer
$randomizer = new \Random\Randomizer();

// Losowa liczba z zakresu
$dice = $randomizer->getInt(1, 6);

// Losowe bajty
$bytes = $randomizer->getBytes(16);

// Przetasowanie tablicy
$cards = ['A', 'K', 'Q', 'J', '10', '9', '8', '7'];
$shuffled = $randomizer->shuffleArray($cards);

// Wybór losowego elementu
$winner = $randomizer->pickArrayKeys($participants, 1);

// Możesz też używać różnych silników
$secure = new \Random\Randomizer(new \Random\Engine\Secure());
$token = bin2hex($secure->getBytes(32));

5. Deprecated Dynamic Properties

PHP 8.2 oznacza dynamiczne właściwości jako deprecated (poza stdClass i __get/__set). To krok w stronę bardziej przewidywalnego i bezpiecznego kodu.

<?php
class User
{
    public string $name;
}

$user = new User();
$user->name = 'Jan'; // OK

// Deprecated w PHP 8.2
$user->email = 'jan@example.com'; // Deprecation warning

// Jeśli chcesz używać dynamicznych właściwości, użyj #[AllowDynamicProperties]
#[\AllowDynamicProperties]
class FlexibleUser
{
    public string $name;
}

$user = new FlexibleUser();
$user->email = 'jan@example.com'; // OK - dozwolone przez atrybut

6. SensitiveParameter Attribute - Ukrywanie Wrażliwych Danych

Atrybut #[SensitiveParameter] pozwala na ukrywanie wrażliwych danych (takich jak hasła, tokeny API) w stack trace i logach. Parametry oznaczone tym atrybutem będą wyświetlane jako "Object(SensitiveParameterValue)" zamiast rzeczywistej wartości.

<?php
function login(
    string $username,
    #[\SensitiveParameter] string $password
): bool {
    if ($password !== 'secret123') {
        throw new \Exception('Invalid password');
    }
    return true;
}

try {
    login('admin', 'wrong_password');
} catch (\Exception $e) {
    echo $e->getTraceAsString();
}

// Output w PHP 8.2:
// #0 login('admin', Object(SensitiveParameterValue))
// Hasło NIE jest wyświetlone w stack trace!

// Dla porównania PHP 8.1:
// #0 login('admin', 'wrong_password')
// Hasło jest widoczne - potencjalne zagrożenie bezpieczeństwa

// Użycie z API
class ApiClient
{
    public function __construct(
        private string $endpoint,
        #[\SensitiveParameter] private string $apiKey
    ) {}

    public function request(string $path): array
    {
        // Jeśli wystąpi błąd, apiKey nie będzie w traceback
        return $this->http->get($this->endpoint . $path);
    }
}

// Database connection
function connectToDatabase(
    string $host,
    string $database,
    string $username,
    #[\SensitiveParameter] string $password
): PDO {
    return new PDO(
        "mysql:host=$host;dbname=$database",
        $username,
        $password
    );
}

Inne ważne nowości w PHP 8.2

  • Constants in traits - możliwość definiowania stałych w traitach
  • Fetch enum properties in const expressions - użycie właściwości enumów w wyrażeniach stałych
  • Deprecate ${} string interpolation - przestarzała składnia interpolacji stringów
  • Disjunctive Normal Form (DNF) Types - kombinacja union i intersection types

🎓 Chcesz poznać wszystkie nowości PHP?

Ten wpis to fragment kompleksowego kursu PHP 8.4, w którym szczegółowo omawiam wszystkie wersje PHP 8.x wraz z praktycznymi przykładami z rzeczywistych projektów.

Kup pełny kurs PHP 8.4 🚀 Pobierz darmowy fragment 📥

Zobacz też inne wersje PHP