diff --git a/src/Core/src/ContainerScope.php b/src/Core/src/ContainerScope.php index d925ded86..85030f114 100644 --- a/src/Core/src/ContainerScope.php +++ b/src/Core/src/ContainerScope.php @@ -6,6 +6,8 @@ use Fiber; use Psr\Container\ContainerInterface; +use Spiral\Core\Exception\Container\ContainerException; +use Spiral\Core\Internal\Proxy; use Throwable; /** @@ -32,7 +34,12 @@ public static function getContainer(): ?ContainerInterface */ public static function runScope(ContainerInterface $container, callable $scope): mixed { - [$previous, self::$container] = [self::$container, $container]; + if (Proxy::isProxy($container)) { + // Ignore Proxy to avoid recursion + $container = $previous = self::$container ?? throw new ContainerException('Proxy is out of scope.'); + } else { + [$previous, self::$container] = [self::$container, $container]; + } try { if (Fiber::getCurrent() === null) { diff --git a/src/Core/src/Internal/Proxy/Resolver.php b/src/Core/src/Internal/Proxy/Resolver.php index 94b90d270..2e0699594 100644 --- a/src/Core/src/Internal/Proxy/Resolver.php +++ b/src/Core/src/Internal/Proxy/Resolver.php @@ -5,6 +5,7 @@ namespace Spiral\Core\Internal\Proxy; use Psr\Container\ContainerInterface; +use Spiral\Core\Container; use Spiral\Core\ContainerScope; use Spiral\Core\Exception\Container\ContainerException; use Spiral\Core\Exception\Container\RecursiveProxyException; @@ -29,15 +30,21 @@ public static function resolve( 'Resolved `null` from the container.', ); } catch (\Throwable $e) { + $scope = self::getScope($c); throw new ContainerException( - \sprintf('Unable to resolve `%s` in a Proxy in `%s` scope.', $alias, self::getScope($c)), + $scope === null + ? "Unable to resolve `{$alias}` in a Proxy." + : "Unable to resolve `{$alias}` in a Proxy in `{$scope}` scope.", previous: $e, ); } if (Proxy::isProxy($result)) { + $scope = self::getScope($c); throw new RecursiveProxyException( - \sprintf('Recursive proxy detected for `%s` in `%s` scope.', $alias, self::getScope($c)), + $scope === null + ? "Recursive proxy detected for `{$alias}`." + : "Recursive proxy detected for `{$alias}` in `{$scope}` scope.", ); } @@ -45,10 +52,18 @@ public static function resolve( } /** - * @return non-empty-string + * @return non-empty-string|null */ - private static function getScope(ContainerInterface $c): string + private static function getScope(ContainerInterface $c): ?string { + if (!$c instanceof Container) { + if (!Proxy::isProxy($c)) { + return null; + } + + $c = null; + } + return \implode('.', \array_reverse(\array_map( static fn (?string $name): string => $name ?? 'null', Introspector::scopeNames($c), diff --git a/src/Core/tests/Scope/ProxyTest.php b/src/Core/tests/Scope/ProxyTest.php index 00a17de14..fd2ddb43b 100644 --- a/src/Core/tests/Scope/ProxyTest.php +++ b/src/Core/tests/Scope/ProxyTest.php @@ -9,6 +9,7 @@ use Spiral\Core\Attribute\Proxy; use Spiral\Core\Container; use Spiral\Core\Container\InjectorInterface; +use Spiral\Core\ContainerScope; use Spiral\Core\Exception\Container\RecursiveProxyException; use Spiral\Core\Scope; use Spiral\Tests\Core\Scope\Stub\Context; @@ -316,6 +317,25 @@ public function testRecursiveProxy(): void ); } + /** + * The {@see ContainerScope::runScope} ignores Container Proxy to avoid recursion. + */ + public function testProxyIntoContainerScope(): void + { + $root = new Container(); + + $root->runScope( + new Scope(), + static function (#[Proxy] ContainerInterface $proxy, ContainerInterface $scoped) { + self::assertNotSame($scoped, $proxy); + ContainerScope::runScope($proxy, static function (ContainerInterface $passed) use ($proxy, $scoped) { + self::assertNotSame($passed, $proxy); + self::assertSame($scoped, ContainerScope::getContainer()); + }); + }, + ); + } + /* // Proxy::$attachContainer=true tests