Рік із property hooks: як найважливіша функція PHP десятиліття змінює написання коду

Перекладено ШІ 0 stitcher.io 30 липня, 2026

Property hooks у PHP 8.4 стали наймасштабнішим оновленням десятиліття, що кардинально змінює підхід до написання коду. Дізнайтеся, як цей інструмент допомагає позбутися зайвих getters і setters та ефективно використовувати властивості в interfaces.

Минув рівно рік з моменту, як property hooks інтегрували в ядро PHP. Ті, хто стежить за моєю роботою над Tempest, знають: щойно це сталося, ми почали готувати кодову базу до PHP 8.4, впроваджуючи нову фічу скрізь, де це було доречно. Я вважаю себе «early adopter», тож хочу підбити підсумки використання property hooks за цей рік.

Багато хто ще навіть не перейшов на PHP 8.4, не кажучи вже про використання property hooks. Тож зараз ідеальний час, щоб познайомитися з цією новинкою, яку я б назвав однією з найважливіших фіч PHP останнього десятиліття.

Що таке property hooks?

Property hooks — це одна з наймасштабніших та найдетальніших RFC в історії PHP. Прочитати її повністю — неабияка справа, тому дозвольте мені коротко все резюмувати. Property hooks дозволяють «втручатися» в процес отримання (get) та встановлення (set) значення властивості. Це схоже на магічні методи __get() та __set(), але для кожної конкретної властивості окремо:

final class Book
{
    public function __construct(
        private array $authors,
    ) {}

    public string $credits {
        get {
            return implode(', ', array_map(
                fn (Author $author) => $author->name, 
                $this->authors,
            ));
        }
    }
    
    public Author $mainAuthor {
        set (Author $mainAuthor) {
            $this->authors[] = $mainAuthor;
            $this->mainAuthor = $mainAuthor;
        }
        
        get => $this->mainAuthor;
    }
}

Property hooks чудово справляються зі скороченням шаблонного коду (boilerplate) для getters та setters. Це стосується не лише структури самого класу, а й того, як з ним взаємодіяти ззовні.

Замість такого коду:

$oldMainAuthor = $book->getMainAuthor();
$book->setMainAuthor($newMainAuthor);

echo $book->getCredits();

Завдяки property hooks ви можете писати так:

$oldMainAuthor = $book->mainAuthor;
$book->mainAuthor = $newMainAuthor;

echo $book->credits;

Особливо доречно property hooks виглядають у контексті models, value objects та data objects, роблячи публічний API класу набагато зручнішим.

Як я вже згадував, RFC щодо property hooks величезна. Вона містить багато деталей, як-от скорочений синтаксис (shorthand syntax):

final class Book
{
    public string $credits {
        get => implode(', ', array_map(
            fn (Author $author) => $author->name, 
            $this->authors,
        ));
    }

    // …
}

Або virtual properties — властивості, що мають лише доступ на читання (get):

final class Book
{
    public Author $mainAuthor {
        get => $this->authors[0];
    }
}

Також варто вивчити правила щодо references, успадкування та варіативності типів.

Але найважливіша перевага property hooks — значно вагоміша за інші — це можливість визначати їх в interface. Звучить незвично, чи не так? «Властивості в інтерфейсах». Але це справді має сенс. Зараз покажу.

Властивості в інтерфейсах

По суті, property hooks — це скорочений запис звичайних getters та setters, які є методами. З цієї точки зору цілком логічно мати можливість визначати їх в interface. Інакше вам довелося б і надалі використовувати звичайні методи кожного разу, коли ви працюєте через інтерфейс (що є гарною практикою). Тож замість цього:

interface Book
{
    public function getChapters(): array;
    public function getMainAuthor(): Author;
    public function getCredits(): string;
}

Ви можете написати так:

interface Book
{
    public array $chapters { get; }
    public Author $mainAuthor { get; }
    public string $credits { get; }
}

Тепер будь-який клас, що реалізує інтерфейс Book, зобов’язаний мати ці властивості доступними для читання. При цьому ви все ще можете зробити їх readonly або додати private(set). Це зберігає безпеку інкапсуляції, але позбавляє зайвого коду:

final class Ebook implements Book
{
    private(set) array $chapters;
    public readonly Author $mainAuthor;
    public readonly string $credits;
}

Для порівняння: якби ви хотіли такої ж інкапсуляції зі звичайними методами, ваш клас виглядав би так:

final class Ebook implements Book
{
    private array $chapters;
    private Author $mainAuthor;
    private string $credits;
    
    public function getChapters(): array
    {
        return $this->chapters;
    }
    
    private function addChapter(Chapter $chapter): void
    {
        $this->chapters[] = $chapter;
    }
    
    public function getMainAuthor(): Author
    {
        return $this->mainAuthor;
    }
    
    public function getCredits(): string
    {
        return $this->credits;
    }
}

Чесно кажучи, я втомився, поки просто писав цей приклад. Навіть не віриться, що ще рік тому це був єдиний спосіб.

Крім аргументу про «методи під прикриттям», є ще одна причина, чому властивості в інтерфейсах — це круто: data objects та value objects. Протягом останніх років PHP активно додавав фічі, що спрощують написання класів для зберігання даних: typed properties, readonly properties та classes, а також constructor property promotion.

final readonly class GenericRequest
{
    public function __construct(
        public Method $method,
        public string $uri,
        public array $headers,
        // …
    ) {}
}

Однак той факт, що властивості не могли бути частиною інтерфейсу, обмежував потенціал усіх цих нововведень — принаймні для тих, хто віддає перевагу програмуванню на рівні інтерфейсів. Можна сказати, що поява property hooks миттєво підсилила ефект від багатьох інших можливостей PHP. Саме тому я вважаю це найважливішою зміною останнього десятиліття.

Рік потому

Який мій досвід із property hooks? Я постійно використовую їх в інтерфейсах. Навіть якби ця RFC не принесла нічого, крім «властивостей в інтерфейсах», я був би щасливий:

interface Database
{
    public DatabaseDialect $dialect { get; }

    // …
}
interface DatabaseConfig extends HasTag
{
    public string $dsn { get; }
    
    // …
}
interface Request
{
    public Method $method { get; }
    public string $uri { get; }
    
    // …
}

Можливість створювати «хуки» для властивостей також корисна. Я регулярно використовую get hooks. Virtual properties іноді стають у пригоді, особливо в моделях чи data objects:

final class PageVisited implements ShouldBeStored, HasCreatedAtDate
{
    // …
    
    public DateTimeImmutable $createdAt {
        get => $this->visitedAt;
    }
}

А от для set hooks я не знайшов багато застосувань. В усій кодовій базі Tempest ми використали лише один:

final class TestingCache implements Cache
{
    private Cache $cache;
    
    public bool $enabled {
        get => $this->cache->enabled;
        set => $this->cache->enabled = $value;
    }
    
    // …
}

Це може мати сенс для «proxy objects», як-от ця обгортка для кешування, але в цілому таких випадків небагато.

Сама ж стилістика... нормальна. Мені не дуже подобається, що одну й ту саму дію можна зробити різними способами: є скорочені версії та неявна змінна $value для set hooks — це іноді плутає. Я прихильник «opinionated design», тож не заперечував би, якби існував лише один стандартний спосіб написання hooks, але це вже дрібниці.

Також я помітив, що зазвичай пишу property hooks після конструктора. Спочатку це здається дивним, бо це властивості, але якщо сприймати їх як методи, то все стає на свої місця.

final class WelcomeEmail implements Email, HasAttachments
{
    public function __construct(
        private readonly User $user,
    ) {}

    public Envelope $envelope {
        get => new Envelope(
            subject: 'Welcome',
            to: $this->user->email,
        );
    }

    public string|View $html {
        get => view('welcome.view.php', user: $this->user);
    }
    
    public array $attachments {
        get => [
            Attachment::fromFilesystem(__DIR__ . '/welcome.pdf')
        ];
    }
}

Зрештою, property hooks — це справді крута фіча та game changer для розробки на PHP. Якщо хочете обговорити це — чекаю на вас у Discord!

Популярні

Інше, що варто прочитати

13 Оновлено 26 червня, 2026

Удосконалюйте свої проєкти Laravel за допомогою справжнього штучного інтелекту для кодування з Laravel Boost!

Готові підняти свій робочий процес у Laravel на новий рівень? У цій статті я розгляну Laravel Boost, інноваційний AI-допомічник для програмування, який зробить вашу розробку швидшою та продуктивнішою

79 Оновлено 26 червня, 2026

Laravel Boost — ваш стартовий набір для програмування з використанням штучного інтелекту

Вперше у світі Laravel з'являється можливість, яка значно спростить ваше повсякденне програмування завдяки новому пакету Laravel Boost. Читайте статтю, щоб дізнатися, як посилена інтеграція штучного інтелекту може підвищити ефективність вашої роботи та оптимізувати створення проектів у Laravel

14 Оновлено 26 червня, 2026

Локальні моделі та їх скоупи в Laravel за допомогою атрибута Scope

В Laravel 12 ми отримали можливість використовувати новий підхід для визначення локальних скоупів у моделях Eloquent. Дізнайтеся, як новий атрибут #[Scope] спрощує цей процес і зберігає ваші назви методів незмінними