Skip to content

Commit

Permalink
minor #54253 [VarExporter] Improve performance when creating lazy obj…
Browse files Browse the repository at this point in the history
…ects (nicolas-grekas)

This PR was merged into the 7.1 branch.

Discussion
----------

[VarExporter] Improve performance when creating lazy objects

| Q             | A
| ------------- | ---
| Branch?       | 7.1
| Bug fix?      | no
| New feature?  | no
| Deprecations? | no
| Issues        | -
| License       | MIT

Creating lazy objects can happen in a tight loop, and then, this has measurable benefits.
Found while working on doctrine/orm#11087

Commits
-------

bf4cb39 [VarExporter] Improve performance when creating lazy objects
  • Loading branch information
nicolas-grekas committed Mar 13, 2024
2 parents 3fdc2c9 + bf4cb39 commit a0721fd
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 27 deletions.
Expand Up @@ -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;
}

Expand Down
24 changes: 14 additions & 10 deletions src/Symfony/Component/VarExporter/Internal/LazyObjectState.php
Expand Up @@ -27,22 +27,20 @@ class LazyObjectState
public const STATUS_INITIALIZED_FULL = 3;
public const STATUS_INITIALIZED_PARTIAL = 4;

/**
* @var array<string, true>
*/
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<string, true> $skippedProperties
*/
public function __construct(
public readonly \Closure $initializer,
public readonly array $skippedProperties = [],
) {
}

public function initialize($instance, $propertyName, $propertyScope)
Expand Down Expand Up @@ -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;
}
}
19 changes: 14 additions & 5 deletions src/Symfony/Component/VarExporter/LazyGhostTrait.php
Expand Up @@ -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);
}

Expand Down
18 changes: 14 additions & 4 deletions src/Symfony/Component/VarExporter/LazyProxyTrait.php
Expand Up @@ -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 ??= []);
}

Expand Down

0 comments on commit a0721fd

Please sign in to comment.