-
-
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
base: 2.10.x
Are you sure you want to change the base?
Conversation
6a946e0
to
9bdb39c
Compare
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 LaryGhostObjects 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
?
switch ($this->autoGenerate) { | ||
case Configuration::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED: | ||
if (file_exists($fileName) && filemtime($fileName) >= filemtime($class->getReflectionClass()->getFileName())) { | ||
break; | ||
} | ||
// no break | ||
case Configuration::AUTOGENERATE_FILE_NOT_EXISTS: | ||
if (file_exists($fileName)) { | ||
break; | ||
} | ||
// no break | ||
case Configuration::AUTOGENERATE_ALWAYS: | ||
$this->generateProxyClass($class, $fileName, $proxyClassName); | ||
break; | ||
} |
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.
It would be more intuitive this way:
switch ($this->autoGenerate) { | |
case Configuration::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED: | |
if (file_exists($fileName) && filemtime($fileName) >= filemtime($class->getReflectionClass()->getFileName())) { | |
break; | |
} | |
// no break | |
case Configuration::AUTOGENERATE_FILE_NOT_EXISTS: | |
if (file_exists($fileName)) { | |
break; | |
} | |
// no break | |
case Configuration::AUTOGENERATE_ALWAYS: | |
$this->generateProxyClass($class, $fileName, $proxyClassName); | |
break; | |
} | |
if ( | |
match ($this->autoGenerate) { | |
Configuration::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED => ! file_exists($fileName) || filemtime($fileName) < filemtime($class->getReflectionClass()->getFileName()), | |
Configuration::AUTOGENERATE_FILE_NOT_EXISTS => ! file_exists($fileName), | |
Configuration::AUTOGENERATE_ALWAYS => true, | |
Configuration::AUTOGENERATE_NEVER => false, | |
} | |
) { | |
$this->generateProxyClass($class, $fileName, $proxyClassName); | |
} |
|
||
trigger_deprecation( | ||
'doctrine/mongodb-odm', | ||
'2.6', |
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.
'2.6', | |
'2.10', |
Summary
Use the
LazyGhostTrait
fromsymfony/var-exporter
to generate lazy proxy classes.LazyGhostProxyFactory
that implements theProxyFactory
interface and use it by defaultlazy-ghost-objects