Skip to content

PHP 8 Features — Complete Guide to Named Arguments, Attributes, Readonly

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about PHP 8 Features. We cover key concepts, practical examples, and best practices to help you master this topic.

PHP 8 introduced major language features including named arguments, attributes, readonly properties, union types, match expression, nullsafe operator, and constructor property promotion.

What You'll Learn

By the end of this tutorial, you'll use PHP 8 named arguments, create and read attributes, use readonly properties, leverage union types, and use the match expression and nullsafe operator.

Why PHP 8 Matters

PHP 8 modernizes the language significantly. Named arguments improve API clarity, attributes replace docblock annotations, and readonly properties enforce immutability.

Real-World Use

A PHP 8 application uses named arguments for query Builder methods, readonly DTOs with constructor promotion, and custom attributes for validation rules, eliminating boilerplate.

PHP 8 Path

flowchart LR
  A[PHP Basics] --> B[PHP 8 Features]
  B --> C[PHP 8.1 Enums/Fibers]
  B --> D[PHP 8.2 Readonly]
  C --> E[PHP 8.3/8.4]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

Named Arguments

Pass arguments by parameter name instead of position, skipping defaults.

<?php
function createUser(string $name, string $email = "", bool $admin = false, array $tags = []): array {
    return compact("name", "email", "admin", "tags");
}
$user = createUser(name: "Alice", admin: true, tags: ["vip"]);
print_r($user);
// Skipped $email defaults to ""

Constructor Property Promotion

Declare and assign properties directly in the constructor signature.

<?php
class User {
    public function __construct(
        public string $name,
        private string $email,
        protected bool $admin = false,
        private \DateTimeImmutable $createdAt = new \DateTimeImmutable()
    ) {}
    public function getEmail(): string { return $this->email; }
}
$user = new User(name: "Alice", email: "alice@example.com");
echo $user->name;

Attributes

Replace docblock annotations with native PHP attributes.

<?php
#[Attribute]
class ValidationRule {
    public function __construct(
        public string $rule,
        public ?string $message = null
    ) {}
}
class UserDTO {
    public function __construct(
        #[ValidationRule("required")]
        #[ValidationRule("min:3")]
        public string $name,
        #[ValidationRule("email")]
        public string $email
    ) {}
}
$reflection = new ReflectionClass(UserDTO::class);
foreach ($reflection->getProperties() as $prop) {
    $attrs = $prop->getAttributes(ValidationRule::class);
    echo "{$prop->name}: " . count($attrs) . " rules\n";
}

Readonly Properties

Readonly properties can be set once and never changed after initialization.

<?php
class Config {
    public readonly string $apiKey;
    public readonly int $timeout;
    public function __construct(string $apiKey, int $timeout = 30) {
        $this->apiKey = $apiKey;
        $this->timeout = $timeout;
    }
}
$config = new Config(apiKey: "sk-123", timeout: 60);
echo $config->apiKey;
// $config->apiKey = "new"; // Error: Cannot modify readonly property

Match Expression

Match is a strict type-safe alternative to switch that returns a value.

<?php
function getStatusLabel(int $code): string {
    return match ($code) {
        200 => "OK",
        201 => "Created",
        301, 302 => "Redirect",
        400 => "Bad Request",
        401 => "Unauthorized",
        403 => "Forbidden",
        404 => "Not Found",
        500 => "Server Error",
        default => "Unknown",
    };
}
echo getStatusLabel(404);

Common Mistakes

1. Mixing Named and Positional Arguments

Named arguments must come after positional in PHP 8.0. PHP 8.1 allows mixing but keep consistent.

2. Forgetting Attributes Need #[Attribute] Declaration

Custom attribute classes must be marked with #[Attribute]. Otherwise, Reflection ignores them.

3. Setting Readonly Properties After Construction

Readonly properties can only be set during declaration or in the constructor. Any later assignment throws an error.

4. Misunderstanding Match Strict Comparison

Match uses === (strict) comparison, not == (loose). 0 and "0" do not match.

5. Overusing Named Arguments in Hot Paths

Named arguments have a small performance overhead. Use positional for performance-critical internal calls.

Practice Questions

1. What is constructor property promotion?

A PHP 8 feature that declares and assigns class properties directly in the constructor parameter list.

2. How do you create a custom attribute?

Define a class with #[Attribute] and use #[AttributeName] before classes, methods, or properties.

3. What does readonly do on a property?

Prevents modification after initialization. The property can be set once and is immutable afterward.

4. How is match different from switch?

Match returns a value, uses strict comparison, and throws on unhandled cases without default.

5. Challenge: Create a DTO class using PHP 8 features.

<?php
#[Attribute]
class SerializedName {
    public function __construct(public string $name) {}
}
class ProductDTO {
    public function __construct(
        public readonly string $name,
        #[SerializedName("product_price")]
        public readonly float $price,
        public readonly int $stock = 0,
        public readonly ?string $description = null,
    ) {}
}

FAQ

Can named arguments be combined with variadic parameters?

Yes, but named arguments cannot be passed to variadic parameters that are not explicitly named.

Are PHP 8.0 features backward compatible?

Mostly. Named arguments and match may cause issues with functions that expect positional arguments.

What is the syntax for union types?

string|int|float declares a parameter that accepts string, int, or float types.

How does the nullsafe operator work?

$user?->address?->city returns null if any part of the chain is null instead of throwing an error.

What is the str_contains function?

A PHP 8.0 function that checks if a string contains another string: str_contains('hello', 'ell') returns true.

Mini Project: Request Validation with Attributes

Build a validation system using PHP 8 attributes.

<?php
#[Attribute]
class Validate {
    public function __construct(public string $rule) {}
}
class Request {
    public function __construct(
        #[Validate("required")] public string $name,
        #[Validate("email")] public string $email,
        #[Validate("min:8")] public string $password,
    ) {}
}
function validate(object $dto): array {
    $errors = [];
    $ref = new ReflectionClass($dto);
    foreach ($ref->getProperties() as $prop) {
        $attrs = $prop->getAttributes(Validate::class);
        foreach ($attrs as $attr) {
            $rule = $attr->newInstance()->rule;
            $value = $prop->getValue($dto);
            if ($rule === "required" && empty($value)) $errors[] = "{$prop->name} is required";
            if (str_starts_with($rule, "min:")) {
                $min = (int) substr($rule, 4);
                if (strlen($value) < $min) $errors[] = "{$prop->name} must be at least {$min}";
            }
        }
    }
    return $errors;
}

What's Next

PHP 8.1 Enums and Fibers PHP 8.2 Readonly Classes PHP PSR Standards

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro