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 5, 2024
1 parent 4baf322 commit 0cc8d98
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 2 deletions.
13 changes: 13 additions & 0 deletions src/Core/src/Exception/Container/RecursiveProxyException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Spiral\Core\Exception\Container;

/**
* Recursion can occur due to improper container configuration or
* an unplanned exit from the scope by the execution thread.
*/
class RecursiveProxyException extends ContainerException
{
}
5 changes: 5 additions & 0 deletions src/Core/src/Internal/Proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,9 @@ public static function create(

return $instance;
}

public static function isProxy(object $object): bool
{
return \in_array($object::class, self::$classes, true);
}
}
24 changes: 22 additions & 2 deletions src/Core/src/Internal/Proxy/Resolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
use Psr\Container\ContainerInterface;
use Spiral\Core\ContainerScope;
use Spiral\Core\Exception\Container\ContainerException;
use Spiral\Core\Exception\Container\RecursiveProxyException;
use Spiral\Core\Internal\Introspector;
use Spiral\Core\Internal\Proxy;

/**
* @internal
Expand All @@ -16,7 +19,7 @@ final class Resolver
public static function resolve(
string $alias,
\Stringable|string|null $context = null,
?ContainerInterface $c = null
?ContainerInterface $c = null,
): object {
$c ??= ContainerScope::getContainer() ?? throw new ContainerException('Proxy is out of scope.');

Expand All @@ -27,11 +30,28 @@ public static function resolve(
);
} catch (\Throwable $e) {
throw new ContainerException(
\sprintf('Unable to resolve `%s` in a Proxy.', $alias),
\sprintf('Unable to resolve `%s` in a Proxy in `%s` scope.', $alias, self::getScope($c)),
previous: $e,
);
}

if (Proxy::isProxy($result)) {
throw new RecursiveProxyException(
\sprintf('Recursive proxy detected for `%s` in `%s` scope.', $alias, self::getScope($c)),
);
}

return $result;
}

/**
* @return non-empty-string
*/
private static function getScope(ContainerInterface $c): string
{
return \implode('.', \array_reverse(\array_map(
static fn(?string $name): string => $name ?? 'null',
Introspector::scopeNames($c),
)));
}
}
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\Exception\Container\RecursiveProxyException;
use Spiral\Core\Scope;
use Spiral\Tests\Core\Scope\Stub\Context;
use Spiral\Tests\Core\Scope\Stub\ContextInterface;
Expand Down Expand Up @@ -296,6 +297,25 @@ public function __toString(): string
);
}

/**
* Proxy gets a proxy of the same type.
*/
public function testRecursiveProxy(): void
{
$root = new Container();
$root->bind(UserInterface::class, new \Spiral\Core\Config\Proxy(UserInterface::class));

$this->expectException(RecursiveProxyException::class);
$this->expectExceptionMessage(
'Recursive proxy detected for `Spiral\Tests\Core\Scope\Stub\UserInterface` in `root.null` scope.',
);

$root->runScope(
new Scope(),
fn(#[Proxy] UserInterface $user) => $user->getName(),
);
}

/*
// Proxy::$attachContainer=true tests
Expand Down

0 comments on commit 0cc8d98

Please sign in to comment.