Lab 02: Readonly & Enums

Time: 40 minutes | Level: Advanced | Docker: docker run -it --rm php:8.3-cli bash

PHP 8.1 introduced readonly properties and enums. PHP 8.2 added readonly classes. Together these features enable immutable value objects and type-safe constants with behavior.


Step 1: Readonly Properties (PHP 8.1)

<?php
class Point {
    public function __construct(
        public readonly float $x,
        public readonly float $y,
    ) {}
}

$p = new Point(3.0, 4.0);
echo "$p->x, $p->y\n";  // 3, 4

try {
    $p->x = 1.0;  // Fatal error
} catch (\Error $e) {
    echo $e->getMessage() . "\n";
}

📸 Verified Output:

💡 Readonly properties can only be written once, during initialization (in the constructor). Uninitialized readonly properties cannot be read.


Step 2: Readonly Classes (PHP 8.2)

Marking a class readonly makes ALL properties readonly automatically:

📸 Verified Output:

💡 Readonly classes cannot have non-readonly properties. They work perfectly as value objects and DTOs.


Step 3: Pure Enums

Pure enums have named cases but no backing value:

📸 Verified Output:


Step 4: Backed Enums (int and string)

📸 Verified Output:


Step 5: Enum with Interface and Constants

📸 Verified Output:


Step 6: Enum in match Expression

📸 Verified Output:

💡 Enums work in match with identity comparison (===). No need for ->value comparison.


Step 7: Enum as Type — No Invalid States

📸 Verified Output:


Step 8: Capstone — Immutable State Machine

Combine readonly classes and enums for a type-safe order processing state machine:

📸 Verified Output:


Summary

Feature
Syntax
PHP Version

Readonly property

public readonly Type $prop

8.1+

Readonly class

readonly class Foo {}

8.2+

Pure enum

enum Suit { case Hearts; }

8.1+

Backed enum (string)

enum Status: string { case A = 'a'; }

8.1+

Backed enum (int)

enum Perm: int { case R = 1; }

8.1+

Get all cases

Status::cases()

8.1+

From value (strict)

Status::from('active')

8.1+

From value (safe)

Status::tryFrom('unknown') → null

8.1+

Enum implements interface

enum Foo: string implements Bar {}

8.1+

Enum constants

const DEFAULT = self::CaseName;

8.1+

Last updated