Skip to content

Commit

Permalink
Leverage native lazy proxies
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed Nov 16, 2023
1 parent 18fa44a commit 247b453
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 876 deletions.
124 changes: 1 addition & 123 deletions src/Symfony/Component/VarExporter/Internal/LazyObjectRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,127 +20,5 @@
*/
class LazyObjectRegistry
{
/**
* @var array<class-string, \ReflectionClass>
*/
public static array $classReflectors = [];

/**
* @var array<class-string, array<string, mixed>>
*/
public static array $defaultProperties = [];

/**
* @var array<class-string, list<\Closure>>
*/
public static array $classResetters = [];

/**
* @var array<class-string, array{get: \Closure, set: \Closure, isset: \Closure, unset: \Closure}>
*/
public static array $classAccessors = [];

/**
* @var array<class-string, array{set: bool, isset: bool, unset: bool, clone: bool, serialize: bool, unserialize: bool, sleep: bool, wakeup: bool, destruct: bool, get: int}>
*/
public static array $parentMethods = [];

public static ?\Closure $noInitializerState = null;

public static function getClassResetters($class)
{
$classProperties = [];

if ((self::$classReflectors[$class] ??= new \ReflectionClass($class))->isInternal()) {
$propertyScopes = [];
} else {
$propertyScopes = Hydrator::$propertyScopes[$class] ??= Hydrator::getPropertyScopes($class);
}

foreach ($propertyScopes as $key => [$scope, $name, $readonlyScope]) {
$propertyScopes[$k = "\0$scope\0$name"] ?? $propertyScopes[$k = "\0*\0$name"] ?? $k = $name;

if ($k === $key && "\0$class\0lazyObjectState" !== $k) {
$classProperties[$readonlyScope ?? $scope][$name] = $key;
}
}

$resetters = [];
foreach ($classProperties as $scope => $properties) {
$resetters[] = \Closure::bind(static function ($instance, $skippedProperties) use ($properties) {
foreach ($properties as $name => $key) {
if (!\array_key_exists($key, $skippedProperties)) {
unset($instance->$name);
}
}
}, 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;
}

public static function getClassAccessors($class)
{
return \Closure::bind(static fn () => [
'get' => static function &($instance, $name, $readonly) {
if (!$readonly) {
return $instance->$name;
}
$value = $instance->$name;

return $value;
},
'set' => static function ($instance, $name, $value) {
$instance->$name = $value;
},
'isset' => static fn ($instance, $name) => isset($instance->$name),
'unset' => static function ($instance, $name) {
unset($instance->$name);
},
], null, \Closure::class === $class ? null : $class)();
}

public static function getParentMethods($class)
{
$parent = get_parent_class($class);
$methods = [];

foreach (['set', 'isset', 'unset', 'clone', 'serialize', 'unserialize', 'sleep', 'wakeup', 'destruct', 'get'] as $method) {
if (!$parent || !method_exists($parent, '__'.$method)) {
$methods[$method] = false;
} else {
$m = new \ReflectionMethod($parent, '__'.$method);
$methods[$method] = !$m->isAbstract() && !$m->isPrivate();
}
}

$methods['get'] = $methods['get'] ? ($m->returnsReference() ? 2 : 1) : 0;

return $methods;
}

public static function getScope($propertyScopes, $class, $property, $readonlyScope = null)
{
if (null === $readonlyScope && !isset($propertyScopes[$k = "\0$class\0$property"]) && !isset($propertyScopes[$k = "\0*\0$property"])) {
return null;
}
$frame = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS, 3)[2];

if (\ReflectionProperty::class === $scope = $frame['class'] ?? \Closure::class) {
$scope = $frame['object']->class;
}
if (null === $readonlyScope && '*' === $k[1] && ($class === $scope || (is_subclass_of($class, $scope) && !isset($propertyScopes["\0$scope\0$property"])))) {
return null;
}

return $scope;
}
public static \WeakMap $initializers;
}
93 changes: 0 additions & 93 deletions src/Symfony/Component/VarExporter/Internal/LazyObjectState.php

This file was deleted.

38 changes: 30 additions & 8 deletions src/Symfony/Component/VarExporter/Internal/LazyObjectTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,42 @@

namespace Symfony\Component\VarExporter\Internal;

if (\PHP_VERSION_ID >= 80300) {
/**
* @internal
*/
trait LazyObjectTrait
{
/**
* @internal
* Returns whether the object is initialized.
*
* @param $partial Whether partially initialized objects should be considered as initialized
*/
trait LazyObjectTrait
public function isLazyObjectInitialized(bool $partial = false): bool
{
private readonly LazyObjectState $lazyObjectState;
return !\ReflectionLazyObject::isLazyObject($this);
}
} else {

/**
* @internal
* @return bool Returns false when the object cannot be reset, ie when it's not a lazy object
*/
trait LazyObjectTrait
public function resetLazyObject(): bool
{
private LazyObjectState $lazyObjectState;
if (\ReflectionLazyObject::isLazyObject($this)) {
return true;
}

if (![$initializer, $strategy, $skippedProperties] = LazyObjectRegistry::$initializers[$this] ?? null) {
return false;
}

$r = \ReflectionLazyObject::makeLazy($this, $initializer, $strategy);

foreach ($skippedProperties as $class => $properties) {
foreach ($properties as $property) {
$r->skipProperty($property, $class);
}
}

return true;
}
}
Loading

0 comments on commit 247b453

Please sign in to comment.