Skip to content

Commit 828941c

Browse files
committed
fix(container): ensure has() returns false on circular aliases to satisfy PSR-11
1 parent 44c91bd commit 828941c

2 files changed

Lines changed: 54 additions & 4 deletions

File tree

src/Container/SimpleContainer.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use function is_int;
1111
use function is_string;
1212
use Psr\Container\ContainerInterface;
13+
use Webmozart\Assert\Assert;
1314
use WebProject\PhpOpenApiMockServer\Container\Exception\ContainerException;
1415
use WebProject\PhpOpenApiMockServer\Container\Exception\NotFoundException;
1516

@@ -33,7 +34,8 @@ final class SimpleContainer implements ContainerInterface
3334

3435
public function get(string $id): mixed
3536
{
36-
$resolved = $this->resolveAlias($id);
37+
$resolved = $this->resolveAlias($id, true);
38+
Assert::string($resolved);
3739

3840
if (array_key_exists($resolved, $this->services)) {
3941
return $this->services[$resolved];
@@ -78,7 +80,11 @@ public function get(string $id): mixed
7880

7981
public function has(string $id): bool
8082
{
81-
$resolved = $this->resolveAlias($id);
83+
$resolved = $this->resolveAlias($id, false);
84+
85+
if (null === $resolved) {
86+
return false;
87+
}
8288

8389
return array_key_exists($resolved, $this->services) || isset($this->factories[$resolved]);
8490
}
@@ -142,13 +148,17 @@ public function setInvokableClass(string $name, string $class): void
142148
$this->factories[$name] = static fn (): object => new $class();
143149
}
144150

145-
private function resolveAlias(string $id): string
151+
private function resolveAlias(string $id, bool $throwOnCircular = true): ?string
146152
{
147153
$seen = [];
148154

149155
while (isset($this->aliases[$id])) {
150156
if (isset($seen[$id])) {
151-
throw new ContainerException("Circular alias detected for '{$id}'.");
157+
if ($throwOnCircular) {
158+
throw new ContainerException("Circular alias detected for '{$id}'.");
159+
}
160+
161+
return null;
152162
}
153163

154164
$seen[$id] = true;

tests/Unit/SimpleContainerTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WebProject\PhpOpenApiMockServer\Tests\Unit;
6+
7+
use Codeception\Test\Unit;
8+
use WebProject\PhpOpenApiMockServer\Container\Exception\ContainerException;
9+
use WebProject\PhpOpenApiMockServer\Container\SimpleContainer;
10+
use WebProject\PhpOpenApiMockServer\Tests\Support\UnitTester;
11+
12+
class SimpleContainerTest extends Unit
13+
{
14+
/**
15+
* @var UnitTester
16+
*/
17+
protected $tester;
18+
19+
public function testHasReturnsFalseOnCircularAlias(): void
20+
{
21+
$container = new SimpleContainer();
22+
$container->setAlias('a', 'b');
23+
$container->setAlias('b', 'a');
24+
25+
// Current behavior: this throws ContainerException
26+
// Desired behavior: this returns false
27+
self::assertFalse($container->has('a'));
28+
}
29+
30+
public function testGetThrowsOnCircularAlias(): void
31+
{
32+
$container = new SimpleContainer();
33+
$container->setAlias('a', 'b');
34+
$container->setAlias('b', 'a');
35+
36+
$this->expectException(ContainerException::class);
37+
$this->expectExceptionMessage("Circular alias detected for 'a'.");
38+
$container->get('a');
39+
}
40+
}

0 commit comments

Comments
 (0)