Error with static return type
mrsuh opened this issue · 3 comments
Hi!
I didn't find issues with my problem and decide to ask you about error with : static
return type.
How to reproduce
php -v
PHP 8.0.0 (cli) (built: Dec 18 2020 21:07:48) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.0-dev, Copyright (c) Zend Technologies
composer info ocramius/proxy-manager
name : ocramius/proxy-manager
descrip. : A library providing utilities to generate, instantiate and generally operate with Object Proxies
keywords : aop, lazy loading, proxy, proxy pattern, service proxies
versions : * 2.13.1
<?php
require_once __DIR__ . '/../vendor/autoload.php';
$factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory();
class HeavyComplexObject
{
public function doFoo(): static
{
return $this;
}
}
$proxy = $factory->createProxy(
HeavyComplexObject::class,
function (&$wrappedObject, $proxy, $method, $parameters, &$initializer) {
$wrappedObject = new HeavyComplexObject(); // instantiation logic here
$initializer = null; // turning off further lazy initialization
return true; // report success
}
);
$proxy->doFoo();
Output
Fatal error: Uncaught TypeError: ProxyManagerGeneratedProxy\__PM__\HeavyComplexObject\Generatedfda300b1d931030ebbdfaf2b78627d82::doFoo(): Return value must be of type ProxyManagerGeneratedProxy\__PM__\HeavyComplexObject\Generatedfda300b1d931030ebbdfaf2b78627d82, HeavyComplexObject returned in /app/vendor/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/EvaluatingGeneratorStrategy.php(54) : eval()'d code:28
Stack trace:
#0 /app/bin/test.php(25): ProxyManagerGeneratedProxy\__PM__\HeavyComplexObject\Generatedfda300b1d931030ebbdfaf2b78627d82->doFoo()
#1 {main}
thrown in /app/vendor/ocramius/proxy-manager/src/ProxyManager/GeneratorStrategy/EvaluatingGeneratorStrategy.php(54) : eval()'d code on line 28
Any ideas how to avoid this error?
A couple things:
- your
HeavyComplexObject
(I know it's taken from the examples) should return only itself, when using thestatic
signature: decoration of methods returning: static
has always been problematic because of that, and is not really fixable here:- declaring
HeavyComplexObject
in the proxy return type ofdoFoo()
would break the type signature (compile error) - returning the proxy itself would break the behavior of
doFoo()
, swapping out the object at runtime, although we don't know ifdoFoo()
should return the same instance ($this
) or a newly instantiatedHeavyComplexObject
- I've described these problems in https://ocramius.github.io/blog/fluent-interfaces-are-evil/ in more detail
- declaring
- for working around most of these problems on fluent interfaces, see https://github.com/Ocramius/ProxyManager/blob/8846623bea8749ded206db3ce701e4e508d516db/docs/lazy-loading-ghost-object.md
- it is more complex to work with
- it will preserve the identity of your object, as there is no value holder being wrapped by a proxy, but just the state of the proxy itself
- it will still break in a declaration like
public function doFoo(): static { return new static(); }
, since the constructor would need to befinal
(or interfaced) to work reliably
This cannot be fixed in the library, as there's no declaration (in PHP) that tells us if the same instance, or a new instance, will be returned by doFoo()
, so doing this in an automated way is not feasible, and even if we wanted to go into analyzing the AST, we could still have scenarios where this happens conditionally.
My recommendation is to try with ghost objects, linked above.
Thank you for detailed answer!
Example works with LazyLoadingGhostFactory
.
Maybe It will help for symfony/symfony#46350
@mrsuh switching proxy type is feasible within the symfony/dependency-injection
system, but is going to be a blood-bath to implement :-)
That would certainly be my recommendation though: I designed ghost objects much after I've designed the initial value-holder design, which got adopted by laminas/laminas-servicemanager
, php-di/php-di
and symfony/dependency-injection
.
Now the path would be to migrate to the newer/better proxy type, but at the cost of added complexity and potential BC issues (the interface on the proxies changes)