spiral / framework

High-Performance PHP Framework
https://spiral.dev
MIT License
1.81k stars 88 forks source link

Autowiring with proxy #650

Open roxblnfk opened 2 years ago

roxblnfk commented 2 years ago

Hard way

Using type association and intersection with the Lazy class, we can generate proxies and decorators.

class Service {
    public function __construct(
        Lazy|OrmInterface $proxy, // magic proxy
        Lazy&OrmInterface $decorator,
    ) {
        \assert($proxy instanceof Lazy);
        \assert($proxy::class === Lazy::class || $proxy::class === Proxy::class);
        \assert(!$proxy instanceof OrmInterface);

        \assert($decorator::class === 'AutoGeneratedDecoratorClassName');
        \assert($decorator instanceof Lazy);
        \assert($decorator instanceof OrmInterface);

        $proxy->getSchema();
        $decorator->getSchema();

        # Problems
        ## There are the same problems with properties like in Cycle ORM proxy entities
            $proxy->list[] = 'foo'; // error if proxy is not resolved
            $foo = &$proxy->list;   // error
        ## Decorator will have very difficult code
        ## Decorator is non-compatible with final classes and methods
    }
}

Simple way

Make the proxy without magic, just with get() method.

/**
 * @template TDefinition
 */
class Proxy {
    /**
     * @return TDefinition
     */
    public function get(): mixed;
}
class Service {
    /**
     * @var Proxy<OrmInterface> Use template for autocompletion
     */
    private Proxy $orm;

    public function __construct(
        Proxy|OrmInterface $proxy,
    ) {
        $this->orm = $proxy; // Mb psalm will throw some warning here
    }

    public function doSomeAction() {
        $this->orm->get() // Resolve ORM
            ->getSchema();
        // ...
    }
}
wolfy-j commented 2 years ago

Love it. We can also use attributes.