Phase 3 · Object-Oriented & Modern PHPModule 19~48 min read

Modern Types, Enums & Attributes

Use strict types, rich type declarations, enums, attributes, and variance to make illegal states harder to represent.

What you'll learn

Use PHP's modern type system to make invalid states difficult to express. Enums model closed choices, union and intersection types clarify contracts, and attributes attach structured metadata for frameworks and tools.

By the end of this lesson, you'll be able to:

  • Apply strict and expressive type declarations
  • Model finite states with backed enums
  • Read a custom attribute through reflection

Core mental model

Professional PHP is less about memorizing APIs and more about choosing a clear boundary for each responsibility. Use this table as a decision guide while reading the examples.

ConceptWhat it protectsDecision rule
Strict typesPredictable scalar callsDeclare it in every application PHP file.
EnumClosed valid choicesUse instead of magic strings with a known set.
AttributeMachine-readable metadataUse when another component must inspect declarations.

Professional workflow

Build the feature in small, verifiable steps. Each step leaves the system in a state you can test.

  1. Describe the type and metadata boundary boundary: its inputs, outputs, invariants, and expected failures.
  2. Implement the smallest happy path behind an explicit contract.
  3. Add validation and translate low-level failures into language the caller understands.
  4. Exercise the boundary with realistic data, then inspect output, logs, and resource cleanup.
  5. Refactor only after behavior is protected by a repeatable check.

Make the boundary visible

Name inputs, outputs, side effects, and failure cases before adding framework or infrastructure code. That habit keeps advanced PHP understandable as the application grows.

Guided code lab

Model lifecycle states

A backed enum gives storage values while keeping valid choices exhaustive.

CourseStatus.php
<?php
declare(strict_types=1);

enum CourseStatus: string
{
    case Draft = 'draft';
    case Published = 'published';
    case Archived = 'archived';

    public function isVisible(): bool
    {
        return $this === self::Published;
    }
}

$status = CourseStatus::from('published');

Attach route metadata

The attribute carries data; reflection-based infrastructure decides how to use it.

Route.php
<?php
#[Attribute(Attribute::TARGET_METHOD)]
final readonly class Route
{
    public function __construct(public string $method, public string $path) {}
}

final class CourseController
{
    #[Route('GET', '/courses')]
    public function index(): array { return []; }
}

$method = new ReflectionMethod(CourseController::class, 'index');
$route = $method->getAttributes(Route::class)[0]->newInstance();

Production practice

Contract

Prefer the narrowest honest return and parameter types; avoid mixed at internal boundaries.

Verification

Test every enum case and any reflection convention that turns attributes into runtime behavior.

Operations

Static analysis should run at a strict level so declared types stay truthful across changes.

Common failure mode

Types do not validate business meaning. A string type still accepts an empty email; pair types with domain validation.

Independent workshop

Create a typed command bus with enum priorities and a Handler attribute discovered through reflection.

Your finished workshop must include:

  • Backed Priority enum with behavior
  • Repeatable Handler attribute
  • A discovery script rejecting duplicate handlers

Definition of done

Run the happy path and at least two failure paths, explain one design tradeoff in a short README, and leave the code formatted and ready for review.

Recap & quick check

Key takeaways

  • Strict declarations reduce surprising coercion.
  • Enums replace scattered magic strings.
  • Attributes are metadata, not hidden business logic.
  • Static analysis extends what runtime types can prove.

Quick check

1. What does an enum represent best?

2. Who interprets an attribute?

3. Does string guarantee a valid email?

Keep the workshop: later phases deliberately build on these boundaries, so today's small example can become part of your portfolio architecture.