Skip to content

Commit 9dc1596

Browse files
committed
fix(container): support factories with 0, 1, or 2 arguments using reflection
1 parent 828941c commit 9dc1596

2 files changed

Lines changed: 105 additions & 1 deletion

File tree

src/Container/SimpleContainer.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,17 @@
55
namespace WebProject\PhpOpenApiMockServer\Container;
66

77
use function array_key_exists;
8+
use function class_exists;
9+
use Closure;
810
use function is_array;
911
use function is_callable;
1012
use function is_int;
13+
use function is_object;
1114
use function is_string;
1215
use Psr\Container\ContainerInterface;
16+
use ReflectionFunction;
17+
use ReflectionMethod;
18+
use function str_contains;
1319
use Webmozart\Assert\Assert;
1420
use WebProject\PhpOpenApiMockServer\Container\Exception\ContainerException;
1521
use WebProject\PhpOpenApiMockServer\Container\Exception\NotFoundException;
@@ -55,7 +61,21 @@ public function get(string $id): mixed
5561
throw new ContainerException("Factory for '{$resolved}' is not callable.");
5662
}
5763

58-
$instance = $factory($this, $resolved);
64+
$reflection = match (true) {
65+
$factory instanceof Closure => new ReflectionFunction($factory),
66+
is_string($factory) && !str_contains($factory, '::') => new ReflectionFunction($factory),
67+
is_string($factory) && str_contains($factory, '::') => new ReflectionMethod($factory),
68+
is_array($factory) => new ReflectionMethod($factory[0], $factory[1]),
69+
is_object($factory) => new ReflectionMethod($factory, '__invoke'),
70+
default => throw new ContainerException("Unsupported factory type for '{$resolved}'."),
71+
};
72+
73+
$params = $reflection->getNumberOfParameters();
74+
$instance = match (true) {
75+
0 === $params => $factory(),
76+
1 === $params => $factory($this),
77+
default => $factory($this, $resolved),
78+
};
5979

6080
if (isset($this->delegators[$resolved])) {
6181
foreach ($this->delegators[$resolved] as $delegator) {
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WebProject\PhpOpenApiMockServer\Tests\Unit;
6+
7+
use Codeception\Test\Unit;
8+
use Psr\Container\ContainerInterface;
9+
use stdClass;
10+
use WebProject\PhpOpenApiMockServer\Container\SimpleContainer;
11+
12+
class SimpleContainerFactoryTest extends Unit
13+
{
14+
public function test0ArgClosureFactory(): void
15+
{
16+
$container = new SimpleContainer();
17+
$container->setFactory('service', static function () {
18+
return new stdClass();
19+
});
20+
21+
$instance = $container->get('service');
22+
self::assertInstanceOf(stdClass::class, $instance);
23+
}
24+
25+
public function test1ArgClosureFactory(): void
26+
{
27+
$container = new SimpleContainer();
28+
$container->setFactory('service', function ($container) {
29+
$this->assertInstanceOf(ContainerInterface::class, $container);
30+
31+
return new stdClass();
32+
});
33+
34+
$instance = $container->get('service');
35+
self::assertInstanceOf(stdClass::class, $instance);
36+
}
37+
38+
public function test2ArgClosureFactory(): void
39+
{
40+
$container = new SimpleContainer();
41+
$container->setFactory('service', function ($container, $id) {
42+
$this->assertInstanceOf(ContainerInterface::class, $container);
43+
$this->assertSame('service', $id);
44+
45+
return new stdClass();
46+
});
47+
48+
$instance = $container->get('service');
49+
self::assertInstanceOf(stdClass::class, $instance);
50+
}
51+
52+
public function testNative0ArgFunction(): void
53+
{
54+
$container = new SimpleContainer();
55+
$container->setFactory('service', 'time');
56+
57+
$instance = $container->get('service');
58+
self::assertIsInt($instance);
59+
}
60+
61+
public function testInvokableClassFactory(): void
62+
{
63+
$container = new SimpleContainer();
64+
$container->setInvokableClass('service', stdClass::class);
65+
66+
$instance = $container->get('service');
67+
self::assertInstanceOf(stdClass::class, $instance);
68+
}
69+
70+
public function test1ArgInvokableObjectFactory(): void
71+
{
72+
$container = new SimpleContainer();
73+
$factory = new class {
74+
public function __invoke(ContainerInterface $container): stdClass
75+
{
76+
return new stdClass();
77+
}
78+
};
79+
$container->setFactory('service', $factory);
80+
81+
$instance = $container->get('service');
82+
self::assertInstanceOf(stdClass::class, $instance);
83+
}
84+
}

0 commit comments

Comments
 (0)