<? phpukraine ДОКУМЕНТАЦІЯ
⌕Пошук по платформі
Документація українською

Переклад офіційної документації українською. Кожен розділ показує стан готовності: недоперекладене позначене відкрито, а не приховане.

НОВЕ У 8.x · ПЕРЕКЛАДЕНО оновлено 18 вересня 2026

Property hooks · PHP Manual

Property hooks, які в деяких інших мовах звуться "property accessors", дозволяють перехоплювати й перевизначати поведінку читання та запису властивості. Ця можливість має дві цілі:

  1. Вона дозволяє працювати з властивостями напряму, без get- і set-методів, залишаючи відкритою можливість додати додаткову поведінку в майбутньому. Це робить більшість шаблонних get/set-методів непотрібними, навіть якщо хуки не використовуються.
  2. Вона дозволяє мати властивості, які описують обʼєкт, але не зберігають значення напряму.

Для нестатичних властивостей доступні два хуки: get і set. Вони перевизначають відповідно поведінку читання і запису властивості. Хуки працюють і з типізованими, і з нетипізованими властивостями.

Властивість може бути "backed" або віртуальною. Backed-властивість справді зберігає значення. Будь-яка властивість без хуків є backed. Віртуальна властивість має хуки, і ці хуки не звертаються до самої властивості. У цьому випадку хуки фактично рівнозначні методам, а обʼєкт не витрачає памʼять на зберігання значення цієї властивості.

Property hooks несумісні з readonly-властивостями. Якщо потрібно обмежити доступ до операції get чи set на додачу до зміни її поведінки, використовуйте асиметричну видимість властивостей.

Інформація про версії

Property hooks зʼявилися в PHP 8.4.

Базовий синтаксис хуків

Загальний синтаксис оголошення хука такий.

Приклад: property hooks (повна форма)

<?php
class Example
{
    private bool $modified = false;

    public string $foo = 'default value' {
        get {
            if ($this->modified) {
                return $this->foo . ' (modified)';
            }
            return $this->foo;
        }
        set(string $value) {
            $this->foo = strtolower($value);
            $this->modified = true;
        }
    }
}

$example = new Example();
$example->foo = 'changed';
print $example->foo;
?>

Властивість $foo завершується {}, а не крапкою з комою. Це вказує на наявність хуків. Тут визначені обидва хуки, get і set, хоча дозволено визначити лише один із них. Обидва хуки мають тіло, позначене {}, яке може містити довільний код.

Хук set додатково дозволяє вказати тип і назву вхідного значення, синтаксисом як у методу. Тип має або збігатися з типом властивості, або бути контраваріантним (ширшим) щодо нього. Наприклад, властивість типу string може мати хук set, який приймає string|Stringable, але не такий, що приймає лише array.

Принаймні один із хуків звертається до $this->foo, тобто до самої властивості. Отже, властивість буде backed. Під час виклику $example->foo = 'changed' переданий рядок спершу переводиться в нижній регістр, а потім зберігається в backing-значення. Під час читання властивості до раніше збереженого значення за умовою може додаватися додатковий текст.

Для типових випадків існує кілька скорочених варіантів синтаксису.

Якщо хук get складається з єдиного виразу, {} можна опустити й замінити стрілковим виразом.

Приклад: вираз get для властивості

Цей приклад рівнозначний попередньому.

<?php
class Example
{
    private bool $modified = false;

    public string $foo = 'default value' {
        get => $this->foo . ($this->modified ? ' (modified)' : '');

        set(string $value) {
            $this->foo = strtolower($value);
            $this->modified = true;
        }
    }
}
?>

Якщо тип параметра хука set збігається з типом властивості (а так буває зазвичай), його можна опустити. Тоді значення для запису автоматично отримує назву $value.

Приклад: значення за замовчуванням для set

Цей приклад рівнозначний попередньому.

<?php
class Example
{
    private bool $modified = false;

    public string $foo = 'default value' {
        get => $this->foo . ($this->modified ? ' (modified)' : '');

        set {
            $this->foo = strtolower($value);
            $this->modified = true;
        }
    }
}
?>

Якщо хук set лише записує змінену версію переданого значення, його теж можна спростити до стрілкового виразу. Значення, до якого обчислюється вираз, буде записане в backing-значення.

Приклад: вираз set для властивості

<?php
class Example
{
    private bool $modified = false;

    public string $foo = 'default value' {
        get => $this->foo . ($this->modified ? ' (modified)' : '');
        set => strtolower($value);
    }
}
?>

Цей приклад не зовсім рівнозначний попередньому, бо він не змінює ще й $this->modified. Якщо в тілі хука set потрібно кілька інструкцій, використовуйте варіант із фігурними дужками.

Властивість може реалізувати нуль, один або обидва хуки, залежно від ситуації. Усі скорочені форми незалежні одна від одної. Тобто короткий get разом із довгим set, короткий set із явним типом тощо - усе це коректно.

Для backed-властивості пропущений хук get чи set означає, що буде використана стандартна поведінка читання або запису.

Хуки можна визначати при використанні просування властивостей у конструкторі. Проте в такому разі значення, передані в конструктор, мають відповідати типу, повʼязаному з властивістю, незалежно від того, що дозволяє хук set.

Розгляньте такий приклад:

<?php
class Example
{
    public function __construct(
        public private(set) DateTimeInterface $created {
            set (string|DateTimeInterface $value) {
                if (is_string($value)) {
                    $value = new DateTimeImmutable($value);
                }
                $this->created = $value;
            }
        },
    ) {
    }
}

Усередині рушій розкладає це на таке:

<?php
class Example
{
    public private(set) DateTimeInterface $created {
        set (string|DateTimeInterface $value) {
            if (is_string($value)) {
                $value = new DateTimeImmutable($value);
            }
            $this->created = $value;
        }
    }

    public function __construct(
        DateTimeInterface $created,
    ) {
        $this->created = $created;
    }
}

Будь-які спроби записати властивість поза конструктором прийматимуть значення типу string або DateTimeInterface, але конструктор дозволить лише DateTimeInterface. Так відбувається тому, що визначений тип властивості (DateTimeInterface) використовується як тип параметра в сигнатурі конструктора, незалежно від того, що дозволяє хук set.

Якщо така поведінка потрібна від конструктора, просування властивостей у конструкторі використати не вийде.

Віртуальні властивості

Віртуальні властивості - це властивості без backing-значення. Властивість є віртуальною, якщо ні її хук get, ні хук set не звертаються до самої властивості точним синтаксисом. Тобто властивість з назвою $foo, хук якої містить $this->foo, буде backed. А ось наступний приклад не є backed-властивістю і призведе до помилки:

Приклад: некоректна віртуальна властивість

<?php
class Example
{
    public string $foo {
        get {
            $temp = __PROPERTY__;
            return $this->$temp; // Не звертається до $this->foo, тому не рахується.
        }
    }
}
?>

Для віртуальних властивостей пропущений хук означає, що відповідної операції не існує, і спроба її виконати призведе до помилки. Віртуальні властивості не займають памʼяті в обʼєкті. Вони добре підходять для "похідних" властивостей, наприклад тих, що є комбінацією двох інших.

Приклад: віртуальна властивість

<?php
class Rectangle
{
    // Віртуальна властивість.
    public int $area {
        get => $this->h * $this->w;
    }

    public function __construct(public int $h, public int $w) {}
}

$s = new Rectangle(4, 5);
print $s->area; // виводить 20
$s->area = 30; // Помилка, бо операція set не визначена.
?>

Визначати для віртуальної властивості обидва хуки, get і set, теж дозволено.

Область видимості

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

Найпомітніший наслідок цього: нетривіальні хуки можуть за потреби викликати як завгодно складний метод.

Приклад: виклик методу з хука

<?php
class Person {
    public string $phone {
        set => $this->sanitizePhone($value);
    }

    private function sanitizePhone(string $value): string {
        $value = ltrim($value, '+');
        $value = ltrim($value, '1');

        if (!preg_match('/\d\d\d\-\d\d\d\-\d\d\d\d/', $value)) {
            throw new \InvalidArgumentException();
        }
        return $value;
    }
}
?>

Посилання

Оскільки наявність хуків перехоплює процес читання й запису властивостей, вони спричиняють проблеми при отриманні посилання на властивість або при непрямій модифікації на кшталт $this->arrayProp['key'] = 'value';. Причина в тому, що будь-яка спроба змінити значення за посиланням обходила б хук set, якщо він визначений.

У рідкісних випадках, коли отримати посилання на властивість із хуками справді необхідно, хук get можна позначити префіксом &, щоб він повертав значення за посиланням. Визначення і get, і &get для однієї властивості є синтаксичною помилкою.

Визначати хуки &get і set одночасно для backed-властивості заборонено. Як зазначено вище, запис у повернене за посиланням значення обійшов би хук set. У віртуальних властивостей спільного значення між цими двома хуками немає, тому визначати обидва дозволено.

Запис за індексом властивості-масиву теж передбачає неявне посилання. Тому запис у backed-властивість типу масив із визначеними хуками дозволений тоді й лише тоді, коли визначено єдиний хук &get. У віртуальної властивості запис у масив, повернений із get або &get, є допустимим, але чи вплине він на обʼєкт, залежить від реалізації хука.

Перезапис властивості-масиву цілком працює нормально й поводиться так само, як і для будь-якої іншої властивості. Особливої обережності вимагає лише робота з елементами масиву.

Успадкування

Final-хуки

Хуки можна оголосити final, і тоді їх не можна перевизначити.

Приклад: final-хуки

<?php
class User
{
    public string $username {
        final set => strtolower($value);
    }
}

class Manager extends User
{
    public string $username {
        // Так можна
        get => strtoupper($this->username);

        // А так НЕ можна, бо set у батьківському класі оголошений final.
        set => strtoupper($value);
    }
}
?>

Властивість теж можна оголосити final. Final-властивість дочірній клас не може перевизначити жодним чином, що виключає і зміну хуків, і розширення доступу до неї.

Оголошувати хуки final для властивості, яка вже оголошена final, надлишково, і це буде мовчки проігноровано. Так само поводяться final-методи.

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

Приклад: успадкування хуків

<?php
class Point
{
    public int $x;
    public int $y;
}

class PositivePoint extends Point
{
    public int $x {
        set {
            if ($value < 0) {
                throw new \InvalidArgumentException('Too small');
            }
            $this->x = $value;
        }
    }
}
?>

Кожен хук перевизначає батьківські реалізації незалежно від інших. Якщо дочірній клас додає хуки, будь-яке значення за замовчуванням, задане для властивості, зникає, і його треба оголосити повторно. Це узгоджується з тим, як успадкування працює для властивостей без хуків.

Доступ до батьківських хуків

Хук у дочірньому класі може звернутися до властивості батьківського класу через ключове слово parent::$prop, за яким іде потрібний хук. Наприклад, parent::$propName::get(). Це читається як "звернутися до властивості prop, визначеної в батьківському класі, і виконати її операцію get" (або set, відповідно).

Якщо звертатися інакше, хук батьківського класу ігнорується. Така поведінка узгоджується з роботою всіх методів. Це також дає спосіб дістатися до сховища батьківського класу, якщо воно є. Якщо у батьківської властивості хука немає, буде використана її стандартна поведінка get/set. Хуки не можуть звертатися до жодного іншого хука, окрім свого батьківського на своїй же властивості.

Наведений вище приклад можна переписати так, і тоді клас Point зможе в майбутньому додати власний хук set без проблем (у попередньому прикладі доданий у батьківському класі хук був би проігнорований у дочірньому).

Приклад: доступ до батьківського хука (set)

<?php
class Point
{
    public int $x;
    public int $y;
}

class PositivePoint extends Point
{
    public int $x {
        set {
            if ($value < 0) {
                throw new \InvalidArgumentException('Too small');
            }
            parent::$x::set($value);
        }
    }
}
?>

Приклад перевизначення лише хука get:

Приклад: доступ до батьківського хука (get)

<?php
class Strings
{
    public string $val;
}

class CaseFoldingStrings extends Strings
{
    public bool $uppercase = true;

    public string $val {
        get => $this->uppercase
            ? strtoupper(parent::$val::get())
            : strtolower(parent::$val::get());
    }
}
?>

Серіалізація

У PHP є кілька різних способів серіалізувати обʼєкт - для зовнішнього споживання або для налагодження. Поведінка хуків залежить від конкретного випадку. Подекуди береться сире backing-значення властивості в обхід усіх хуків. В інших випадках властивість читається або записується "через" хук, як при будь-якій звичайній операції читання/запису.

  • var_dump(): сире значення
  • serialize(): сире значення
  • unserialize(): сире значення
  • __serialize()/__unserialize(): власна логіка, використовує хук get/set
  • Приведення до масиву: сире значення
  • var_export(): хук get
  • json_encode(): хук get
  • JsonSerializable: власна логіка, використовує хук get
  • get_object_vars(): хук get
  • get_mangled_object_vars(): сире значення
Стан перекладу

Перекладаємо з офіційної документації, розділ за розділом, і не ховаємо недоперекладене. Помітили неточність у терміні чи реченні: напишіть, виправимо.

48%
Готовності
1
У роботі
15
Ще не перекладено
Глосарій термінів