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();
// ...
}
}
Hard way
Using type association and intersection with the Lazy class, we can generate proxies and decorators.
Simple way
Make the proxy without magic, just with
get()
method.