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

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

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

Ліниві обʼєкти · PHP Manual

Лінивий обʼєкт (lazy object) - це обʼєкт, ініціалізацію якого відкладено до моменту, коли його стан читають або змінюють. Приклади застосування: компоненти впровадження залежностей, які повністю ініціалізують ліниві сервіси лише за потреби, ORM, що віддають ліниві сутності й гідратують їх із бази тільки при зверненні, або JSON-парсер, який відкладає розбір до звернення до елементів.

Підтримано дві стратегії лінивих обʼєктів: Ghost Objects і Virtual Proxies, далі «ліниві привиди» (lazy ghosts) і «ліниві проксі» (lazy proxies). В обох стратегіях лінивий обʼєкт привʼязано до ініціалізатора або фабрики, яку викликають автоматично, коли його стан уперше читають або змінюють. З точки зору абстракції ліниві привиди не відрізняються від нелінивих обʼєктів: ними можна користуватися, не знаючи про лінивість, тож їх можна передавати в код, який про лінивість не здогадується. Ліниві проксі так само прозорі, але треба бути обережним, коли використовують їхню ідентичність, бо проксі й реальний екземпляр мають різні ідентичності.

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

Ліниві обʼєкти зʼявилися в PHP 8.4.

Створення лінивих обʼєктів

Лінивий екземпляр можна створити для будь-якого користувацького класу або класу stdClass (інші внутрішні класи не підтримано), а також скинути наявний екземпляр таких класів, зробивши його лінивим. Точки входу для створення лінивого обʼєкта - методи ReflectionClass::newLazyGhost і ReflectionClass::newLazyProxy.

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

Приклад: створення лінивого привида

<?php
class Example
{
    public function __construct(public int $prop)
    {
        echo __METHOD__, "\n";
    }
}

$reflector = new ReflectionClass(Example::class);
$lazyObject = $reflector->newLazyGhost(function (Example $object) {
    // Ініціалізуємо обʼєкт на місці
    $object->__construct(1);
});

var_dump($lazyObject);
var_dump(get_class($lazyObject));

// Запускає ініціалізацію
var_dump($lazyObject->prop);
?>

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

lazy ghost object(Example)#3 (0) {
  ["prop"]=>
  uninitialized(int)
}
string(7) "Example"
Example::__construct
int(1)

Приклад: створення лінивого проксі

<?php
class Example
{
    public function __construct(public int $prop)
    {
        echo __METHOD__, "\n";
    }
}

$reflector = new ReflectionClass(Example::class);
$lazyObject = $reflector->newLazyProxy(function (Example $object) {
    // Створюємо й повертаємо реальний екземпляр
    return new Example(1);
});

var_dump($lazyObject);
var_dump(get_class($lazyObject));

// Запускає ініціалізацію
var_dump($lazyObject->prop);
?>

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

lazy proxy object(Example)#3 (0) {
  ["prop"]=>
  uninitialized(int)
}
string(7) "Example"
Example::__construct
int(1)

Будь-яке звернення до властивостей лінивого обʼєкта запускає його ініціалізацію (зокрема через ReflectionProperty). Проте деякі властивості можуть бути відомі наперед, і звернення до них не повинно запускати ініціалізацію:

Приклад: жадібна ініціалізація властивостей

<?php
class BlogPost
{
    public function __construct(
        public int $id,
        public string $title,
        public string $content,
    ) { }
}

$reflector = new ReflectionClass(BlogPost::class);

$post = $reflector->newLazyGhost(function ($post) {
    $data = fetch_from_store($post->id);
    $post->__construct($data['id'], $data['title'], $data['content']);
});

// Без цього рядка наступний виклик ReflectionProperty::setValue()
// запустив би ініціалізацію.
$reflector->getProperty('id')->skipLazyInitialization($post);
$reflector->getProperty('id')->setValue($post, 123);

// Як варіант, можна одразу скористатися цим:
$reflector->getProperty('id')->setRawValueWithoutLazyInitialization($post, 123);

// До властивості id можна звертатися, не запускаючи ініціалізацію
var_dump($post->id);
?>

Методи ReflectionProperty::skipLazyInitialization і ReflectionProperty::setRawValueWithoutLazyInitialization дають способи обійти ліниву ініціалізацію при зверненні до властивості.

Про стратегії лінивих обʼєктів

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

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

Життєвий цикл лінивих обʼєктів

Обʼєкти можна зробити лінивими під час створення за допомогою ReflectionClass::newLazyGhost чи ReflectionClass::newLazyProxy або вже після створення через ReflectionClass::resetAsLazyGhost чи ReflectionClass::resetAsLazyProxy. Після цього лінивий обʼєкт може стати ініціалізованим унаслідок однієї з таких операцій:

  • Взаємодія з обʼєктом у спосіб, що запускає автоматичну ініціалізацію. Див. Тригери ініціалізації.
  • Позначення всіх його властивостей як нелінивих через ReflectionProperty::skipLazyInitialization або ReflectionProperty::setRawValueWithoutLazyInitialization.
  • Явний виклик ReflectionClass::initializeLazyObject або ReflectionClass::markLazyObjectAsInitialized.

Оскільки ліниві обʼєкти стають ініціалізованими, коли всі їхні властивості позначено як неліниві, наведені вище методи не позначать обʼєкт як лінивий, якщо жодної властивості не вдалося позначити лінивою.

Тригери ініціалізації

Ліниві обʼєкти спроєктовано повністю прозорими для тих, хто їх використовує, тому звичайні операції, які читають або змінюють стан обʼєкта, автоматично запускають ініціалізацію перед виконанням самої операції. Сюди належать, зокрема, такі операції:

  • Читання або запис властивості.
  • Перевірка, чи властивість встановлено, та її видалення через unset.
  • Доступ до властивості або її зміна через ReflectionProperty::getValue, ReflectionProperty::getRawValue, ReflectionProperty::setValue чи ReflectionProperty::setRawValue.
  • Перелічення властивостей через ReflectionObject::getProperties, ReflectionObject::getProperty, get_object_vars().
  • Ітерація по властивостях обʼєкта, який не реалізує Iterator чи IteratorAggregate, за допомогою foreach.
  • Серіалізація обʼєкта через serialize(), json_encode() тощо.
  • Клонування обʼєкта.

Виклики методів, які не звертаються до стану обʼєкта, ініціалізацію не запускають. Так само взаємодія з обʼєктом, що викликає магічні методи або hook-функції, не запускає ініціалізацію, якщо ці методи чи функції не звертаються до стану обʼєкта.

Операції, які не запускають ініціалізацію

Наведені нижче методи або низькорівневі операції дають доступ до лінивих обʼєктів або дозволяють змінювати їх, не запускаючи ініціалізацію:

  • Позначення властивостей як нелінивих через ReflectionProperty::skipLazyInitialization або ReflectionProperty::setRawValueWithoutLazyInitialization.
  • Отримання внутрішнього представлення властивостей через get_mangled_object_vars() або приведенням обʼєкта до масиву.
  • Використання serialize(), коли встановлено ReflectionClass::SKIP_INITIALIZATION_ON_SERIALIZE, якщо тільки __serialize() або __sleep() не запускають ініціалізацію.
  • Виклик ReflectionObject::__toString.
  • Використання var_dump() або debug_zval_dump(), якщо тільки __debugInfo() не запускає ініціалізацію.

Послідовність ініціалізації

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

Ghost Objects

  • Обʼєкт позначається як нелінивий.
  • Властивості, які не були ініціалізовані через ReflectionProperty::skipLazyInitialization або ReflectionProperty::setRawValueWithoutLazyInitialization, отримують свої значення за замовчуванням, якщо такі є. На цьому етапі обʼєкт схожий на створений через ReflectionClass::newInstanceWithoutConstructor, за винятком уже ініціалізованих властивостей.
  • Далі викликається функція-ініціалізатор, яка першим параметром отримує обʼєкт. Від функції очікують (але не вимагають), що вона ініціалізує стан обʼєкта; вона має повертати null або не повертати значення взагалі. На цьому етапі обʼєкт уже не лінивий, тож функція може звертатися до його властивостей напряму.

Після ініціалізації обʼєкт не відрізняється від обʼєкта, який ніколи не був лінивим.

Proxy Objects

  • Обʼєкт позначається як нелінивий.
  • На відміну від ghost-обʼєктів, властивості обʼєкта на цьому етапі не змінюються.
  • Функція-фабрика викликається з обʼєктом як першим параметром і має повернути неліниий екземпляр сумісного класу (див. ReflectionClass::newLazyProxy).
  • Повернутий екземпляр називають реальним екземпляром, і його привʼязують до проксі.
  • Значення властивостей проксі відкидаються, ніби до них застосували unset().

Після ініціалізації звернення до будь-якої властивості проксі дає той самий результат, що й звернення до відповідної властивості реального екземпляра; усі звернення до властивостей проксі перенаправляються до реального екземпляра, зокрема до оголошених, динамічних, неіснуючих властивостей і тих, які позначено через ReflectionProperty::skipLazyInitialization чи ReflectionProperty::setRawValueWithoutLazyInitialization.

Сам обʼєкт-проксі не замінюється реальним екземпляром і не підставляється замість нього.

Хоча фабрика отримує проксі першим параметром, від неї не очікують змін у ньому (зміни дозволені, але будуть втрачені на завершальному кроці ініціалізації). Проте проксі можна використати для рішень на основі значень ініціалізованих властивостей, класу, самого обʼєкта чи його ідентичності. Наприклад, ініціалізатор може взяти значення ініціалізованої властивості під час створення реального екземпляра.

Спільна поведінка

Область видимості та контекст $this функції-ініціалізатора чи фабрики залишаються незмінними, і діють звичайні обмеження видимості.

Після успішної ініціалізації обʼєкт більше не тримає посилання на ініціалізатор чи фабрику, і її можна звільнити, якщо інших посилань немає.

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

Клонування

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

Для проксі-обʼєктів клонуються і проксі, і його реальний екземпляр, а повертається клон проксі. Метод __clone викликається на реальному екземплярі, а не на проксі. Клон проксі та клон реального екземпляра звʼязані так само, як під час ініціалізації, тому звернення до клона проксі перенаправляються до клона реального екземпляра.

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

Деструктори

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

Методи ReflectionClass::resetAsLazyGhost і ReflectionClass::resetAsLazyProxy можуть викликати деструктор обʼєкта, який скидають.

Стан перекладу

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

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