Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error with static return type #760

Closed
mrsuh opened this issue May 14, 2022 · 3 comments
Closed

Error with static return type #760

mrsuh opened this issue May 14, 2022 · 3 comments
Labels

Comments

@mrsuh
Copy link

mrsuh commented May 14, 2022

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?

@mrsuh mrsuh changed the title Error with static error type Error with static return type May 14, 2022
@Ocramius
Copy link
Owner

Ocramius commented May 14, 2022

A couple things:

  1. your HeavyComplexObject (I know it's taken from the examples) should return only itself, when using the static 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 of doFoo() 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 if doFoo() should return the same instance ($this) or a newly instantiated HeavyComplexObject
    • I've described these problems in https://ocramius.github.io/blog/fluent-interfaces-are-evil/ in more detail
  2. 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 be final (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.

@mrsuh
Copy link
Author

mrsuh commented May 14, 2022

Thank you for detailed answer!
Example works with LazyLoadingGhostFactory.
Maybe It will help for symfony/symfony#46350

@mrsuh mrsuh closed this as completed May 14, 2022
@Ocramius
Copy link
Owner

@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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants