Last active
August 7, 2020 17:19
-
-
Save andrerom/e5ba41baccbf46ab9e6e99a0412d22ee to your computer and use it in GitHub Desktop.
PHP RFC: Lazy objects
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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