-
-
Notifications
You must be signed in to change notification settings - Fork 505
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
Switch proxies to LazyGhostTrait
#2700
Conversation
9bdb39c
to
a81c08c
Compare
LazyGhostTrait
LazyGhostTrait
use function substr; | ||
|
||
/** @internal */ | ||
class LazyGhostProxyClassNameResolver implements ClassNameResolver, ProxyClassNameResolver |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class is copied from Doctrine\ORM\Proxy\DefaultProxyClassNameResolver
.
* @template T of object | ||
* @template-extends Proxy<T> | ||
*/ | ||
interface InternalProxy extends Proxy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This interface is copied from Doctrine\ORM\Proxy\InternalProxy.
$proxyManagerConfiguration = new ProxyManagerConfiguration(); | ||
$proxyManagerConfiguration->setProxiesTargetDir($this->getProxyDir()); | ||
$proxyManagerConfiguration->setProxiesNamespace($this->getProxyNamespace()); | ||
|
||
switch ($this->getAutoGenerateProxyClasses()) { | ||
case self::AUTOGENERATE_FILE_NOT_EXISTS: | ||
$proxyManagerConfiguration->setGeneratorStrategy(new FileWriterGeneratorStrategy( | ||
new FileLocator($proxyManagerConfiguration->getProxiesTargetDir()), | ||
)); | ||
|
||
break; | ||
case self::AUTOGENERATE_EVAL: | ||
$proxyManagerConfiguration->setGeneratorStrategy(new EvaluatingGeneratorStrategy()); | ||
|
||
break; | ||
default: | ||
throw new InvalidArgumentException('Invalid proxy generation strategy given - only AUTOGENERATE_FILE_NOT_EXISTS and AUTOGENERATE_EVAL are supported.'); | ||
} | ||
|
||
return $proxyManagerConfiguration; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creating ProxyManagerConfiguration
on-demand as the class is not required when LazyGhostObjects are used.
* | ||
* @internal | ||
*/ | ||
final class Autoloader |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class is copied from Doctrine\ORM\Proxy\Autoloader.
return static function (InternalProxy $proxy, mixed $identifier) use ($persister, $classMetadata, $factory): void { | ||
$original = $persister->load([$classMetadata->identifier => $identifier], $proxy); | ||
|
||
if (! $original && ! $factory->lifecycleEventManager->documentNotFound($proxy, $identifier)) { | ||
throw DocumentNotFoundException::documentNotFound($classMetadata->getName(), $identifier); | ||
} | ||
|
||
// phpcs:ignore SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed | ||
if ($proxy instanceof NotifyPropertyChanged) { | ||
$proxy->addPropertyChangedListener($factory->uow); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initialization code copied from StaticProxyFactory
.
* | ||
* @template T of object | ||
*/ | ||
public function getProxy(ClassMetadata $metadata, $identifier): GhostObjectInterface; | ||
public function getProxy(ClassMetadata $metadata, $identifier): object; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The interface is modified, but child classes can be more restrictive. This is not a BC break for implementers of the interface, but it can be for users of the interface.
public static function isLazyObject(object $document): bool | ||
{ | ||
return $document instanceof InternalProxy || $document instanceof LazyLoadingInterface; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most test changes are related to check interfaces to assert if the class is a lazy proxy object. I wonder if this method should be in UOW.
Later, it will use ReflectionClass::getLazyInitializer()
to detect native lazy object.
|
||
/** | ||
* Generate proxy classes using Symfony VarExporter's LazyGhostTrait if true. | ||
* Otherwise, use ProxyManager's LazyLoadingGhostFactory (deprecated) | ||
*/ | ||
public function setUseLazyGhostObject(bool $flag): void | ||
{ | ||
if ($flag === false) { | ||
if (! class_exists(ProxyManagerConfiguration::class)) { | ||
throw new LogicException('Package "friendsofphp/proxy-manager-lts" is required to disable LazyGhostObject.'); | ||
} | ||
|
||
trigger_deprecation( | ||
'doctrine/mongodb-odm', | ||
'2.6', | ||
'Using "friendsofphp/proxy-manager-lts" is deprecated. Use "symfony/var-exporter" LazyGhostObjects instead.', | ||
); | ||
} | ||
|
||
$this->useLazyGhostObject = $flag; | ||
} | ||
|
||
public function isLazyGhostObjectEnabled(): bool | ||
{ | ||
return $this->useLazyGhostObject ?? true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use the same method names as the ORM: isLazyGhostObjectEnabled
and setLazyGhostObjectEnabled
?
lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php
Outdated
Show resolved
Hide resolved
Considering the unchecked checkboxes in your initial PR description, is this a WIP? |
631e341
to
cbab97c
Compare
I've just rebased this PR. The bundle will need an update to register the autoloader. Regarding native lazy objects and prop hooks PR on ODM, I think the approaches are not conflicting this this PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some minor comments on this. I think the "BC Break" in the ProxyFactory interface is fine - proxies are an implementation detail and as long as we communicate that we change the default proxy mechanism we'll be fine. People shouldn't rely on a specific proxy behaviour, other than "it behaves as if it was an instance of the original class".
} | ||
|
||
/** @deprecated */ | ||
public function getProxyManagerConfiguration(): ProxyManagerConfiguration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this will return a new configuration instance every time this is called. I don't recall if there were any issues with that approach (e.g. due to unique identifiers in directories), so it's worth checking that creating a separate configuration doesn't cause the proxy manager to look for proxy files in different directories.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated to keep the instance in memory so that it's still possible to customize the configuration of the proxy manager directly. But it is reset each time a configuration is set on the ODM configuration class.
lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php
Outdated
Show resolved
Hide resolved
cbab97c
to
0798779
Compare
0798779
to
4d4c864
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes LGTM. @malarzm do you want to take another look or should we merge this for 2.10?
I just found an incompatibility with DoctrineMongoDBODMBundle, the |
Summary
Use the
LazyGhostTrait
fromsymfony/var-exporter
to generate lazy proxy classes.LazyGhostProxyFactory
that implements theProxyFactory
interface and use it by defaultlazy-ghost-objects