From bf4cb39808c39eee17b33744c810478030b8f205 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 12 Mar 2024 15:01:44 +0100 Subject: [PATCH] [VarExporter] Improve performance when creating lazy objects --- .../Internal/LazyObjectRegistry.php | 8 ------- .../VarExporter/Internal/LazyObjectState.php | 24 +++++++++++-------- .../Component/VarExporter/LazyGhostTrait.php | 19 +++++++++++---- .../Component/VarExporter/LazyProxyTrait.php | 18 ++++++++++---- 4 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/Symfony/Component/VarExporter/Internal/LazyObjectRegistry.php b/src/Symfony/Component/VarExporter/Internal/LazyObjectRegistry.php index 323e8cca27fb..f6450ce7f0e7 100644 --- a/src/Symfony/Component/VarExporter/Internal/LazyObjectRegistry.php +++ b/src/Symfony/Component/VarExporter/Internal/LazyObjectRegistry.php @@ -76,14 +76,6 @@ public static function getClassResetters($class) }, null, $scope); } - $resetters[] = static function ($instance, $skippedProperties) { - foreach ((array) $instance as $name => $value) { - if ("\0" !== ($name[0] ?? '') && !\array_key_exists($name, $skippedProperties)) { - unset($instance->$name); - } - } - }; - return $resetters; } diff --git a/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php b/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php index 2ac8459ec741..5fc398e05895 100644 --- a/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php +++ b/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php @@ -27,22 +27,20 @@ class LazyObjectState public const STATUS_INITIALIZED_FULL = 3; public const STATUS_INITIALIZED_PARTIAL = 4; - /** - * @var array - */ - public readonly array $skippedProperties; - /** * @var self::STATUS_* */ - public int $status = 0; + public int $status = self::STATUS_UNINITIALIZED_FULL; public object $realInstance; - public function __construct(public readonly \Closure $initializer, $skippedProperties = []) - { - $this->skippedProperties = $skippedProperties; - $this->status = self::STATUS_UNINITIALIZED_FULL; + /** + * @param array $skippedProperties + */ + public function __construct( + public readonly \Closure $initializer, + public readonly array $skippedProperties = [], + ) { } public function initialize($instance, $propertyName, $propertyScope) @@ -88,6 +86,12 @@ public function reset($instance): void $reset($instance, $skippedProperties); } + foreach ((array) $instance as $name => $value) { + if ("\0" !== ($name[0] ?? '') && !\array_key_exists($name, $skippedProperties)) { + unset($instance->$name); + } + } + $this->status = self::STATUS_UNINITIALIZED_FULL; } } diff --git a/src/Symfony/Component/VarExporter/LazyGhostTrait.php b/src/Symfony/Component/VarExporter/LazyGhostTrait.php index 08d0ed2cf098..f09d42641c04 100644 --- a/src/Symfony/Component/VarExporter/LazyGhostTrait.php +++ b/src/Symfony/Component/VarExporter/LazyGhostTrait.php @@ -35,15 +35,24 @@ public static function createLazyGhost(\Closure $initializer, ?array $skippedPro { if (self::class !== $class = $instance ? $instance::class : static::class) { $skippedProperties["\0".self::class."\0lazyObjectState"] = true; - } elseif (\defined($class.'::LAZY_OBJECT_PROPERTY_SCOPES')) { - Hydrator::$propertyScopes[$class] ??= $class::LAZY_OBJECT_PROPERTY_SCOPES; } - $instance ??= (Registry::$classReflectors[$class] ??= new \ReflectionClass($class))->newInstanceWithoutConstructor(); - Registry::$defaultProperties[$class] ??= (array) $instance; + if (!isset(Registry::$defaultProperties[$class])) { + Registry::$classReflectors[$class] ??= new \ReflectionClass($class); + $instance ??= Registry::$classReflectors[$class]->newInstanceWithoutConstructor(); + Registry::$defaultProperties[$class] ??= (array) $instance; + Registry::$classResetters[$class] ??= Registry::getClassResetters($class); + + if (self::class === $class && \defined($class.'::LAZY_OBJECT_PROPERTY_SCOPES')) { + Hydrator::$propertyScopes[$class] ??= $class::LAZY_OBJECT_PROPERTY_SCOPES; + } + } else { + $instance ??= Registry::$classReflectors[$class]->newInstanceWithoutConstructor(); + } + $instance->lazyObjectState = new LazyObjectState($initializer, $skippedProperties ??= []); - foreach (Registry::$classResetters[$class] ??= Registry::getClassResetters($class) as $reset) { + foreach (Registry::$classResetters[$class] as $reset) { $reset($instance, $skippedProperties); } diff --git a/src/Symfony/Component/VarExporter/LazyProxyTrait.php b/src/Symfony/Component/VarExporter/LazyProxyTrait.php index 4dd435bcda63..d8101679acd1 100644 --- a/src/Symfony/Component/VarExporter/LazyProxyTrait.php +++ b/src/Symfony/Component/VarExporter/LazyProxyTrait.php @@ -31,14 +31,24 @@ public static function createLazyProxy(\Closure $initializer, ?object $instance { if (self::class !== $class = $instance ? $instance::class : static::class) { $skippedProperties = ["\0".self::class."\0lazyObjectState" => true]; - } elseif (\defined($class.'::LAZY_OBJECT_PROPERTY_SCOPES')) { - Hydrator::$propertyScopes[$class] ??= $class::LAZY_OBJECT_PROPERTY_SCOPES; } - $instance ??= (Registry::$classReflectors[$class] ??= new \ReflectionClass($class))->newInstanceWithoutConstructor(); + if (!isset(Registry::$defaultProperties[$class])) { + Registry::$classReflectors[$class] ??= new \ReflectionClass($class); + $instance ??= Registry::$classReflectors[$class]->newInstanceWithoutConstructor(); + Registry::$defaultProperties[$class] ??= (array) $instance; + Registry::$classResetters[$class] ??= Registry::getClassResetters($class); + + if (self::class === $class && \defined($class.'::LAZY_OBJECT_PROPERTY_SCOPES')) { + Hydrator::$propertyScopes[$class] ??= $class::LAZY_OBJECT_PROPERTY_SCOPES; + } + } else { + $instance ??= Registry::$classReflectors[$class]->newInstanceWithoutConstructor(); + } + $instance->lazyObjectState = new LazyObjectState($initializer); - foreach (Registry::$classResetters[$class] ??= Registry::getClassResetters($class) as $reset) { + foreach (Registry::$classResetters[$class] as $reset) { $reset($instance, $skippedProperties ??= []); }