From e458c401ac648c52ba622cf6942d503da591566e Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Thu, 7 Mar 2024 12:42:47 +0200 Subject: [PATCH] Add mixed param type in proxy --- .../src/Internal/Proxy/ProxyClassRenderer.php | 2 +- .../Internal/Proxy/ProxyClassRendererTest.php | 79 +++++++++++++------ src/Core/tests/Scope/ProxyTest.php | 22 ++++++ src/Core/tests/Scope/Stub/User.php | 23 ++++++ src/Core/tests/Scope/Stub/UserInterface.php | 12 +++ 5 files changed, 115 insertions(+), 23 deletions(-) create mode 100644 src/Core/tests/Scope/Stub/User.php create mode 100644 src/Core/tests/Scope/Stub/UserInterface.php diff --git a/src/Core/src/Internal/Proxy/ProxyClassRenderer.php b/src/Core/src/Internal/Proxy/ProxyClassRenderer.php index d053f87a3..0a1307cac 100644 --- a/src/Core/src/Internal/Proxy/ProxyClassRenderer.php +++ b/src/Core/src/Internal/Proxy/ProxyClassRenderer.php @@ -142,7 +142,7 @@ public static function renderParameter(\ReflectionParameter $param): string return \ltrim( \sprintf( '%s %s%s%s%s', - $param->hasType() ? self::renderParameterTypes($param->getType(), $param->getDeclaringClass()) : '', + $param->hasType() ? 'mixed' : '', $param->isPassedByReference() ? '&' : '', $param->isVariadic() ? '...' : '', '$' . $param->getName(), diff --git a/src/Core/tests/Internal/Proxy/ProxyClassRendererTest.php b/src/Core/tests/Internal/Proxy/ProxyClassRendererTest.php index c420bd84e..eb249fd62 100644 --- a/src/Core/tests/Internal/Proxy/ProxyClassRendererTest.php +++ b/src/Core/tests/Internal/Proxy/ProxyClassRendererTest.php @@ -6,6 +6,7 @@ use ArrayAccess; use Countable; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Spiral\Core\Attribute\Proxy; use Spiral\Core\Internal\Proxy\ProxyClassRenderer; @@ -50,41 +51,40 @@ public static function provideRenderParameter(): iterable yield [$from(fn($string) => 0), '$string']; yield [$from(fn($string = '') => 0), '$string = \'\'']; - yield [$from(fn(string $string = "\n\\'\"") => 0), "string \$string = '\n\\\\\\'\"'"]; - yield [$from(fn(string $string = '123') => 0), 'string $string = \'123\'']; - yield [$from(fn(string $string = self::STRING_CONST) => 0), 'string $string = self::STRING_CONST']; + yield [$from(fn(string $string = "\n\\'\"") => 0), "mixed \$string = '\n\\\\\\'\"'"]; + yield [$from(fn(string $string = '123') => 0), 'mixed $string = \'123\'']; + yield [$from(fn(string $string = self::STRING_CONST) => 0), 'mixed $string = self::STRING_CONST']; yield [ $from(fn(string $string = ProxyClassRendererTest::STRING_CONST) => 0), - 'string $string = \\' . self::class . '::STRING_CONST', + 'mixed $string = \\' . self::class . '::STRING_CONST', ]; - yield [$from(fn(string|int $string = self::INT_CONST) => 0), 'string|int $string = self::INT_CONST']; + yield [$from(fn(string|int $string = self::INT_CONST) => 0), 'mixed $string = self::INT_CONST']; yield [$from(fn(mixed $string = 42) => 0), 'mixed $string = 42']; - yield [$from(fn(int $string = 42) => 0), 'int $string = 42']; - yield [$from(fn(float $string = 42) => 0), 'float $string = 42.0']; - yield [$from(fn(?bool $string = false) => 0), '?bool $string = false']; - yield [$from(fn(bool|null $string = true) => 0), '?bool $string = true']; - yield [$from(fn(object $string = null) => 0), '?object $string = NULL']; - yield [$from(fn(iterable $string = null) => 0), '?iterable $string = NULL']; - yield [$from(fn(Countable&ArrayAccess $val) => 0), '\Countable&\ArrayAccess $val']; - yield [$from(fn(string ...$val) => 0), 'string ...$val']; - yield [$from(fn(string|int ...$val) => 0), 'string|int ...$val']; - yield [$from(fn(string|int &$link) => 0), 'string|int &$link']; - yield [$from(self::withSelf(...)), \sprintf('\%s $self = new self()', self::class)]; - yield [$from(fn(object $link = new \stdClass()) => 0), 'object $link = new \stdClass()']; + yield [$from(fn(int $string = 42) => 0), 'mixed $string = 42']; + yield [$from(fn(float $string = 42) => 0), 'mixed $string = 42.0']; + yield [$from(fn(?bool $string = false) => 0), 'mixed $string = false']; + yield [$from(fn(bool|null $string = true) => 0), 'mixed $string = true']; + yield [$from(fn(object $string = null) => 0), 'mixed $string = NULL']; + yield [$from(fn(iterable $string = null) => 0), 'mixed $string = NULL']; + yield [$from(fn(Countable&ArrayAccess $val) => 0), 'mixed $val']; + yield [$from(fn(string ...$val) => 0), 'mixed ...$val']; + yield [$from(fn(string|int ...$val) => 0), 'mixed ...$val']; + yield [$from(fn(string|int &$link) => 0), 'mixed &$link']; + yield [$from(self::withSelf(...)), 'mixed $self = new self()']; + yield [$from(fn(object $link = new \stdClass()) => 0), 'mixed $link = new \stdClass()']; yield [ $from(fn(#[Proxy] float|int|\stdClass|null $string = new \stdClass(1, 2, bar: "\n'zero")) => 0), - "\stdClass|int|float|null \$string = new \stdClass(1, 2, bar: '\n\'zero')", + "mixed \$string = new \stdClass(1, 2, bar: '\n\'zero')", ]; yield [ $from(fn(SimpleEnum $val = SimpleEnum::B) => 0), - \sprintf('\%s $val = \%s::B', SimpleEnum::class, SimpleEnum::class), + \sprintf('mixed $val = \%s::B', SimpleEnum::class), ]; } /** * @dataProvider provideRenderParameter * @covers ::renderParameter - * @covers ::renderParameterTypes */ public function testRenderParameter(\ReflectionParameter $param, $expected): void { @@ -98,9 +98,9 @@ public static function provideRenderMethod(): iterable #[ExpectedAttribute('public function test1(...$variadic)')] public function test1(...$variadic) {} - #[ExpectedAttribute('public function test2(string|int $string = self::INT_CONST): string|int')] + #[ExpectedAttribute('public function test2(mixed $string = self::INT_CONST): string|int')] public function test2(string|int $string = self::INT_CONST): string|int {} - #[ExpectedAttribute('public function test3(object $obj = new \stdClass(new \stdClass(), new \stdClass()))')] + #[ExpectedAttribute('public function test3(mixed $obj = new \stdClass(new \stdClass(), new \stdClass()))')] public function test3(object $obj = new stdClass(new stdClass(), new stdClass())) {} #[ExpectedAttribute('public function test4(): \\' . ProxyClassRendererTest::class)] public function test4(): ProxyClassRendererTest {} @@ -128,6 +128,41 @@ public function testRenderMethod(\ReflectionMethod $param, $expected): void self::assertSame($expected, $signature); } + #[DataProvider('provideRenderParameterTypes')] + public function testRenderParameterTypes(\ReflectionParameter $param, string $expected): void + { + $this->assertSame( + $expected, + ProxyClassRenderer::renderParameterTypes($param->getType(), $param->getDeclaringClass()) + ); + } + + /** + * @psalm-suppress UnusedClosureParam + */ + public static function provideRenderParameterTypes(): iterable + { + $from = static fn(\Closure $closure): \ReflectionParameter => new \ReflectionParameter($closure, 0); + + yield [$from(fn(string $string) => 0), 'string']; + yield [$from(fn(string|int $string) => 0), 'string|int']; + yield [$from(fn(mixed $string) => 0), 'mixed']; + yield [$from(fn(int $string) => 0), 'int']; + yield [$from(fn(float $string) => 0), 'float']; + yield [$from(fn(?bool $string) => 0), '?bool']; + yield [$from(fn(bool|null $string) => 0), '?bool']; + yield [$from(fn(object $string) => 0), 'object']; + yield [$from(fn(iterable $string) => 0), 'iterable']; + yield [$from(fn(Countable&ArrayAccess $val) => 0), '\Countable&\ArrayAccess']; + yield [$from(fn(string ...$val) => 0), 'string']; + yield [$from(fn(string|int ...$val) => 0), 'string|int']; + yield [$from(fn(string|int &$link) => 0), 'string|int']; + yield [$from(self::withSelf(...)), '\\' . self::class]; + yield [$from(fn(object $link) => 0), 'object']; + yield [$from(fn(#[Proxy] float|int|\stdClass|null $string) => 0), '\stdClass|int|float|null']; + yield [$from(fn(SimpleEnum $val) => 0), '\\' . SimpleEnum::class]; + } + private static function withSelf(self $self = new self()): void { } diff --git a/src/Core/tests/Scope/ProxyTest.php b/src/Core/tests/Scope/ProxyTest.php index e4b3c621e..2665220cf 100644 --- a/src/Core/tests/Scope/ProxyTest.php +++ b/src/Core/tests/Scope/ProxyTest.php @@ -18,6 +18,8 @@ use Spiral\Tests\Core\Scope\Stub\LoggerInterface; use Spiral\Tests\Core\Scope\Stub\ScopedProxyLoggerCarrier; use Spiral\Tests\Core\Scope\Stub\ScopedProxyStdClass; +use Spiral\Tests\Core\Scope\Stub\User; +use Spiral\Tests\Core\Scope\Stub\UserInterface; use WeakReference; final class ProxyTest extends BaseTestCase @@ -273,6 +275,26 @@ public function __destruct() self::assertFalse($context->destroyed); } + public function testImplementationWithWiderTypes(): void + { + $root = new Container(); + $root->getBinder('http')->bindSingleton(UserInterface::class, static fn () => new User('Foo')); + $proxy = $root->runScope(new Scope(), static fn(#[Proxy] UserInterface $proxy) => $proxy); + + $root->runScope( + new Scope('http'), + static function () use ($root, $proxy) { + self::assertSame('Foo', $proxy->getName()); + $proxy->setName(new class implements \Stringable { + public function __toString(): string + { + return 'Bar'; + } + }); + self::assertSame('Bar', $proxy->getName()); + } + ); + } /* // Proxy::$attachContainer=true tests diff --git a/src/Core/tests/Scope/Stub/User.php b/src/Core/tests/Scope/Stub/User.php new file mode 100644 index 000000000..fd178fdb8 --- /dev/null +++ b/src/Core/tests/Scope/Stub/User.php @@ -0,0 +1,23 @@ +name = (string) $name; + } + + public function getName(): string + { + return $this->name; + } +} diff --git a/src/Core/tests/Scope/Stub/UserInterface.php b/src/Core/tests/Scope/Stub/UserInterface.php new file mode 100644 index 000000000..cd840efbd --- /dev/null +++ b/src/Core/tests/Scope/Stub/UserInterface.php @@ -0,0 +1,12 @@ +