Skip to main content
Back to Blog PHP

PHP 8 Features Every Developer Should Be Using

Abdelrahman Shrief
Abdelrahman Shrief February 19, 2026
5 min read

Named Arguments

Pass arguments by name instead of position for clearer code:

<?php

function createUser(
    string $name,
    string $email,
    string $role = 'user',
    bool $sendWelcomeEmail = true,
    ?string $avatar = null
): User {
    // Implementation
}

// Old way - hard to read
createUser('John', 'john@example.com', 'admin', true, null);

// With named arguments - crystal clear
createUser(
    name: 'John',
    email: 'john@example.com',
    role: 'admin',
    sendWelcomeEmail: true,
);

// Skip optional arguments
createUser(
    name: 'Jane',
    email: 'jane@example.com',
    avatar: 'https://example.com/avatar.jpg',
);

Constructor Property Promotion

Reduce boilerplate in your classes:

<?php

// Before PHP 8
class Point
{
    public float $x;
    public float $y;
    public float $z;

    public function __construct(float $x, float $y, float $z)
    {
        $this->x = $x;
        $this->y = $y;
        $this->z = $z;
    }
}

// PHP 8 - Constructor Property Promotion
class Point
{
    public function __construct(
        public float $x,
        public float $y,
        public float $z,
    ) {}
}

// With readonly (PHP 8.1+)
class User
{
    public function __construct(
        public readonly int $id,
        public readonly string $name,
        public string $email,
    ) {}
}

Match Expression

A more powerful alternative to switch statements:

<?php

// Old switch statement
switch ($status) {
    case 'draft':
        $label = 'Document is in draft';
        break;
    case 'published':
        $label = 'Document is live';
        break;
    case 'archived':
        $label = 'Document is archived';
        break;
    default:
        $label = 'Unknown status';
}

// PHP 8 match expression
$label = match($status) {
    'draft' => 'Document is in draft',
    'published' => 'Document is live',
    'archived' => 'Document is archived',
    default => 'Unknown status',
};

// Match with multiple conditions
$emoji = match($status) {
    'draft', 'pending' => '📝',
    'published', 'active' => '✅',
    'archived', 'deleted' => '📦',
    default => '❓',
};

Nullsafe Operator

Safely access nested nullable properties:

<?php

// Before PHP 8 - verbose null checks
$country = null;
if ($user !== null) {
    $address = $user->getAddress();
    if ($address !== null) {
        $country = $address->country;
    }
}

// PHP 8 - Nullsafe operator
$country = $user?->getAddress()?->country;

// Combining with null coalescing
$countryName = $user?->address?->country?->name ?? 'Unknown';

Attributes

Native metadata annotations in PHP:

<?php

use Attribute;

#[Attribute(Attribute::TARGET_METHOD)]
class Route
{
    public function __construct(
        public string $path,
        public string $method = 'GET',
    ) {}
}

class UserController
{
    #[Route('/users', method: 'GET')]
    public function index(): array
    {
        return User::all()->toArray();
    }

    #[Route('/users/{id}', method: 'GET')]
    public function show(int $id): array
    {
        return User::findOrFail($id)->toArray();
    }

    #[Route('/users', method: 'POST')]
    public function store(): array
    {
        // Create user
    }
}

Conclusion

PHP 8 brings significant improvements to the language. Start using these features today to write cleaner, more maintainable code.

Abdelrahman Shrief
Abdelrahman Shrief Senior Backend Developer

Share this post

Need Help With Your Project?

Let's discuss how I can help bring your ideas to life with expert backend development.

Get in Touch