From f74b691a88a80083fb154a55e34a123a20086ae3 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 24 Jan 2023 15:02:24 +0100 Subject: [PATCH 1/7] Update license years (last time) --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 99757d5..7536cae 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2023 Fabien Potencier +Copyright (c) 2018-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From aee001e23a67c1599082faf069e440bf565940c0 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 7 Feb 2023 16:02:15 +0100 Subject: [PATCH 2/7] [VarExporter] Fix lazy-proxying readonly classes on PHP 8.3 --- Internal/LazyObjectRegistry.php | 5 +- Internal/LazyObjectState.php | 2 + Internal/LazyObjectTrait.php | 30 ++++++ LazyGhostTrait.php | 3 +- LazyProxyTrait.php | 111 ++++++++------------- ProxyHelper.php | 23 ++--- Tests/Fixtures/LazyGhost/ReadOnlyClass.php | 20 ++++ Tests/LazyGhostTraitTest.php | 11 ++ Tests/LazyProxyTraitTest.php | 15 ++- Tests/ProxyHelperTest.php | 38 +++---- 10 files changed, 143 insertions(+), 115 deletions(-) create mode 100644 Internal/LazyObjectTrait.php create mode 100644 Tests/Fixtures/LazyGhost/ReadOnlyClass.php diff --git a/Internal/LazyObjectRegistry.php b/Internal/LazyObjectRegistry.php index 2ce7df1..c78bdcf 100644 --- a/Internal/LazyObjectRegistry.php +++ b/Internal/LazyObjectRegistry.php @@ -45,10 +45,7 @@ class LazyObjectRegistry */ public static $parentMethods = []; - /** - * @var LazyObjectState - */ - public static $noInitializerState; + public static ?\Closure $noInitializerState = null; public static function getClassResetters($class) { diff --git a/Internal/LazyObjectState.php b/Internal/LazyObjectState.php index 99f721d..2f649dd 100644 --- a/Internal/LazyObjectState.php +++ b/Internal/LazyObjectState.php @@ -37,6 +37,8 @@ class LazyObjectState */ public int $status = 0; + public object $realInstance; + public function __construct(public readonly \Closure|array $initializer, $skippedProperties = []) { $this->skippedProperties = $skippedProperties; diff --git a/Internal/LazyObjectTrait.php b/Internal/LazyObjectTrait.php new file mode 100644 index 0000000..cccdf6c --- /dev/null +++ b/Internal/LazyObjectTrait.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarExporter\Internal; + +if (\PHP_VERSION_ID >= 80300) { + /** + * @internal + */ + trait LazyObjectTrait + { + private readonly LazyObjectState $lazyObjectState; + } +} else { + /** + * @internal + */ + trait LazyObjectTrait + { + private LazyObjectState $lazyObjectState; + } +} diff --git a/LazyGhostTrait.php b/LazyGhostTrait.php index 92c240b..467e4d9 100644 --- a/LazyGhostTrait.php +++ b/LazyGhostTrait.php @@ -14,10 +14,11 @@ 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; trait LazyGhostTrait { - private LazyObjectState $lazyObjectState; + use LazyObjectTrait; /** * Creates a lazy-loading ghost instance. diff --git a/LazyProxyTrait.php b/LazyProxyTrait.php index 71ecb5b..153c382 100644 --- a/LazyProxyTrait.php +++ b/LazyProxyTrait.php @@ -15,17 +15,17 @@ 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; trait LazyProxyTrait { - private LazyObjectState $lazyObjectState; - private object $lazyObjectReal; + use LazyObjectTrait; /** * Creates a lazy-loading virtual proxy. * * @param \Closure():object $initializer Returns the proxied object - * @param static|null $instance + * @param static|null $instance */ public static function createLazyProxy(\Closure $initializer, object $instance = null): static { @@ -52,11 +52,7 @@ public static function createLazyProxy(\Closure $initializer, object $instance = */ public function isLazyObjectInitialized(bool $partial = false): bool { - if (!isset($this->lazyObjectState) || Registry::$noInitializerState === $this->lazyObjectState) { - return true; - } - - return \array_key_exists("\0".self::class."\0lazyObjectReal", (array) $this); + return !isset($this->lazyObjectState) || isset($this->lazyObjectState->realInstance) || Registry::$noInitializerState === $this->lazyObjectState->initializer; } /** @@ -64,8 +60,8 @@ public function isLazyObjectInitialized(bool $partial = false): bool */ public function initializeLazyObject(): parent { - if (isset($this->lazyObjectReal)) { - return $this->lazyObjectReal; + if ($state = $this->lazyObjectState ?? null) { + return $state->realInstance ??= ($state->initializer)(); } return $this; @@ -76,13 +72,11 @@ public function initializeLazyObject(): parent */ public function resetLazyObject(): bool { - if (!isset($this->lazyObjectState) || Registry::$noInitializerState === $this->lazyObjectState) { + if (!isset($this->lazyObjectState) || Registry::$noInitializerState === $this->lazyObjectState->initializer) { return false; } - if (\array_key_exists("\0".self::class."\0lazyObjectReal", (array) $this)) { - unset($this->lazyObjectReal); - } + unset($this->lazyObjectState->realInstance); return true; } @@ -98,14 +92,7 @@ public function &__get($name): mixed if (null === $scope || isset($propertyScopes["\0$scope\0$name"])) { if ($state = $this->lazyObjectState ?? null) { - if ('lazyObjectReal' === $name && self::class === $scope) { - $this->lazyObjectReal = ($state->initializer)(); - - return $this->lazyObjectReal; - } - if (isset($this->lazyObjectReal)) { - $instance = $this->lazyObjectReal; - } + $instance = $state->realInstance ??= ($state->initializer)(); } $parent = 2; goto get_in_scope; @@ -113,8 +100,8 @@ public function &__get($name): mixed } $parent = (Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['get']; - if (isset($this->lazyObjectReal)) { - $instance = $this->lazyObjectReal; + if ($state = $this->lazyObjectState ?? null) { + $instance = $state->realInstance ??= ($state->initializer)(); } else { if (2 === $parent) { return parent::__get($name); @@ -174,22 +161,15 @@ public function __set($name, $value): void $scope = Registry::getScope($propertyScopes, $class, $name, $readonlyScope); if ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"])) { - if (isset($this->lazyObjectState)) { - if ('lazyObjectReal' === $name && self::class === $scope) { - $this->lazyObjectReal = $value; - - return; - } - if (isset($this->lazyObjectReal)) { - $instance = $this->lazyObjectReal; - } + if ($state = $this->lazyObjectState ?? null) { + $instance = $state->realInstance ??= ($state->initializer)(); } goto set_in_scope; } } - if (isset($this->lazyObjectReal)) { - $instance = $this->lazyObjectReal; + if ($state = $this->lazyObjectState ?? null) { + $instance = $state->realInstance ??= ($state->initializer)(); } elseif ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['set']) { parent::__set($name, $value); @@ -216,22 +196,15 @@ public function __isset($name): bool $scope = Registry::getScope($propertyScopes, $class, $name); if (null === $scope || isset($propertyScopes["\0$scope\0$name"])) { - if (isset($this->lazyObjectState)) { - if ('lazyObjectReal' === $name && self::class === $scope) { - $state = $this->lazyObjectState ?? null; - - return null !== $this->lazyObjectReal = $state ? ($state->initializer)() : null; - } - if (isset($this->lazyObjectReal)) { - $instance = $this->lazyObjectReal; - } + if ($state = $this->lazyObjectState ?? null) { + $instance = $state->realInstance ??= ($state->initializer)(); } goto isset_in_scope; } } - if (isset($this->lazyObjectReal)) { - $instance = $this->lazyObjectReal; + if ($state = $this->lazyObjectState ?? null) { + $instance = $state->realInstance ??= ($state->initializer)(); } elseif ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['isset']) { return parent::__isset($name); } @@ -256,22 +229,15 @@ public function __unset($name): void $scope = Registry::getScope($propertyScopes, $class, $name, $readonlyScope); if ($readonlyScope === $scope || isset($propertyScopes["\0$scope\0$name"])) { - if (isset($this->lazyObjectState)) { - if ('lazyObjectReal' === $name && self::class === $scope) { - unset($this->lazyObjectReal); - - return; - } - if (isset($this->lazyObjectReal)) { - $instance = $this->lazyObjectReal; - } + if ($state = $this->lazyObjectState ?? null) { + $instance = $state->realInstance ??= ($state->initializer)(); } goto unset_in_scope; } } - if (isset($this->lazyObjectReal)) { - $instance = $this->lazyObjectReal; + if ($state = $this->lazyObjectState ?? null) { + $instance = $state->realInstance ??= ($state->initializer)(); } elseif ((Registry::$parentMethods[self::class] ??= Registry::getParentMethods(self::class))['unset']) { parent::__unset($name); @@ -298,26 +264,30 @@ public function __clone(): void return; } - if (\array_key_exists("\0".self::class."\0lazyObjectReal", (array) $this)) { - $this->lazyObjectReal = clone $this->lazyObjectReal; - } - if ($state = $this->lazyObjectState ?? null) { - $this->lazyObjectState = clone $state; + $this->lazyObjectState = clone $this->lazyObjectState; + + if (isset($this->lazyObjectState->realInstance)) { + $this->lazyObjectState->realInstance = clone $this->lazyObjectState->realInstance; } } public function __serialize(): array { $class = self::class; + $state = $this->lazyObjectState ?? null; - if (!isset($this->lazyObjectReal) && (Registry::$parentMethods[$class] ??= Registry::getParentMethods($class))['serialize']) { + 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)(); + } } - unset($properties["\0$class\0lazyObjectState"]); - if (isset($this->lazyObjectReal) || Registry::$parentMethods[$class]['serialize'] || !Registry::$parentMethods[$class]['sleep']) { + if ($state || Registry::$parentMethods[$class]['serialize'] || !Registry::$parentMethods[$class]['sleep']) { return $properties; } @@ -341,17 +311,18 @@ public function __unserialize(array $data): void { $class = self::class; - if (isset($data["\0$class\0lazyObjectReal"])) { + 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 (1 < \count($data)) { + if ($data) { PublicHydrator::hydrate($this, $data); - } else { - $this->lazyObjectReal = $data["\0$class\0lazyObjectReal"]; } - $this->lazyObjectState = Registry::$noInitializerState ??= new LazyObjectState(static fn () => throw new \LogicException('Lazy proxy has no initializer.')); + $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 { diff --git a/ProxyHelper.php b/ProxyHelper.php index c323f0b..5ae3892 100644 --- a/ProxyHelper.php +++ b/ProxyHelper.php @@ -27,8 +27,8 @@ final class ProxyHelper */ public static function generateLazyGhost(\ReflectionClass $class): string { - if (\PHP_VERSION_ID >= 80200 && $class->isReadOnly()) { - throw new LogicException(sprintf('Cannot generate lazy ghost: class "%s" is read-only.', $class->name)); + if (\PHP_VERSION_ID >= 80200 && \PHP_VERSION_ID < 80300 && $class->isReadOnly()) { + throw new LogicException(sprintf('Cannot generate lazy ghost: class "%s" is readonly.', $class->name)); } if ($class->isFinal()) { throw new LogicException(sprintf('Cannot generate lazy ghost: class "%s" is final.', $class->name)); @@ -91,8 +91,8 @@ 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 >= 80200 && $class?->isReadOnly()) { - throw new LogicException(sprintf('Cannot generate lazy proxy: class "%s" is read-only.', $class->name)); + if (\PHP_VERSION_ID >= 80200 && \PHP_VERSION_ID < 80300 && $class?->isReadOnly()) { + throw new LogicException(sprintf('Cannot generate lazy proxy: class "%s" is readonly.', $class->name)); } $methodReflectors = [$class?->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) ?? []]; @@ -149,8 +149,8 @@ public static function generateLazyProxy(?\ReflectionClass $class, array $interf $body = " $parentCall;"; } elseif (str_ends_with($signature, '): never') || str_ends_with($signature, '): void')) { $body = <<lazyObjectReal)) { - \$this->lazyObjectReal->{$method->name}(...\\func_get_args()); + if (isset(\$this->lazyObjectState)) { + (\$this->lazyObjectState->realInstance ??= (\$this->lazyObjectState->initializer)())->{$method->name}(...\\func_get_args()); } else { {$parentCall}; } @@ -171,8 +171,8 @@ public static function generateLazyProxy(?\ReflectionClass $class, array $interf } $body = <<lazyObjectReal)) { - return \$this->lazyObjectReal->{$method->name}(...\\func_get_args()); + if (isset(\$this->lazyObjectState)) { + return (\$this->lazyObjectState->realInstance ??= (\$this->lazyObjectState->initializer)())->{$method->name}(...\\func_get_args()); } return {$parentCall}; @@ -195,17 +195,14 @@ public static function generateLazyProxy(?\ReflectionClass $class, array $interf $methods = ['initializeLazyObject' => implode('', $body).' }'] + $methods; } $body = $methods ? "\n".implode("\n\n", $methods)."\n" : ''; - $propertyScopes = $class ? substr(self::exportPropertyScopes($class->name), 1, -6) : ''; + $propertyScopes = $class ? self::exportPropertyScopes($class->name) : '[]'; return << [self::class, 'lazyObjectReal', null], - "\\0".self::class."\\0lazyObjectReal" => [self::class, 'lazyObjectReal', null],{$propertyScopes} - ]; + private const LAZY_OBJECT_PROPERTY_SCOPES = {$propertyScopes}; {$body}} // Help opcache.preload discover always-needed symbols diff --git a/Tests/Fixtures/LazyGhost/ReadOnlyClass.php b/Tests/Fixtures/LazyGhost/ReadOnlyClass.php new file mode 100644 index 0000000..52f49e2 --- /dev/null +++ b/Tests/Fixtures/LazyGhost/ReadOnlyClass.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost; + +readonly class ReadOnlyClass +{ + public function __construct( + public int $foo + ) { + } +} diff --git a/Tests/LazyGhostTraitTest.php b/Tests/LazyGhostTraitTest.php index 3ef2a8c..8be310c 100644 --- a/Tests/LazyGhostTraitTest.php +++ b/Tests/LazyGhostTraitTest.php @@ -19,6 +19,7 @@ use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\ChildTestClass; use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\LazyClass; use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\MagicClass; +use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\ReadOnlyClass; use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\TestClass; class LazyGhostTraitTest extends TestCase @@ -407,6 +408,16 @@ public function testIndirectModification() $this->assertSame([123], $proxy->foo); } + /** + * @requires PHP 8.3 + */ + public function testReadOnlyClass() + { + $proxy = $this->createLazyGhost(ReadOnlyClass::class, fn ($proxy) => $proxy->__construct(123)); + + $this->assertSame(123, $proxy->foo); + } + /** * @template T * diff --git a/Tests/LazyProxyTraitTest.php b/Tests/LazyProxyTraitTest.php index 074934a..58d49e5 100644 --- a/Tests/LazyProxyTraitTest.php +++ b/Tests/LazyProxyTraitTest.php @@ -168,10 +168,10 @@ public function testDynamicProperty() $this->assertSame(1, $initCounter); $this->assertSame(123, $proxy->dynProp); $this->assertTrue(isset($proxy->dynProp)); - $this->assertCount(2, (array) $proxy); + $this->assertCount(1, (array) $proxy); unset($proxy->dynProp); $this->assertFalse(isset($proxy->dynProp)); - $this->assertCount(2, (array) $proxy); + $this->assertCount(1, (array) $proxy); } public function testStringMagicGet() @@ -250,9 +250,14 @@ public function testIndirectModification() */ public function testReadOnlyClass() { - $this->expectException(LogicException::class); - $this->expectExceptionMessage('Cannot generate lazy proxy: class "Symfony\Component\VarExporter\Tests\Fixtures\LazyProxy\ReadOnlyClass" is read-only.'); - $this->createLazyProxy(ReadOnlyClass::class, fn () => new ReadOnlyClass(123)); + if (\PHP_VERSION_ID < 80300) { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Cannot generate lazy proxy: class "Symfony\Component\VarExporter\Tests\Fixtures\LazyProxy\ReadOnlyClass" is readonly.'); + } + + $proxy = $this->createLazyProxy(ReadOnlyClass::class, fn () => new ReadOnlyClass(123)); + + $this->assertSame(123, $proxy->foo); } public function testLazyDecoratorClass() diff --git a/Tests/ProxyHelperTest.php b/Tests/ProxyHelperTest.php index 7745a77..ec44bc4 100644 --- a/Tests/ProxyHelperTest.php +++ b/Tests/ProxyHelperTest.php @@ -66,15 +66,12 @@ public function testGenerateLazyProxy() { use \Symfony\Component\VarExporter\LazyProxyTrait; - private const LAZY_OBJECT_PROPERTY_SCOPES = [ - 'lazyObjectReal' => [self::class, 'lazyObjectReal', null], - "\0".self::class."\0lazyObjectReal" => [self::class, 'lazyObjectReal', null], - ]; + private const LAZY_OBJECT_PROPERTY_SCOPES = []; public function foo1(): ?\Symfony\Component\VarExporter\Tests\Bar { - if (isset($this->lazyObjectReal)) { - return $this->lazyObjectReal->foo1(...\func_get_args()); + if (isset($this->lazyObjectState)) { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->foo1(...\func_get_args()); } return parent::foo1(...\func_get_args()); @@ -82,8 +79,8 @@ public function foo1(): ?\Symfony\Component\VarExporter\Tests\Bar public function foo4(\Symfony\Component\VarExporter\Tests\Bar|string $b): void { - if (isset($this->lazyObjectReal)) { - $this->lazyObjectReal->foo4(...\func_get_args()); + if (isset($this->lazyObjectState)) { + ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->foo4(...\func_get_args()); } else { parent::foo4(...\func_get_args()); } @@ -91,8 +88,8 @@ public function foo4(\Symfony\Component\VarExporter\Tests\Bar|string $b): void protected function foo7() { - if (isset($this->lazyObjectReal)) { - return $this->lazyObjectReal->foo7(...\func_get_args()); + if (isset($this->lazyObjectState)) { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->foo7(...\func_get_args()); } return throw new \BadMethodCallException('Cannot forward abstract method "Symfony\Component\VarExporter\Tests\TestForProxyHelper::foo7()".'); @@ -116,15 +113,12 @@ public function testGenerateLazyProxyForInterfaces() { use \Symfony\Component\VarExporter\LazyProxyTrait; - private const LAZY_OBJECT_PROPERTY_SCOPES = [ - 'lazyObjectReal' => [self::class, 'lazyObjectReal', null], - "\0".self::class."\0lazyObjectReal" => [self::class, 'lazyObjectReal', null], - ]; + private const LAZY_OBJECT_PROPERTY_SCOPES = []; public function initializeLazyObject(): \Symfony\Component\VarExporter\Tests\TestForProxyHelperInterface1&\Symfony\Component\VarExporter\Tests\TestForProxyHelperInterface2 { - if (isset($this->lazyObjectReal)) { - return $this->lazyObjectReal; + if ($state = $this->lazyObjectState ?? null) { + return $state->realInstance ??= ($state->initializer)(); } return $this; @@ -132,8 +126,8 @@ public function initializeLazyObject(): \Symfony\Component\VarExporter\Tests\Tes public function foo1(): ?\Symfony\Component\VarExporter\Tests\Bar { - if (isset($this->lazyObjectReal)) { - return $this->lazyObjectReal->foo1(...\func_get_args()); + if (isset($this->lazyObjectState)) { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->foo1(...\func_get_args()); } return throw new \BadMethodCallException('Cannot forward abstract method "Symfony\Component\VarExporter\Tests\TestForProxyHelperInterface1::foo1()".'); @@ -141,8 +135,8 @@ public function foo1(): ?\Symfony\Component\VarExporter\Tests\Bar public function foo2(?\Symfony\Component\VarExporter\Tests\Bar $b): \Symfony\Component\VarExporter\Tests\TestForProxyHelperInterface2 { - if (isset($this->lazyObjectReal)) { - return $this->lazyObjectReal->foo2(...\func_get_args()); + if (isset($this->lazyObjectState)) { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->foo2(...\func_get_args()); } return throw new \BadMethodCallException('Cannot forward abstract method "Symfony\Component\VarExporter\Tests\TestForProxyHelperInterface2::foo2()".'); @@ -170,8 +164,8 @@ public function testAttributes() public function foo(#[\SensitiveParameter] $a): int { - if (isset($this->lazyObjectReal)) { - return $this->lazyObjectReal->foo(...\func_get_args()); + if (isset($this->lazyObjectState)) { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->foo(...\func_get_args()); } return parent::foo(...\func_get_args()); From a50208393fcb72def51c8e86773ffa2b7cbb2baf Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Wed, 14 Dec 2022 15:42:16 +0100 Subject: [PATCH 3/7] Migrate to `static` data providers using `rector/rector` --- Tests/InstantiatorTest.php | 2 +- Tests/VarExporterTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/InstantiatorTest.php b/Tests/InstantiatorTest.php index cbd2236..ec6fc98 100644 --- a/Tests/InstantiatorTest.php +++ b/Tests/InstantiatorTest.php @@ -35,7 +35,7 @@ public function testFailingInstantiation(string $class) Instantiator::instantiate($class); } - public function provideFailingInstantiation() + public static function provideFailingInstantiation() { yield ['ReflectionClass']; yield ['SplHeap']; diff --git a/Tests/VarExporterTest.php b/Tests/VarExporterTest.php index a4ea1a9..21320e1 100644 --- a/Tests/VarExporterTest.php +++ b/Tests/VarExporterTest.php @@ -53,7 +53,7 @@ public function testFailingSerialization($value) } } - public function provideFailingSerialization() + public static function provideFailingSerialization() { yield [hash_init('md5')]; yield [new \ReflectionClass(\stdClass::class)]; @@ -122,7 +122,7 @@ public function testExport(string $testName, $value, bool $staticValueExpected = } } - public function provideExport() + public static function provideExport() { yield ['multiline-string', ["\0\0\r\nA" => "B\rC\n\n"], true]; yield ['lf-ending-string', "'BOOM'\n.var_dump(123)//'", true]; From 2987f5ac8fbf7e4585bc52728d6309e273de8e19 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 14 Feb 2023 09:53:37 +0100 Subject: [PATCH 4/7] Fix merge --- Tests/LazyGhostTraitTest.php | 2 +- Tests/ProxyHelperTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/LazyGhostTraitTest.php b/Tests/LazyGhostTraitTest.php index 8be310c..ae989ad 100644 --- a/Tests/LazyGhostTraitTest.php +++ b/Tests/LazyGhostTraitTest.php @@ -149,7 +149,7 @@ public function testMagicClass(MagicClass $instance) $this->assertSame(123, $clone->bar); } - public function provideMagicClass() + public static function provideMagicClass() { yield [new MagicClass()]; diff --git a/Tests/ProxyHelperTest.php b/Tests/ProxyHelperTest.php index ec44bc4..557d11c 100644 --- a/Tests/ProxyHelperTest.php +++ b/Tests/ProxyHelperTest.php @@ -26,7 +26,7 @@ public function testExportSignature(string $expected, \ReflectionMethod $method) $this->assertSame($expected, ProxyHelper::exportSignature($method)); } - public function provideExportSignature() + public static function provideExportSignature() { $methods = (new \ReflectionClass(TestForProxyHelper::class))->getMethods(); $source = file(__FILE__); From be74908a6942fdd331554b3cec27ff41b45ccad4 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 21 Feb 2023 17:34:40 +0100 Subject: [PATCH 5/7] Fix phpdocs in components --- VarExporter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VarExporter.php b/VarExporter.php index 003388e..8581337 100644 --- a/VarExporter.php +++ b/VarExporter.php @@ -34,7 +34,7 @@ final class VarExporter * * @param mixed $value The value to export * @param bool &$isStaticValue Set to true after execution if the provided value is static, false otherwise - * @param bool &$classes Classes found in the value are added to this list as both keys and values + * @param array &$foundClasses Classes found in the value are added to this list as both keys and values * * @throws ExceptionInterface When the provided value cannot be serialized */ From 8302bb670204500d492c6b8c595ee9a27da62cd6 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 14 Mar 2023 16:48:45 +0100 Subject: [PATCH 6/7] Fix some Composer keywords --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 67c4a27..83140ee 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "symfony/var-exporter", "type": "library", "description": "Allows exporting any serializable PHP data structure to plain PHP code", - "keywords": ["export", "serialize", "instantiate", "hydrate", "construct", "clone", "lazy loading", "proxy"], + "keywords": ["export", "serialize", "instantiate", "hydrate", "construct", "clone", "lazy-loading", "proxy"], "homepage": "https://symfony.com", "license": "MIT", "authors": [ From 9a07920c2058bafee921ce4d90aeef2193837d63 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 21 Apr 2023 10:04:17 +0200 Subject: [PATCH 7/7] [VarExporter] Fix forwarding references to proxied classes --- ProxyHelper.php | 23 ++++++++++++++++++----- Tests/ProxyHelperTest.php | 14 +++++++------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/ProxyHelper.php b/ProxyHelper.php index 5ae3892..2e150cb 100644 --- a/ProxyHelper.php +++ b/ProxyHelper.php @@ -142,15 +142,15 @@ public static function generateLazyProxy(?\ReflectionClass $class, array $interf continue; } - $signature = self::exportSignature($method); - $parentCall = $method->isAbstract() ? "throw new \BadMethodCallException('Cannot forward abstract method \"{$method->class}::{$method->name}()\".')" : "parent::{$method->name}(...\\func_get_args())"; + $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 = <<lazyObjectState)) { - (\$this->lazyObjectState->realInstance ??= (\$this->lazyObjectState->initializer)())->{$method->name}(...\\func_get_args()); + (\$this->lazyObjectState->realInstance ??= (\$this->lazyObjectState->initializer)())->{$method->name}({$args}); } else { {$parentCall}; } @@ -172,7 +172,7 @@ public static function generateLazyProxy(?\ReflectionClass $class, array $interf $body = <<lazyObjectState)) { - return (\$this->lazyObjectState->realInstance ??= (\$this->lazyObjectState->initializer)())->{$method->name}(...\\func_get_args()); + return (\$this->lazyObjectState->realInstance ??= (\$this->lazyObjectState->initializer)())->{$method->name}({$args}); } return {$parentCall}; @@ -213,8 +213,11 @@ class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); EOPHP; } - public static function exportSignature(\ReflectionFunctionAbstract $function, bool $withParameterTypes = true): string + public static function exportSignature(\ReflectionFunctionAbstract $function, bool $withParameterTypes = true, string &$args = null): string { + $hasByRef = false; + $args = ''; + $param = null; $parameters = []; foreach ($function->getParameters() as $param) { $parameters[] = ($param->getAttributes(\SensitiveParameter::class) ? '#[\SensitiveParameter] ' : '') @@ -222,6 +225,16 @@ public static function exportSignature(\ReflectionFunctionAbstract $function, bo .($param->isPassedByReference() ? '&' : '') .($param->isVariadic() ? '...' : '').'$'.$param->name .($param->isOptional() && !$param->isVariadic() ? ' = '.self::exportDefault($param) : ''); + $hasByRef = $hasByRef || $param->isPassedByReference(); + $args .= ($param->isVariadic() ? '...$' : '$').$param->name.', '; + } + + if (!$param || !$hasByRef) { + $args = '...\func_get_args()'; + } elseif ($param->isVariadic()) { + $args = substr($args, 0, -2); + } else { + $args .= sprintf('...\array_slice(\func_get_args(), %d)', \count($parameters)); } $signature = 'function '.($function->returnsReference() ? '&' : '') diff --git a/Tests/ProxyHelperTest.php b/Tests/ProxyHelperTest.php index 557d11c..806c5c9 100644 --- a/Tests/ProxyHelperTest.php +++ b/Tests/ProxyHelperTest.php @@ -77,12 +77,12 @@ public function foo1(): ?\Symfony\Component\VarExporter\Tests\Bar return parent::foo1(...\func_get_args()); } - public function foo4(\Symfony\Component\VarExporter\Tests\Bar|string $b): void + public function foo4(\Symfony\Component\VarExporter\Tests\Bar|string $b, &$d): void { if (isset($this->lazyObjectState)) { - ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->foo4(...\func_get_args()); + ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->foo4($b, $d, ...\array_slice(\func_get_args(), 2)); } else { - parent::foo4(...\func_get_args()); + parent::foo4($b, $d, ...\array_slice(\func_get_args(), 2)); } } @@ -133,7 +133,7 @@ public function foo1(): ?\Symfony\Component\VarExporter\Tests\Bar return throw new \BadMethodCallException('Cannot forward abstract method "Symfony\Component\VarExporter\Tests\TestForProxyHelperInterface1::foo1()".'); } - public function foo2(?\Symfony\Component\VarExporter\Tests\Bar $b): \Symfony\Component\VarExporter\Tests\TestForProxyHelperInterface2 + public function foo2(?\Symfony\Component\VarExporter\Tests\Bar $b, ...$d): \Symfony\Component\VarExporter\Tests\TestForProxyHelperInterface2 { if (isset($this->lazyObjectState)) { return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->foo2(...\func_get_args()); @@ -196,7 +196,7 @@ public function foo1(): ?Bar { } - public function foo2(?Bar $b): ?self + public function foo2(?Bar $b, ...$d): ?self { } @@ -204,7 +204,7 @@ public function &foo3(Bar &$b, string &...$c) { } - public function foo4(Bar|string $b): void + public function foo4(Bar|string $b, &$d): void { } @@ -234,7 +234,7 @@ public function foo1(): ?Bar; interface TestForProxyHelperInterface2 { - public function foo2(?Bar $b): self; + public function foo2(?Bar $b, ...$d): self; public static function foo3(): string; }