Skip to content

Commit

Permalink
Fix recursion in Container Proxy (#1140)
Browse files Browse the repository at this point in the history
  • Loading branch information
roxblnfk committed Sep 10, 2024
1 parent bbff436 commit 727e1a8
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 5 deletions.
9 changes: 8 additions & 1 deletion src/Core/src/ContainerScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use Fiber;
use Psr\Container\ContainerInterface;
use Spiral\Core\Exception\Container\ContainerException;
use Spiral\Core\Internal\Proxy;
use Throwable;

/**
Expand All @@ -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) {
Expand Down
23 changes: 19 additions & 4 deletions src/Core/src/Internal/Proxy/Resolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,26 +30,40 @@ public static function resolve(
'Resolved `null` from the container.',
);
} catch (\Throwable $e) {
$scope = self::getScope($c);

Check warning on line 33 in src/Core/src/Internal/Proxy/Resolver.php

View check run for this annotation

Codecov / codecov/patch

src/Core/src/Internal/Proxy/Resolver.php#L33

Added line #L33 was not covered by tests
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.",

Check warning on line 37 in src/Core/src/Internal/Proxy/Resolver.php

View check run for this annotation

Codecov / codecov/patch

src/Core/src/Internal/Proxy/Resolver.php#L35-L37

Added lines #L35 - L37 were not covered by tests
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}`."

Check warning on line 46 in src/Core/src/Internal/Proxy/Resolver.php

View check run for this annotation

Codecov / codecov/patch

src/Core/src/Internal/Proxy/Resolver.php#L46

Added line #L46 was not covered by tests
: "Recursive proxy detected for `{$alias}` in `{$scope}` scope.",
);
}

return $result;
}

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

Check warning on line 61 in src/Core/src/Internal/Proxy/Resolver.php

View check run for this annotation

Codecov / codecov/patch

src/Core/src/Internal/Proxy/Resolver.php#L60-L61

Added lines #L60 - L61 were not covered by tests
}

$c = null;

Check warning on line 64 in src/Core/src/Internal/Proxy/Resolver.php

View check run for this annotation

Codecov / codecov/patch

src/Core/src/Internal/Proxy/Resolver.php#L64

Added line #L64 was not covered by tests
}

return \implode('.', \array_reverse(\array_map(
static fn (?string $name): string => $name ?? 'null',
Introspector::scopeNames($c),
Expand Down
20 changes: 20 additions & 0 deletions src/Core/tests/Scope/ProxyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 727e1a8

Please sign in to comment.