Tous les articles
PHPBackend

PHP 8.4: Property Hooks, Asymmetric Visibility, and Four New Array Functions

//
·7 min de lecture

PHP 8.4 introduces property hooks as a first-class language feature, asymmetric property visibility (public read / protected write), and four new array functions: array_find, array_find_key, array_any, and array_all.

PHP 8.4 was released on November 21, 2024. It is the most feature-rich minor release since PHP 8.1 introduced enums and fibers. The two headline additions — property hooks and asymmetric visibility — eliminate large classes of boilerplate without changing runtime semantics.

>Property Hooks

Property hooks let you attach get and set logic directly to a property declaration, removing the need for separate getter/setter methods in many cases.

php
<?php

class User
{
    // Computed property — no backing storage, get-only
    public string $fullName {
        get => "$this->firstName $this->lastName";
    }

    // Validated setter — modifies the value before storing
    public string $email {
        set(string $value) {
            if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
                throw new ValueError("Invalid email: $value");
            }
            $this->email = strtolower($value);
        }
    }

    // Both hooks — normalise on write, format on read
    public string $phone {
        get => '+' . ltrim($this->phone, '+');
        set => preg_replace('/D/', '', $value);
    }

    public function __construct(
        public readonly string $firstName,
        public readonly string $lastName,
        string $email,
    ) {
        $this->email = $email;
    }
}

$user = new User('Alice', 'Smith', 'Alice@Example.COM');
echo $user->email;    // alice@example.com
echo $user->fullName; // Alice Smith

NOTEProperties with hooks cannot be used with readonly unless the hook is get-only. Interface properties can now declare required hooks.

>Asymmetric Visibility

Properties can now have different read and write visibility — a pattern previously requiring manual getter methods.

php
<?php

class Order
{
    // Public read, private write
    public private(set) int $itemCount = 0;

    // Public read, protected write
    public protected(set) OrderStatus $status = OrderStatus::Draft;

    public function addItem(OrderItem $item): void
    {
        $this->items[]   = $item;
        $this->itemCount++;              // ✓ private write allowed here
    }

    protected function updateStatus(OrderStatus $status): void
    {
        $this->status = $status;         // ✓ protected write allowed here
    }
}

$order = new Order();
echo $order->itemCount;       // ✓ public read
$order->itemCount = 5;        // ✗ Error: cannot write private(set) from outside

>New array functions

php
<?php

$users = [
    ['name' => 'Alice', 'age' => 30, 'active' => true],
    ['name' => 'Bob',   'age' => 17, 'active' => false],
    ['name' => 'Carol', 'age' => 25, 'active' => true],
];

// array_find() — first element matching predicate, or null
$firstAdult = array_find($users, fn($u) => $u['age'] >= 18);
// ['name' => 'Alice', ...]

// array_find_key() — key of first matching element, or null
$key = array_find_key($users, fn($u) => $u['name'] === 'Carol');
// 2

// array_any() — true if at least one element matches
$hasInactive = array_any($users, fn($u) => !$u['active']);
// true

// array_all() — true if every element matches
$allAdults = array_all($users, fn($u) => $u['age'] >= 18);
// false

>#[\Deprecated] attribute

php
<?php

class PaymentGateway
{
    #[Deprecated(
        message: 'Use processPayment() instead.',
        since:   '2.0',
    )]
    public function charge(int $cents): bool
    {
        return $this->processPayment($cents);
    }

    public function processPayment(int $cents): bool { /* ... */ }
}

// Calling a deprecated method triggers E_USER_DEPRECATED
$gw = new PaymentGateway();
$gw->charge(1000); // Deprecated: charge() — Use processPayment() instead.

>new without parentheses

php
<?php

// ✗ PHP 8.3 — parentheses required to chain
$result = (new Parser())->parse($input);

// ✓ PHP 8.4 — chain directly
$result = new Parser()->parse($input);

// Works with static methods, property access, array access
$value   = new Config()->get('key');
$version = new ApiClient()->version;