Skip to content

Instantly share code, notes, and snippets.

@andrerom
Last active August 7, 2020 17:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save andrerom/e5ba41baccbf46ab9e6e99a0412d22ee to your computer and use it in GitHub Desktop.
Save andrerom/e5ba41baccbf46ab9e6e99a0412d22ee to your computer and use it in GitHub Desktop.
PHP RFC: Lazy objects
<?php
/**
* Possible idea for PHP RFC (PHP language feature).
*
* Problem:
* In generic PHP applications, like Drupal, Sylius, Magento, eZ, .... It is common that end user
* finds themselves in situation where the system is either loading too much data for them, or not providing
* enough in order to solve the use case they are working on. This can be on a case by case basis even for single
* end user. In other words it very hard for the system to get that balance right for all users/cases.
*
* Similar cases can be mentioned for PHP framworks, being able to defer loading of things like databases or
* other external connections until used has a real impact. In both cases both run time performance & memory usage.
*
* Solution:
* Somehow provide a native way to lazy load objects in similar way Generators(yield) somewhat allows on
* arrays/Collections. This is done by in similar way as async/await (but different concept) in other
* languages, by introducing a new keyword, for instance `lazy`. That instead of returning a full object,
* returns a empty object (empty zval_object) reference in the php engine just with the minimal info
* needed such as class type. While rest of the execution is performed once object is used.
*
* E.g:
* $obj = lazy factory($service);// type hinted to return "Ice" class, this is requirement for using this
* if (!$obj instanceof Ice) return;
* if (!$obj) return;
* if ($obj === null) return;
* if (empty($obj)) return;
* if (!isset($obj)) return;
* if (count($obj) > 2) return;// unless class implements Countable, returns 1 without loading object
*
* Class Cream {
* function __construct(Ice $ice) {
* $this->ice = $ice;
* {
* }
* $cream = new Cream($obj);
*
* $cream->ice;
* $cream->ice->color;// <-- This is where Ice object is fully created.
*
*
* Main reason against this:
* It's possible to do this in user land using proxy objects, however they:
* - etiher forces you to implement alternative classes for each and every interface you want to lazy load and use those,
* or use a solution such as Proxy Manager which relies on having to compile proxy classes for you.
* - In both those cases you end up with call time overhead on usage of the proxy object
*/
/**
* Example: Heavy objects
*
* This is mainly shown as an example to show the concept, what
* will actuall benefit users is the second form on functions(factories).
*/
$ice = lazy Ice($service);
if ($ice instanceof Ice) {
// True, lazy object go by their type even if not initialized yet
}
// Object is initialized on any kind of property or method invocation
$flavor = $ice->flavor;
/**
* Example: Heavy Factories
*/
function factory($service) : Ice {
// lots of heavy duty stuff here, before returning Ice
}
// Can only be done if function has a class as return type hint
// !! $service can potentially be lazy here also
$ice = lazy factory($service);
if ($ice instanceof Ice) {
// True, lazy object go by their type even if not initialized yet
}
// Object is initialized on any kind of property or method invocation
// effectively this works the same way as yield / Generators
$flavor = $ice->flavor;
// Somewhat relevant links:
// - https://docs.microsoft.com/en-us/dotnet/framework/performance/lazy-initialization
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment