Frame 66.jpg

В PHP 8.4 добавили ленивые объекты и прокси (RFC , дока).

Давайте пройдёмся по RFC.

Введение

Автор ссылается на то, что отложенная инициализация объектов в PHP уже используется и это важный механизм, например: ленивые сервисы в DI и ленивые связи в ORM.

Сделать ленивость непросто и подобные вещи были реализованы в ocramius/proxy-manager и symfony/var-exporter. Реализация проксей костылями накладывает ограничения и штрафы на производительность.

RFC предлагает внедрить такие ленивые объекты, как Ghost Object и State Proxy в ядро.

<aside> 📌

Начало бодрое!

Когда я пилил Cycle ORM v2, тоже стояла задача сделать прокси-объекты. Я изучил ocramius/proxy-manager, нашёл там кучу багов и пришёл к выводу, что это неюзабельное говно. Я не могу подобрать ни одной причины, почему в доктрине или где либо ещё это использовалось… Может Ocramius был соавтором докрины? 🤔

В итоге мы написали свои прокси, которые даже работают быстрее. Но ограничения те же: нельзя финалить классы сущностей, и какие механизмы PHP ломаются при использовании проксей, типа $entity->relation[] = $item;.

В Spiral DI тоже есть прокси на области видимости. Там мы отстранились от классов и просто завязались на интерфейсы.

</aside>

Реализация

Ленивый объект — обычный объект, который инициализируется потом: при первом обращении к его свойствам или методам. Создаётся через рефлексию с передачей функции инициализации.

Отличия предлагаемых ленивых объектов:

Ghost Object внешне неотличим от обычного объекта: его ID и имя класса — всё выглядит также, как и обычно. Объект просто инициализируется позже, при обращении. При этом в функцию инициализации сразу попадает объект, созданный без вызова конструктора, как если бы был вызван метод рефлексии ReflectionClass::newInstanceWithoutConstructor() , остаётся просто дёрнуть конструктор.

State Proxy — это уже не тот же самый объект, который в итоге лениво создаётся, у них даже ID разный. Задача инициализатора в этом случае — создать и вернуть объект, а прокся будет проксировать на него всё взаимодействие.

Для ленивых объектов есть возможность закинуть некоторые проперти заранее, обращение к которым не будут вызывать инициализацию.

<aside> 📌

Мне пока всё нравится: Ghost Object дают возможность легально отложить инициализацию объекта, а с Proxy можно максимально элегантно и круто завернуть любой класс, в том числе финальный.

Даже в Java нет такой крутотеньки!

</aside>

Frame 64.jpg

Proposal

Далее секция Proposal, в которой можно посмотреть самое интересное: что добавится в рефлексию и как оно будет использовано. Я не буду обозревать нововведения в ReflectionProperty и очевидные методы в ReflectionClass типа isUninitializedLazyObject(): с ними всё плюс-минус понятно при беглом ознакомлении с RFC. Возьму то, что интересно.