update lock clucknut
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 14s
Build, Push and Deploy / build-and-push (push) Successful in 3m14s
Build, Push and Deploy / deploy-staging (push) Successful in 25s
Build, Push and Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-04-18 20:32:18 +07:00
parent 4554035227
commit dcaf267458
3359 changed files with 153185 additions and 205489 deletions

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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)) {

View 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;
}
}