update lock clucknut
All checks were successful
All checks were successful
This commit is contained in:
7
vendor/symfony/var-exporter/CHANGELOG.md
vendored
7
vendor/symfony/var-exporter/CHANGELOG.md
vendored
@@ -1,13 +1,6 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
8.0
|
||||
---
|
||||
|
||||
* Restrict `ProxyHelper::generateLazyProxy()` to generating abstraction-based lazy decorators; use native lazy proxies otherwise
|
||||
* Remove `LazyGhostTrait` and `LazyProxyTrait`, use native lazy objects instead
|
||||
* Remove `ProxyHelper::generateLazyGhost()`, use native lazy objects instead
|
||||
|
||||
7.4
|
||||
---
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount
|
||||
$p = $parent->hasProperty($n) ? $parent->getProperty($n) : null;
|
||||
} while (!$p && $parent = $parent->getParentClass());
|
||||
|
||||
$c = $p && (!$p->isPublic() || $p->isProtectedSet() || $p->isPrivateSet()) ? $p->class : 'stdClass';
|
||||
$c = $p && (!$p->isPublic() || (\PHP_VERSION_ID >= 80400 ? $p->isProtectedSet() || $p->isPrivateSet() : $p->isReadOnly())) ? $p->class : 'stdClass';
|
||||
} elseif ('*' === $n[1]) {
|
||||
$n = substr($n, 3);
|
||||
$c = $reflector->getProperty($n)->class;
|
||||
|
||||
@@ -222,7 +222,7 @@ public static function getSimpleHydrator($class)
|
||||
if ($propertyReflector->isStatic()) {
|
||||
continue;
|
||||
}
|
||||
if (!$propertyReflector->isAbstract() && $propertyReflector->getHooks()) {
|
||||
if (\PHP_VERSION_ID >= 80400 && !$propertyReflector->isAbstract() && $propertyReflector->getHooks()) {
|
||||
$notByRef->{$propertyReflector->name} = $propertyReflector->setRawValue(...);
|
||||
} elseif ($propertyReflector->isReadOnly()) {
|
||||
$notByRef->{$propertyReflector->name} = static function ($object, $value) use ($propertyReflector) {
|
||||
@@ -277,7 +277,7 @@ public static function getPropertyScopes($class): array
|
||||
$name = $property->name;
|
||||
$access = ($flags << 2) | ($flags & \ReflectionProperty::IS_READONLY ? self::PROPERTY_NOT_BY_REF : 0);
|
||||
|
||||
if (!$property->isAbstract() && $h = $property->getHooks()) {
|
||||
if (\PHP_VERSION_ID >= 80400 && !$property->isAbstract() && $h = $property->getHooks()) {
|
||||
$access |= self::PROPERTY_HAS_HOOKS | (isset($h['get']) && !$h['get']->returnsReference() ? self::PROPERTY_NOT_BY_REF : 0);
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ public static function getPropertyScopes($class): array
|
||||
|
||||
$propertyScopes[$name] = [$class, $name, null, $access, $property];
|
||||
|
||||
if ($flags & \ReflectionProperty::IS_PRIVATE_SET) {
|
||||
if ($flags & (\PHP_VERSION_ID >= 80400 ? \ReflectionProperty::IS_PRIVATE_SET : \ReflectionProperty::IS_READONLY)) {
|
||||
$propertyScopes[$name][2] = $property->class;
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ public static function getPropertyScopes($class): array
|
||||
$name = $property->name;
|
||||
$access = ($flags << 2) | ($flags & \ReflectionProperty::IS_READONLY ? self::PROPERTY_NOT_BY_REF : 0);
|
||||
|
||||
if ($h = $property->getHooks()) {
|
||||
if (\PHP_VERSION_ID >= 80400 && $h = $property->getHooks()) {
|
||||
$access |= self::PROPERTY_HAS_HOOKS | (isset($h['get']) && !$h['get']->returnsReference() ? self::PROPERTY_NOT_BY_REF : 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ public function &__get($name): mixed
|
||||
$notByRef = $access & Hydrator::PROPERTY_NOT_BY_REF || ($access >> 2) & \ReflectionProperty::IS_PRIVATE_SET;
|
||||
}
|
||||
|
||||
if ($notByRef || 2 !== ((Registry::$parentGet[$class] ??= Registry::getParentGet($class)) ?: 2)) {
|
||||
if ($notByRef || 2 !== ((Registry::$parentMethods[$class] ??= Registry::getParentMethods($class))['get'] ?: 2)) {
|
||||
$value = $instance->$name;
|
||||
|
||||
return $value;
|
||||
|
||||
@@ -41,9 +41,11 @@ class LazyObjectRegistry
|
||||
public static array $classAccessors = [];
|
||||
|
||||
/**
|
||||
* @var array<class-string, int>
|
||||
* @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 $parentGet = [];
|
||||
public static array $parentMethods = [];
|
||||
|
||||
public static ?\Closure $noInitializerState = null;
|
||||
|
||||
public static function getClassResetters($class)
|
||||
{
|
||||
@@ -84,16 +86,80 @@ public static function getClassResetters($class)
|
||||
return $resetters;
|
||||
}
|
||||
|
||||
public static function getParentGet($class): int
|
||||
public static function getClassAccessors($class)
|
||||
{
|
||||
return \Closure::bind(static fn () => [
|
||||
'get' => static function &($instance, $name, $notByRef) {
|
||||
if (!$notByRef) {
|
||||
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 = [];
|
||||
|
||||
if (!$parent || !method_exists($parent, '__get')) {
|
||||
return 0;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
$m = new \ReflectionMethod($parent, '__get');
|
||||
$methods['get'] = $methods['get'] ? ($m->returnsReference() ? 2 : 1) : 0;
|
||||
|
||||
return !$m->isAbstract() && !$m->isPrivate() ? ($m->returnsReference() ? 2 : 1) : 0;
|
||||
return $methods;
|
||||
}
|
||||
|
||||
public static function getScopeForRead($propertyScopes, $class, $property)
|
||||
{
|
||||
if (!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 ('*' === $k[1] && ($class === $scope || (is_subclass_of($class, $scope) && !isset($propertyScopes["\0$scope\0$property"])))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $scope;
|
||||
}
|
||||
|
||||
public static function getScopeForWrite($propertyScopes, $class, $property, $flags)
|
||||
{
|
||||
if (!($flags & (\ReflectionProperty::IS_PRIVATE | \ReflectionProperty::IS_PROTECTED | \ReflectionProperty::IS_READONLY | (\PHP_VERSION_ID >= 80400 ? \ReflectionProperty::IS_PRIVATE_SET | \ReflectionProperty::IS_PROTECTED_SET : 0)))) {
|
||||
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 ($flags & (\ReflectionProperty::IS_PRIVATE | (\PHP_VERSION_ID >= 80400 ? \ReflectionProperty::IS_PRIVATE_SET : \ReflectionProperty::IS_READONLY))) {
|
||||
return $scope;
|
||||
}
|
||||
if ($flags & (\ReflectionProperty::IS_PROTECTED | (\PHP_VERSION_ID >= 80400 ? \ReflectionProperty::IS_PROTECTED_SET : 0)) && ($class === $scope || (is_subclass_of($class, $scope) && !isset($propertyScopes["\0$scope\0$property"])))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $scope;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace Symfony\Component\VarExporter\Internal;
|
||||
|
||||
use Symfony\Component\VarExporter\Hydrator as PublicHydrator;
|
||||
|
||||
/**
|
||||
* Keeps the state of lazy objects.
|
||||
*
|
||||
@@ -20,10 +22,80 @@
|
||||
*/
|
||||
class LazyObjectState
|
||||
{
|
||||
public ?\Closure $initializer = null;
|
||||
public const STATUS_UNINITIALIZED_FULL = 1;
|
||||
public const STATUS_UNINITIALIZED_PARTIAL = 2;
|
||||
public const STATUS_INITIALIZED_FULL = 3;
|
||||
public const STATUS_INITIALIZED_PARTIAL = 4;
|
||||
|
||||
/**
|
||||
* @var self::STATUS_*
|
||||
*/
|
||||
public int $status = self::STATUS_UNINITIALIZED_FULL;
|
||||
|
||||
public object $realInstance;
|
||||
public object $cloneInstance;
|
||||
|
||||
/**
|
||||
* @param array<string, true> $skippedProperties
|
||||
*/
|
||||
public function __construct(
|
||||
public ?\Closure $initializer = null,
|
||||
public array $skippedProperties = [],
|
||||
) {
|
||||
}
|
||||
|
||||
public function initialize($instance, $propertyName, $writeScope)
|
||||
{
|
||||
if (self::STATUS_UNINITIALIZED_FULL !== $this->status) {
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
$this->status = self::STATUS_INITIALIZED_PARTIAL;
|
||||
|
||||
try {
|
||||
if ($defaultProperties = array_diff_key(LazyObjectRegistry::$defaultProperties[$instance::class], $this->skippedProperties)) {
|
||||
PublicHydrator::hydrate($instance, $defaultProperties);
|
||||
}
|
||||
|
||||
($this->initializer)($instance);
|
||||
} catch (\Throwable $e) {
|
||||
$this->status = self::STATUS_UNINITIALIZED_FULL;
|
||||
$this->reset($instance);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $this->status = self::STATUS_INITIALIZED_FULL;
|
||||
}
|
||||
|
||||
public function reset($instance): void
|
||||
{
|
||||
$class = $instance::class;
|
||||
$propertyScopes = Hydrator::$propertyScopes[$class] ??= Hydrator::getPropertyScopes($class);
|
||||
$skippedProperties = $this->skippedProperties;
|
||||
$properties = (array) $instance;
|
||||
|
||||
foreach ($propertyScopes as $key => [$scope, $name, , $access]) {
|
||||
$propertyScopes[$k = "\0$scope\0$name"] ?? $propertyScopes[$k = "\0*\0$name"] ?? $k = $name;
|
||||
|
||||
if ($k === $key && ($access & Hydrator::PROPERTY_HAS_HOOKS || ($access >> 2) & \ReflectionProperty::IS_READONLY || !\array_key_exists($k, $properties))) {
|
||||
$skippedProperties[$k] = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (LazyObjectRegistry::$classResetters[$class] as $reset) {
|
||||
$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;
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
if (isset($this->cloneInstance)) {
|
||||
|
||||
34
vendor/symfony/var-exporter/Internal/LazyObjectTrait.php
vendored
Normal file
34
vendor/symfony/var-exporter/Internal/LazyObjectTrait.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\VarExporter\Internal;
|
||||
|
||||
use Symfony\Component\Serializer\Attribute\Ignore;
|
||||
|
||||
if (\PHP_VERSION_ID >= 80300) {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
trait LazyObjectTrait
|
||||
{
|
||||
#[Ignore]
|
||||
private readonly LazyObjectState $lazyObjectState;
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
trait LazyObjectTrait
|
||||
{
|
||||
#[Ignore]
|
||||
private LazyObjectState $lazyObjectState;
|
||||
}
|
||||
}
|
||||
376
vendor/symfony/var-exporter/LazyGhostTrait.php
vendored
Normal file
376
vendor/symfony/var-exporter/LazyGhostTrait.php
vendored
Normal file
@@ -0,0 +1,376 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\VarExporter;
|
||||
|
||||
use Symfony\Component\Serializer\Attribute\Ignore;
|
||||
use Symfony\Component\VarExporter\Internal\Hydrator;
|
||||
use Symfony\Component\VarExporter\Internal\LazyObjectRegistry as Registry;
|
||||
use Symfony\Component\VarExporter\Internal\LazyObjectState;
|
||||
use Symfony\Component\VarExporter\Internal\LazyObjectTrait;
|
||||
|
||||
if (\PHP_VERSION_ID >= 80400) {
|
||||
trigger_deprecation('symfony/var-exporter', '7.3', 'The "%s" trait is deprecated, use native lazy objects instead.', LazyGhostTrait::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 7.3, use native lazy objects instead
|
||||
*/
|
||||
trait LazyGhostTrait
|
||||
{
|
||||
use LazyObjectTrait;
|
||||
|
||||
/**
|
||||
* Creates a lazy-loading ghost instance.
|
||||
*
|
||||
* Skipped properties should be indexed by their array-cast identifier, see
|
||||
* https://php.net/manual/language.types.array#language.types.array.casting
|
||||
*
|
||||
* @param \Closure(static):void $initializer The closure should initialize the object it receives as argument
|
||||
* @param array<string, true>|null $skippedProperties An array indexed by the properties to skip, a.k.a. the ones
|
||||
* that the initializer doesn't initialize, if any
|
||||
* @param static|null $instance
|
||||
*/
|
||||
public static function createLazyGhost(\Closure $initializer, ?array $skippedProperties = null, ?object $instance = null): static
|
||||
{
|
||||
if (self::class !== $class = $instance ? $instance::class : static::class) {
|
||||
$skippedProperties["\0".self::class."\0lazyObjectState"] = true;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
if (isset($instance->lazyObjectState)) {
|
||||
$instance->lazyObjectState->initializer = $initializer;
|
||||
$instance->lazyObjectState->skippedProperties = $skippedProperties ??= [];
|
||||
|
||||
if (LazyObjectState::STATUS_UNINITIALIZED_FULL !== $instance->lazyObjectState->status) {
|
||||
$instance->lazyObjectState->reset($instance);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
$instance->lazyObjectState = new LazyObjectState($initializer, $skippedProperties ??= []);
|
||||
|
||||
foreach (Registry::$classResetters[$class] as $reset) {
|
||||
$reset($instance, $skippedProperties);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the object is initialized.
|
||||
*
|
||||
* @param bool $partial Whether partially initialized objects should be considered as initialized
|
||||
*/
|
||||
#[Ignore]
|
||||
public function isLazyObjectInitialized(bool $partial = false): bool
|
||||
{
|
||||
if (!$state = $this->lazyObjectState ?? null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return LazyObjectState::STATUS_INITIALIZED_FULL === $state->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces initialization of a lazy object and returns it.
|
||||
*/
|
||||
public function initializeLazyObject(): static
|
||||
{
|
||||
if (!$state = $this->lazyObjectState ?? null) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $state->status) {
|
||||
$state->initialize($this, '', null);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool Returns false when the object cannot be reset, ie when it's not a lazy object
|
||||
*/
|
||||
public function resetLazyObject(): bool
|
||||
{
|
||||
if (!$state = $this->lazyObjectState ?? null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (LazyObjectState::STATUS_UNINITIALIZED_FULL !== $state->status) {
|
||||
$state->reset($this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function &__get($name): mixed
|
||||
{
|
||||
$propertyScopes = Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class);
|
||||
$scope = null;
|
||||
$notByRef = 0;
|
||||
|
||||
if ([$class, , $writeScope, $access] = $propertyScopes[$name] ?? null) {
|
||||
$scope = Registry::getScopeForRead($propertyScopes, $class, $name);
|
||||
$state = $this->lazyObjectState ?? null;
|
||||
|
||||
if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"]))) {
|
||||
$notByRef = $access & Hydrator::PROPERTY_NOT_BY_REF;
|
||||
|
||||
if (LazyObjectState::STATUS_INITIALIZED_FULL === $state->status) {
|
||||
// Work around php/php-src#12695
|
||||
$property = null === $scope ? $name : "\0$scope\0$name";
|
||||
$property = $propertyScopes[$property][4]
|
||||
?? Hydrator::$propertyScopes[$this::class][$property][4] = new \ReflectionProperty($scope ?? $class, $name);
|
||||
} else {
|
||||
$property = null;
|
||||
}
|
||||
if (\PHP_VERSION_ID >= 80400 && !$notByRef && ($access >> 2) & \ReflectionProperty::IS_PRIVATE_SET) {
|
||||
$scope ??= $writeScope;
|
||||
}
|
||||
|
||||
if ($property?->isInitialized($this) ?? LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $state->initialize($this, $name, $writeScope ?? $scope)) {
|
||||
goto get_in_scope;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($parent = (Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['get']) {
|
||||
if (2 === $parent) {
|
||||
return parent::__get($name);
|
||||
}
|
||||
$value = parent::__get($name);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (null === $class) {
|
||||
$frame = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0];
|
||||
trigger_error(\sprintf('Undefined property: %s::$%s in %s on line %s', $this::class, $name, $frame['file'], $frame['line']), \E_USER_NOTICE);
|
||||
}
|
||||
|
||||
get_in_scope:
|
||||
|
||||
try {
|
||||
if (null === $scope) {
|
||||
if (!$notByRef) {
|
||||
return $this->$name;
|
||||
}
|
||||
$value = $this->$name;
|
||||
|
||||
return $value;
|
||||
}
|
||||
$accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
|
||||
|
||||
return $accessor['get']($this, $name, $notByRef);
|
||||
} catch (\Error $e) {
|
||||
if (\Error::class !== $e::class || !str_starts_with($e->getMessage(), 'Cannot access uninitialized non-nullable property')) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
try {
|
||||
if (null === $scope) {
|
||||
$this->$name = [];
|
||||
|
||||
return $this->$name;
|
||||
}
|
||||
|
||||
$accessor['set']($this, $name, []);
|
||||
|
||||
return $accessor['get']($this, $name, $notByRef);
|
||||
} catch (\Error) {
|
||||
if (preg_match('/^Cannot access uninitialized non-nullable property ([^ ]++) by reference$/', $e->getMessage(), $matches)) {
|
||||
throw new \Error('Typed property '.$matches[1].' must not be accessed before initialization', $e->getCode(), $e->getPrevious());
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __set($name, $value): void
|
||||
{
|
||||
$propertyScopes = Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class);
|
||||
$scope = null;
|
||||
|
||||
if ([$class, , $writeScope, $access] = $propertyScopes[$name] ?? null) {
|
||||
$scope = Registry::getScopeForWrite($propertyScopes, $class, $name, $access >> 2);
|
||||
$state = $this->lazyObjectState ?? null;
|
||||
|
||||
if ($state && ($writeScope === $scope || isset($propertyScopes["\0$scope\0$name"]))
|
||||
&& LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status
|
||||
) {
|
||||
if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $state->status) {
|
||||
$state->initialize($this, $name, $writeScope ?? $scope);
|
||||
}
|
||||
goto set_in_scope;
|
||||
}
|
||||
}
|
||||
|
||||
if ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['set']) {
|
||||
parent::__set($name, $value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
set_in_scope:
|
||||
|
||||
if (null === $scope) {
|
||||
$this->$name = $value;
|
||||
} else {
|
||||
$accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
|
||||
$accessor['set']($this, $name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function __isset($name): bool
|
||||
{
|
||||
$propertyScopes = Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class);
|
||||
$scope = null;
|
||||
|
||||
if ([$class, , $writeScope] = $propertyScopes[$name] ?? null) {
|
||||
$scope = Registry::getScopeForRead($propertyScopes, $class, $name);
|
||||
$state = $this->lazyObjectState ?? null;
|
||||
|
||||
if ($state && (null === $scope || isset($propertyScopes["\0$scope\0$name"]))
|
||||
&& LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status
|
||||
&& LazyObjectState::STATUS_UNINITIALIZED_PARTIAL !== $state->initialize($this, $name, $writeScope ?? $scope)
|
||||
) {
|
||||
goto isset_in_scope;
|
||||
}
|
||||
}
|
||||
|
||||
if ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['isset']) {
|
||||
return parent::__isset($name);
|
||||
}
|
||||
|
||||
isset_in_scope:
|
||||
|
||||
if (null === $scope) {
|
||||
return isset($this->$name);
|
||||
}
|
||||
$accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
|
||||
|
||||
return $accessor['isset']($this, $name);
|
||||
}
|
||||
|
||||
public function __unset($name): void
|
||||
{
|
||||
$propertyScopes = Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class);
|
||||
$scope = null;
|
||||
|
||||
if ([$class, , $writeScope, $access] = $propertyScopes[$name] ?? null) {
|
||||
$scope = Registry::getScopeForWrite($propertyScopes, $class, $name, $access >> 2);
|
||||
$state = $this->lazyObjectState ?? null;
|
||||
|
||||
if ($state && ($writeScope === $scope || isset($propertyScopes["\0$scope\0$name"]))
|
||||
&& LazyObjectState::STATUS_INITIALIZED_FULL !== $state->status
|
||||
) {
|
||||
if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $state->status) {
|
||||
$state->initialize($this, $name, $writeScope ?? $scope);
|
||||
}
|
||||
goto unset_in_scope;
|
||||
}
|
||||
}
|
||||
|
||||
if ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['unset']) {
|
||||
parent::__unset($name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
unset_in_scope:
|
||||
|
||||
if (null === $scope) {
|
||||
unset($this->$name);
|
||||
} else {
|
||||
$accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
|
||||
$accessor['unset']($this, $name);
|
||||
}
|
||||
}
|
||||
|
||||
public function __clone(): void
|
||||
{
|
||||
if ($state = $this->lazyObjectState ?? null) {
|
||||
$this->lazyObjectState = clone $state;
|
||||
}
|
||||
|
||||
if ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['clone']) {
|
||||
parent::__clone();
|
||||
}
|
||||
}
|
||||
|
||||
public function __serialize(): array
|
||||
{
|
||||
$class = self::class;
|
||||
|
||||
if ((Registry::$parentMethods[$class] ??= Registry::getParentMethods($class))['serialize']) {
|
||||
$properties = parent::__serialize();
|
||||
} else {
|
||||
$this->initializeLazyObject();
|
||||
$properties = (array) $this;
|
||||
}
|
||||
unset($properties["\0$class\0lazyObjectState"]);
|
||||
|
||||
if (Registry::$parentMethods[$class]['serialize'] || !Registry::$parentMethods[$class]['sleep']) {
|
||||
return $properties;
|
||||
}
|
||||
|
||||
$scope = get_parent_class($class);
|
||||
$data = [];
|
||||
|
||||
foreach (parent::__sleep() as $name) {
|
||||
$value = $properties[$k = $name] ?? $properties[$k = "\0*\0$name"] ?? $properties[$k = "\0$class\0$name"] ?? $properties[$k = "\0$scope\0$name"] ?? $k = null;
|
||||
|
||||
if (null === $k) {
|
||||
trigger_error(\sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $name), \E_USER_NOTICE);
|
||||
} else {
|
||||
$data[$k] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$state = $this->lazyObjectState ?? null;
|
||||
|
||||
if (LazyObjectState::STATUS_UNINITIALIZED_FULL === $state?->status) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['destruct']) {
|
||||
parent::__destruct();
|
||||
}
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
private function setLazyObjectAsInitialized(bool $initialized): void
|
||||
{
|
||||
if ($state = $this->lazyObjectState ?? null) {
|
||||
$state->status = $initialized ? LazyObjectState::STATUS_INITIALIZED_FULL : LazyObjectState::STATUS_UNINITIALIZED_FULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
376
vendor/symfony/var-exporter/LazyProxyTrait.php
vendored
Normal file
376
vendor/symfony/var-exporter/LazyProxyTrait.php
vendored
Normal file
@@ -0,0 +1,376 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\VarExporter;
|
||||
|
||||
use Symfony\Component\Serializer\Attribute\Ignore;
|
||||
use Symfony\Component\VarExporter\Hydrator as PublicHydrator;
|
||||
use Symfony\Component\VarExporter\Internal\Hydrator;
|
||||
use Symfony\Component\VarExporter\Internal\LazyObjectRegistry as Registry;
|
||||
use Symfony\Component\VarExporter\Internal\LazyObjectState;
|
||||
use Symfony\Component\VarExporter\Internal\LazyObjectTrait;
|
||||
|
||||
if (\PHP_VERSION_ID >= 80400) {
|
||||
trigger_deprecation('symfony/var-exporter', '7.3', 'The "%s" trait is deprecated, use native lazy objects instead.', LazyProxyTrait::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 7.3, use native lazy objects instead
|
||||
*/
|
||||
trait LazyProxyTrait
|
||||
{
|
||||
use LazyObjectTrait;
|
||||
|
||||
/**
|
||||
* Creates a lazy-loading virtual proxy.
|
||||
*
|
||||
* @param \Closure():object $initializer Returns the proxied object
|
||||
* @param static|null $instance
|
||||
*/
|
||||
public static function createLazyProxy(\Closure $initializer, ?object $instance = null): static
|
||||
{
|
||||
if (self::class !== $class = $instance ? $instance::class : static::class) {
|
||||
$skippedProperties = ["\0".self::class."\0lazyObjectState" => true];
|
||||
}
|
||||
|
||||
if (!isset(Registry::$defaultProperties[$class])) {
|
||||
Registry::$classReflectors[$class] ??= new \ReflectionClass($class);
|
||||
$instance ??= Registry::$classReflectors[$class]->newInstanceWithoutConstructor();
|
||||
Registry::$defaultProperties[$class] ??= (array) $instance;
|
||||
|
||||
if (self::class === $class && \defined($class.'::LAZY_OBJECT_PROPERTY_SCOPES')) {
|
||||
Hydrator::$propertyScopes[$class] ??= $class::LAZY_OBJECT_PROPERTY_SCOPES;
|
||||
}
|
||||
|
||||
Registry::$classResetters[$class] ??= Registry::getClassResetters($class);
|
||||
} else {
|
||||
$instance ??= Registry::$classReflectors[$class]->newInstanceWithoutConstructor();
|
||||
}
|
||||
|
||||
if (isset($instance->lazyObjectState)) {
|
||||
$instance->lazyObjectState->initializer = $initializer;
|
||||
unset($instance->lazyObjectState->realInstance);
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
$instance->lazyObjectState = new LazyObjectState($initializer);
|
||||
|
||||
foreach (Registry::$classResetters[$class] as $reset) {
|
||||
$reset($instance, $skippedProperties ??= []);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the object is initialized.
|
||||
*
|
||||
* @param bool $partial Whether partially initialized objects should be considered as initialized
|
||||
*/
|
||||
#[Ignore]
|
||||
public function isLazyObjectInitialized(bool $partial = false): bool
|
||||
{
|
||||
return !isset($this->lazyObjectState) || isset($this->lazyObjectState->realInstance) || Registry::$noInitializerState === $this->lazyObjectState->initializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces initialization of a lazy object and returns it.
|
||||
*/
|
||||
public function initializeLazyObject(): parent
|
||||
{
|
||||
if ($state = $this->lazyObjectState ?? null) {
|
||||
return $state->realInstance ??= ($state->initializer)();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool Returns false when the object cannot be reset, ie when it's not a lazy object
|
||||
*/
|
||||
public function resetLazyObject(): bool
|
||||
{
|
||||
if (!isset($this->lazyObjectState) || Registry::$noInitializerState === $this->lazyObjectState->initializer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset($this->lazyObjectState->realInstance);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function &__get($name): mixed
|
||||
{
|
||||
$propertyScopes = Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class);
|
||||
$scope = null;
|
||||
$instance = $this;
|
||||
$notByRef = 0;
|
||||
|
||||
if ([$class, , $writeScope, $access] = $propertyScopes[$name] ?? null) {
|
||||
$notByRef = $access & Hydrator::PROPERTY_NOT_BY_REF;
|
||||
$scope = Registry::getScopeForRead($propertyScopes, $class, $name);
|
||||
|
||||
if (null === $scope || isset($propertyScopes["\0$scope\0$name"])) {
|
||||
if ($state = $this->lazyObjectState ?? null) {
|
||||
$instance = $state->realInstance ??= ($state->initializer)();
|
||||
}
|
||||
if (\PHP_VERSION_ID >= 80400 && !$notByRef && ($access >> 2) & \ReflectionProperty::IS_PRIVATE_SET) {
|
||||
$scope ??= $writeScope;
|
||||
}
|
||||
$parent = 2;
|
||||
goto get_in_scope;
|
||||
}
|
||||
}
|
||||
$parent = (Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['get'];
|
||||
|
||||
if ($state = $this->lazyObjectState ?? null) {
|
||||
$instance = $state->realInstance ??= ($state->initializer)();
|
||||
} else {
|
||||
if (2 === $parent) {
|
||||
return parent::__get($name);
|
||||
}
|
||||
$value = parent::__get($name);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (!$parent && null === $class && !\array_key_exists($name, (array) $instance)) {
|
||||
$frame = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0];
|
||||
trigger_error(\sprintf('Undefined property: %s::$%s in %s on line %s', $instance::class, $name, $frame['file'], $frame['line']), \E_USER_NOTICE);
|
||||
}
|
||||
|
||||
get_in_scope:
|
||||
$notByRef = $notByRef || 1 === $parent;
|
||||
|
||||
try {
|
||||
if (null === $scope) {
|
||||
if (!$notByRef) {
|
||||
return $instance->$name;
|
||||
}
|
||||
$value = $instance->$name;
|
||||
|
||||
return $value;
|
||||
}
|
||||
$accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
|
||||
|
||||
return $accessor['get']($instance, $name, $notByRef);
|
||||
} catch (\Error $e) {
|
||||
if (\Error::class !== $e::class || !str_starts_with($e->getMessage(), 'Cannot access uninitialized non-nullable property')) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
try {
|
||||
if (null === $scope) {
|
||||
$instance->$name = [];
|
||||
|
||||
return $instance->$name;
|
||||
}
|
||||
|
||||
$accessor['set']($instance, $name, []);
|
||||
|
||||
return $accessor['get']($instance, $name, $notByRef);
|
||||
} catch (\Error) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __set($name, $value): void
|
||||
{
|
||||
$propertyScopes = Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class);
|
||||
$scope = null;
|
||||
$instance = $this;
|
||||
|
||||
if ([$class, , $writeScope, $access] = $propertyScopes[$name] ?? null) {
|
||||
$scope = Registry::getScopeForWrite($propertyScopes, $class, $name, $access >> 2);
|
||||
|
||||
if ($writeScope === $scope || isset($propertyScopes["\0$scope\0$name"])) {
|
||||
if ($state = $this->lazyObjectState ?? null) {
|
||||
$instance = $state->realInstance ??= ($state->initializer)();
|
||||
}
|
||||
goto set_in_scope;
|
||||
}
|
||||
}
|
||||
|
||||
if ($state = $this->lazyObjectState ?? null) {
|
||||
$instance = $state->realInstance ??= ($state->initializer)();
|
||||
} elseif ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['set']) {
|
||||
parent::__set($name, $value);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
set_in_scope:
|
||||
|
||||
if (null === $scope) {
|
||||
$instance->$name = $value;
|
||||
} else {
|
||||
$accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
|
||||
$accessor['set']($instance, $name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function __isset($name): bool
|
||||
{
|
||||
$propertyScopes = Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class);
|
||||
$scope = null;
|
||||
$instance = $this;
|
||||
|
||||
if ([$class] = $propertyScopes[$name] ?? null) {
|
||||
$scope = Registry::getScopeForRead($propertyScopes, $class, $name);
|
||||
|
||||
if (null === $scope || isset($propertyScopes["\0$scope\0$name"])) {
|
||||
if ($state = $this->lazyObjectState ?? null) {
|
||||
$instance = $state->realInstance ??= ($state->initializer)();
|
||||
}
|
||||
goto isset_in_scope;
|
||||
}
|
||||
}
|
||||
|
||||
if ($state = $this->lazyObjectState ?? null) {
|
||||
$instance = $state->realInstance ??= ($state->initializer)();
|
||||
} elseif ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['isset']) {
|
||||
return parent::__isset($name);
|
||||
}
|
||||
|
||||
isset_in_scope:
|
||||
|
||||
if (null === $scope) {
|
||||
return isset($instance->$name);
|
||||
}
|
||||
$accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
|
||||
|
||||
return $accessor['isset']($instance, $name);
|
||||
}
|
||||
|
||||
public function __unset($name): void
|
||||
{
|
||||
$propertyScopes = Hydrator::$propertyScopes[$this::class] ??= Hydrator::getPropertyScopes($this::class);
|
||||
$scope = null;
|
||||
$instance = $this;
|
||||
|
||||
if ([$class, , $writeScope, $access] = $propertyScopes[$name] ?? null) {
|
||||
$scope = Registry::getScopeForWrite($propertyScopes, $class, $name, $access >> 2);
|
||||
|
||||
if ($writeScope === $scope || isset($propertyScopes["\0$scope\0$name"])) {
|
||||
if ($state = $this->lazyObjectState ?? null) {
|
||||
$instance = $state->realInstance ??= ($state->initializer)();
|
||||
}
|
||||
goto unset_in_scope;
|
||||
}
|
||||
}
|
||||
|
||||
if ($state = $this->lazyObjectState ?? null) {
|
||||
$instance = $state->realInstance ??= ($state->initializer)();
|
||||
} elseif ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['unset']) {
|
||||
parent::__unset($name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
unset_in_scope:
|
||||
|
||||
if (null === $scope) {
|
||||
unset($instance->$name);
|
||||
} else {
|
||||
$accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
|
||||
$accessor['unset']($instance, $name);
|
||||
}
|
||||
}
|
||||
|
||||
public function __clone(): void
|
||||
{
|
||||
if (!isset($this->lazyObjectState)) {
|
||||
if ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['clone']) {
|
||||
parent::__clone();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->lazyObjectState = clone $this->lazyObjectState;
|
||||
}
|
||||
|
||||
public function __serialize(): array
|
||||
{
|
||||
$class = self::class;
|
||||
$state = $this->lazyObjectState ?? null;
|
||||
|
||||
if (!$state && (Registry::$parentMethods[$class] ??= Registry::getParentMethods($class))['serialize']) {
|
||||
$properties = parent::__serialize();
|
||||
} else {
|
||||
$properties = (array) $this;
|
||||
|
||||
if ($state) {
|
||||
unset($properties["\0$class\0lazyObjectState"]);
|
||||
$properties["\0$class\0lazyObjectReal"] = $state->realInstance ??= ($state->initializer)();
|
||||
}
|
||||
}
|
||||
|
||||
if ($state || Registry::$parentMethods[$class]['serialize'] || !Registry::$parentMethods[$class]['sleep']) {
|
||||
return $properties;
|
||||
}
|
||||
|
||||
$scope = get_parent_class($class);
|
||||
$data = [];
|
||||
|
||||
foreach (parent::__sleep() as $name) {
|
||||
$value = $properties[$k = $name] ?? $properties[$k = "\0*\0$name"] ?? $properties[$k = "\0$class\0$name"] ?? $properties[$k = "\0$scope\0$name"] ?? $k = null;
|
||||
|
||||
if (null === $k) {
|
||||
trigger_error(\sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $name), \E_USER_NOTICE);
|
||||
} else {
|
||||
$data[$k] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
$class = self::class;
|
||||
|
||||
if ($instance = $data["\0$class\0lazyObjectReal"] ?? null) {
|
||||
unset($data["\0$class\0lazyObjectReal"]);
|
||||
|
||||
foreach (Registry::$classResetters[$class] ??= Registry::getClassResetters($class) as $reset) {
|
||||
$reset($this, $data);
|
||||
}
|
||||
|
||||
if ($data) {
|
||||
PublicHydrator::hydrate($this, $data);
|
||||
}
|
||||
$this->lazyObjectState = new LazyObjectState(Registry::$noInitializerState ??= static fn () => throw new \LogicException('Lazy proxy has no initializer.'));
|
||||
$this->lazyObjectState->realInstance = $instance;
|
||||
} elseif ((Registry::$parentMethods[$class] ??= Registry::getParentMethods($class))['unserialize']) {
|
||||
parent::__unserialize($data);
|
||||
} else {
|
||||
PublicHydrator::hydrate($this, $data);
|
||||
|
||||
if (Registry::$parentMethods[$class]['wakeup']) {
|
||||
parent::__wakeup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (isset($this->lazyObjectState)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['destruct']) {
|
||||
parent::__destruct();
|
||||
}
|
||||
}
|
||||
}
|
||||
258
vendor/symfony/var-exporter/ProxyHelper.php
vendored
258
vendor/symfony/var-exporter/ProxyHelper.php
vendored
@@ -21,6 +21,108 @@
|
||||
*/
|
||||
final class ProxyHelper
|
||||
{
|
||||
/**
|
||||
* Helps generate lazy-loading ghost objects.
|
||||
*
|
||||
* @deprecated since Symfony 7.3, use native lazy objects instead
|
||||
*
|
||||
* @throws LogicException When the class is incompatible with ghost objects
|
||||
*/
|
||||
public static function generateLazyGhost(\ReflectionClass $class): string
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 80400) {
|
||||
trigger_deprecation('symfony/var-exporter', '7.3', 'Using ProxyHelper::generateLazyGhost() is deprecated, use native lazy objects instead.');
|
||||
}
|
||||
if (\PHP_VERSION_ID < 80300 && $class->isReadOnly()) {
|
||||
throw new LogicException(\sprintf('Cannot generate lazy ghost with PHP < 8.3: class "%s" is readonly.', $class->name));
|
||||
}
|
||||
if ($class->isFinal()) {
|
||||
throw new LogicException(\sprintf('Cannot generate lazy ghost: class "%s" is final.', $class->name));
|
||||
}
|
||||
if ($class->isInterface() || $class->isAbstract() || $class->isTrait()) {
|
||||
throw new LogicException(\sprintf('Cannot generate lazy ghost: "%s" is not a concrete class.', $class->name));
|
||||
}
|
||||
if (\stdClass::class !== $class->name && $class->isInternal()) {
|
||||
throw new LogicException(\sprintf('Cannot generate lazy ghost: class "%s" is internal.', $class->name));
|
||||
}
|
||||
if ($class->hasMethod('__get') && 'mixed' !== (self::exportType($class->getMethod('__get')) ?? 'mixed')) {
|
||||
throw new LogicException(\sprintf('Cannot generate lazy ghost: return type of method "%s::__get()" should be "mixed".', $class->name));
|
||||
}
|
||||
|
||||
static $traitMethods;
|
||||
$traitMethods ??= (new \ReflectionClass(LazyGhostTrait::class))->getMethods();
|
||||
|
||||
foreach ($traitMethods as $method) {
|
||||
if ($class->hasMethod($method->name) && $class->getMethod($method->name)->isFinal()) {
|
||||
throw new LogicException(\sprintf('Cannot generate lazy ghost: method "%s::%s()" is final.', $class->name, $method->name));
|
||||
}
|
||||
}
|
||||
|
||||
$parent = $class;
|
||||
while ($parent = $parent->getParentClass()) {
|
||||
if (\stdClass::class !== $parent->name && $parent->isInternal()) {
|
||||
throw new LogicException(\sprintf('Cannot generate lazy ghost: class "%s" extends "%s" which is internal.', $class->name, $parent->name));
|
||||
}
|
||||
}
|
||||
|
||||
$hooks = '';
|
||||
$propertyScopes = Hydrator::$propertyScopes[$class->name] ??= Hydrator::getPropertyScopes($class->name);
|
||||
foreach ($propertyScopes as $key => [$scope, $name, , $access]) {
|
||||
$propertyScopes[$k = "\0$scope\0$name"] ?? $propertyScopes[$k = "\0*\0$name"] ?? $k = $name;
|
||||
$flags = $access >> 2;
|
||||
|
||||
if ($k !== $key || !($access & Hydrator::PROPERTY_HAS_HOOKS) || $flags & \ReflectionProperty::IS_VIRTUAL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($flags & (\ReflectionProperty::IS_FINAL | \ReflectionProperty::IS_PRIVATE)) {
|
||||
throw new LogicException(\sprintf('Cannot generate lazy ghost: property "%s::$%s" is final or private(set).', $class->name, $name));
|
||||
}
|
||||
|
||||
$p = $propertyScopes[$k][4] ?? Hydrator::$propertyScopes[$class->name][$k][4] = new \ReflectionProperty($scope, $name);
|
||||
|
||||
$type = self::exportType($p);
|
||||
$hooks .= "\n "
|
||||
.($p->isProtected() ? 'protected' : 'public')
|
||||
.($p->isProtectedSet() ? ' protected(set)' : '')
|
||||
." {$type} \${$name}"
|
||||
.($p->hasDefaultValue() ? ' = '.VarExporter::export($p->getDefaultValue()) : '')
|
||||
." {\n";
|
||||
|
||||
foreach ($p->getHooks() as $hook => $method) {
|
||||
if ('get' === $hook) {
|
||||
$ref = ($method->returnsReference() ? '&' : '');
|
||||
$hooks .= " {$ref}get { \$this->initializeLazyObject(); return parent::\${$name}::get(); }\n";
|
||||
} elseif ('set' === $hook) {
|
||||
$parameters = self::exportParameters($method, true);
|
||||
$arg = '$'.$method->getParameters()[0]->name;
|
||||
$hooks .= " set({$parameters}) { \$this->initializeLazyObject(); parent::\${$name}::set({$arg}); }\n";
|
||||
} else {
|
||||
throw new LogicException(\sprintf('Cannot generate lazy ghost: hook "%s::%s()" is not supported.', $class->name, $method->name));
|
||||
}
|
||||
}
|
||||
|
||||
$hooks .= " }\n";
|
||||
}
|
||||
|
||||
$propertyScopes = self::exportPropertyScopes($class->name, $propertyScopes);
|
||||
|
||||
return <<<EOPHP
|
||||
extends \\{$class->name} implements \Symfony\Component\VarExporter\LazyObjectInterface
|
||||
{
|
||||
use \Symfony\Component\VarExporter\LazyGhostTrait;
|
||||
|
||||
private const LAZY_OBJECT_PROPERTY_SCOPES = {$propertyScopes};
|
||||
{$hooks}}
|
||||
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class);
|
||||
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class);
|
||||
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class);
|
||||
|
||||
EOPHP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helps generate lazy-loading decorators.
|
||||
*
|
||||
@@ -36,6 +138,9 @@ public static function generateLazyProxy(?\ReflectionClass $class, array $interf
|
||||
if ($class?->isFinal()) {
|
||||
throw new LogicException(\sprintf('Cannot generate lazy proxy: class "%s" is final.', $class->name));
|
||||
}
|
||||
if (\PHP_VERSION_ID < 80400) {
|
||||
return self::generateLegacyLazyProxy($class, $interfaces);
|
||||
}
|
||||
|
||||
if ($class && !$class->isAbstract()) {
|
||||
$parent = $class;
|
||||
@@ -44,7 +149,8 @@ public static function generateLazyProxy(?\ReflectionClass $class, array $interf
|
||||
} while (!$extendsInternalClass && $parent = $parent->getParentClass());
|
||||
|
||||
if (!$extendsInternalClass) {
|
||||
throw new LogicException(\sprintf('Cannot generate lazy proxy: leverage native lazy objects instead for class "%s".', $class->name));
|
||||
trigger_deprecation('symfony/var-exporter', '7.3', 'Generating lazy proxy for class "%s" is deprecated; leverage native lazy objects instead.', $class->name);
|
||||
// throw new LogicException(\sprintf('Cannot generate lazy proxy: leverage native lazy objects instead for class "%s".', $class->name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,6 +376,156 @@ class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class);
|
||||
EOPHP;
|
||||
}
|
||||
|
||||
private static function generateLegacyLazyProxy(?\ReflectionClass $class, array $interfaces): string
|
||||
{
|
||||
if (\PHP_VERSION_ID < 80300 && $class?->isReadOnly()) {
|
||||
throw new LogicException(\sprintf('Cannot generate lazy proxy with PHP < 8.3: class "%s" is readonly.', $class->name));
|
||||
}
|
||||
|
||||
$propertyScopes = $class ? Hydrator::$propertyScopes[$class->name] ??= Hydrator::getPropertyScopes($class->name) : [];
|
||||
$methodReflectors = [$class?->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) ?? []];
|
||||
foreach ($interfaces as $interface) {
|
||||
if (!$interface->isInterface()) {
|
||||
throw new LogicException(\sprintf('Cannot generate lazy proxy: "%s" is not an interface.', $interface->name));
|
||||
}
|
||||
$methodReflectors[] = $interface->getMethods();
|
||||
}
|
||||
|
||||
$extendsInternalClass = false;
|
||||
if ($parent = $class) {
|
||||
do {
|
||||
$extendsInternalClass = \stdClass::class !== $parent->name && $parent->isInternal();
|
||||
} while (!$extendsInternalClass && $parent = $parent->getParentClass());
|
||||
}
|
||||
$methodsHaveToBeProxied = $extendsInternalClass;
|
||||
$methods = [];
|
||||
$methodReflectors = array_merge(...$methodReflectors);
|
||||
|
||||
foreach ($methodReflectors as $method) {
|
||||
if ('__get' !== strtolower($method->name) || 'mixed' === ($type = self::exportType($method) ?? 'mixed')) {
|
||||
continue;
|
||||
}
|
||||
$methodsHaveToBeProxied = true;
|
||||
$trait = new \ReflectionMethod(LazyProxyTrait::class, '__get');
|
||||
$body = \array_slice(file($trait->getFileName()), $trait->getStartLine() - 1, $trait->getEndLine() - $trait->getStartLine());
|
||||
$body[0] = str_replace('): mixed', '): '.$type, $body[0]);
|
||||
$methods['__get'] = strtr(implode('', $body).' }', [
|
||||
'Hydrator' => '\\'.Hydrator::class,
|
||||
'Registry' => '\\'.LazyObjectRegistry::class,
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($methodReflectors as $method) {
|
||||
if (($method->isStatic() && !$method->isAbstract()) || isset($methods[$lcName = strtolower($method->name)])) {
|
||||
continue;
|
||||
}
|
||||
if ($method->isFinal()) {
|
||||
if ($extendsInternalClass || $methodsHaveToBeProxied || method_exists(LazyProxyTrait::class, $method->name)) {
|
||||
throw new LogicException(\sprintf('Cannot generate lazy proxy: method "%s::%s()" is final.', $class->name, $method->name));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (method_exists(LazyProxyTrait::class, $method->name) || ($method->isProtected() && !$method->isAbstract())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$signature = self::exportSignature($method, true, $args);
|
||||
$parentCall = $method->isAbstract() ? "throw new \BadMethodCallException('Cannot forward abstract method \"{$method->class}::{$method->name}()\".')" : "parent::{$method->name}({$args})";
|
||||
|
||||
if ($method->isStatic()) {
|
||||
$body = " $parentCall;";
|
||||
} elseif (str_ends_with($signature, '): never') || str_ends_with($signature, '): void')) {
|
||||
$body = <<<EOPHP
|
||||
if (isset(\$this->lazyObjectState)) {
|
||||
(\$this->lazyObjectState->realInstance ??= (\$this->lazyObjectState->initializer)())->{$method->name}({$args});
|
||||
} else {
|
||||
{$parentCall};
|
||||
}
|
||||
EOPHP;
|
||||
} else {
|
||||
if (!$methodsHaveToBeProxied && !$method->isAbstract()) {
|
||||
// Skip proxying methods that might return $this
|
||||
foreach (preg_split('/[()|&]++/', self::exportType($method) ?? 'static') as $type) {
|
||||
if (\in_array($type = ltrim($type, '?'), ['static', 'object'], true)) {
|
||||
continue 2;
|
||||
}
|
||||
foreach ([$class, ...$interfaces] as $r) {
|
||||
if ($r && is_a($r->name, $type, true)) {
|
||||
continue 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$body = <<<EOPHP
|
||||
if (isset(\$this->lazyObjectState)) {
|
||||
return (\$this->lazyObjectState->realInstance ??= (\$this->lazyObjectState->initializer)())->{$method->name}({$args});
|
||||
}
|
||||
|
||||
return {$parentCall};
|
||||
EOPHP;
|
||||
}
|
||||
$methods[$lcName] = " {$signature}\n {\n{$body}\n }";
|
||||
}
|
||||
|
||||
$types = $interfaces = array_unique(array_column($interfaces, 'name'));
|
||||
$interfaces[] = LazyObjectInterface::class;
|
||||
$interfaces = implode(', \\', $interfaces);
|
||||
$parent = $class ? ' extends \\'.$class->name : '';
|
||||
array_unshift($types, $class ? 'parent' : '');
|
||||
$type = ltrim(implode('&\\', $types), '&');
|
||||
|
||||
if (!$class) {
|
||||
$trait = new \ReflectionMethod(LazyProxyTrait::class, 'initializeLazyObject');
|
||||
$body = \array_slice(file($trait->getFileName()), $trait->getStartLine() - 1, $trait->getEndLine() - $trait->getStartLine());
|
||||
$body[0] = str_replace('): parent', '): '.$type, $body[0]);
|
||||
$methods = ['initializeLazyObject' => implode('', $body).' }'] + $methods;
|
||||
}
|
||||
$body = $methods ? "\n".implode("\n\n", $methods)."\n" : '';
|
||||
$propertyScopes = $class ? self::exportPropertyScopes($class->name, $propertyScopes) : '[]';
|
||||
|
||||
if (
|
||||
$class?->hasMethod('__unserialize')
|
||||
&& !$class->getMethod('__unserialize')->getParameters()[0]->getType()
|
||||
) {
|
||||
// fix contravariance type problem when $class declares a `__unserialize()` method without typehint.
|
||||
$lazyProxyTraitStatement = <<<EOPHP
|
||||
use \Symfony\Component\VarExporter\LazyProxyTrait {
|
||||
__unserialize as private __doUnserialize;
|
||||
}
|
||||
EOPHP;
|
||||
|
||||
$body .= <<<EOPHP
|
||||
|
||||
public function __unserialize(\$data): void
|
||||
{
|
||||
\$this->__doUnserialize(\$data);
|
||||
}
|
||||
|
||||
EOPHP;
|
||||
} else {
|
||||
$lazyProxyTraitStatement = <<<EOPHP
|
||||
use \Symfony\Component\VarExporter\LazyProxyTrait;
|
||||
EOPHP;
|
||||
}
|
||||
|
||||
return <<<EOPHP
|
||||
{$parent} implements \\{$interfaces}
|
||||
{
|
||||
{$lazyProxyTraitStatement}
|
||||
|
||||
private const LAZY_OBJECT_PROPERTY_SCOPES = {$propertyScopes};
|
||||
{$body}}
|
||||
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class);
|
||||
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class);
|
||||
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class);
|
||||
|
||||
EOPHP;
|
||||
}
|
||||
|
||||
public static function exportParameters(\ReflectionFunctionAbstract $function, bool $withParameterTypes = true, ?string &$args = null): string
|
||||
{
|
||||
$byRefIndex = 0;
|
||||
|
||||
4
vendor/symfony/var-exporter/README.md
vendored
4
vendor/symfony/var-exporter/README.md
vendored
@@ -84,6 +84,10 @@
|
||||
// be called only when and if a *method* is called.
|
||||
```
|
||||
|
||||
In addition, this component provides traits and methods to aid in implementing
|
||||
the ghost and proxy strategies in previous versions of PHP. Those are deprecated
|
||||
when using PHP 8.4.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
|
||||
9
vendor/symfony/var-exporter/composer.json
vendored
9
vendor/symfony/var-exporter/composer.json
vendored
@@ -16,12 +16,13 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.4"
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/property-access": "^7.4|^8.0",
|
||||
"symfony/serializer": "^7.4|^8.0",
|
||||
"symfony/var-dumper": "^7.4|^8.0"
|
||||
"symfony/property-access": "^6.4|^7.0|^8.0",
|
||||
"symfony/serializer": "^6.4|^7.0|^8.0",
|
||||
"symfony/var-dumper": "^6.4|^7.0|^8.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\VarExporter\\": "" },
|
||||
|
||||
Reference in New Issue
Block a user