Skip to content

Commit 57c3fe7

Browse files
authored
Add QueueFactoryProvider (#263)
1 parent 0a3fafd commit 57c3fe7

File tree

2 files changed

+261
-0
lines changed

2 files changed

+261
-0
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Queue\Provider;
6+
7+
use BackedEnum;
8+
use Psr\Container\ContainerInterface;
9+
use Yiisoft\Definitions\Exception\InvalidConfigException;
10+
use Yiisoft\Factory\StrictFactory;
11+
use Yiisoft\Queue\QueueInterface;
12+
use Yiisoft\Queue\StringNormalizer;
13+
14+
use function array_key_exists;
15+
use function sprintf;
16+
17+
/**
18+
* This queue provider creates queue objects directly from definitions.
19+
*
20+
* @see https://github.com/yiisoft/definitions/
21+
* @see https://github.com/yiisoft/factory/
22+
*/
23+
final class QueueFactoryProvider implements QueueProviderInterface
24+
{
25+
/**
26+
* @psalm-var array<string, QueueInterface|null>
27+
*/
28+
private array $queues = [];
29+
30+
private readonly StrictFactory $factory;
31+
32+
/**
33+
* @param array $definitions Queue definitions indexed by queue names.
34+
* @param ContainerInterface|null $container Container to use for dependencies resolving.
35+
* @param bool $validate If definitions should be validated when set.
36+
*
37+
* @psalm-param array<string, mixed> $definitions
38+
*
39+
* @throws InvalidQueueConfigException
40+
*/
41+
public function __construct(
42+
array $definitions,
43+
?ContainerInterface $container = null,
44+
bool $validate = true,
45+
) {
46+
try {
47+
$this->factory = new StrictFactory($definitions, $container, $validate);
48+
} catch (InvalidConfigException $exception) {
49+
throw new InvalidQueueConfigException($exception->getMessage(), previous: $exception);
50+
}
51+
}
52+
53+
public function get(string|BackedEnum $name): QueueInterface
54+
{
55+
$name = StringNormalizer::normalize($name);
56+
57+
$queue = $this->getOrTryToCreate($name);
58+
if ($queue === null) {
59+
throw new QueueNotFoundException($name);
60+
}
61+
62+
return $queue;
63+
}
64+
65+
public function has(string|BackedEnum $name): bool
66+
{
67+
$name = StringNormalizer::normalize($name);
68+
return $this->factory->has($name);
69+
}
70+
71+
/**
72+
* @throws InvalidQueueConfigException
73+
*/
74+
private function getOrTryToCreate(string $name): ?QueueInterface
75+
{
76+
if (array_key_exists($name, $this->queues)) {
77+
return $this->queues[$name];
78+
}
79+
80+
if (!$this->factory->has($name)) {
81+
$this->queues[$name] = null;
82+
return null;
83+
}
84+
85+
try {
86+
$queue = $this->factory->create($name);
87+
} catch (InvalidConfigException $exception) {
88+
throw new InvalidQueueConfigException($exception->getMessage(), previous: $exception);
89+
}
90+
91+
if (!$queue instanceof QueueInterface) {
92+
throw new InvalidQueueConfigException(
93+
sprintf(
94+
'Queue must implement "%s". For queue "%s" got "%s" instead.',
95+
QueueInterface::class,
96+
$name,
97+
get_debug_type($queue),
98+
),
99+
);
100+
}
101+
102+
$this->queues[$name] = $queue;
103+
return $queue;
104+
}
105+
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Queue\Tests\Unit\Provider;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Yiisoft\Definitions\Reference;
9+
use Yiisoft\Queue\Adapter\AdapterInterface;
10+
use Yiisoft\Queue\Provider\InvalidQueueConfigException;
11+
use Yiisoft\Queue\Provider\QueueFactoryProvider;
12+
use Yiisoft\Queue\Provider\QueueNotFoundException;
13+
use Yiisoft\Queue\QueueInterface;
14+
use Yiisoft\Queue\Stubs\StubAdapter;
15+
use Yiisoft\Queue\Stubs\StubLoop;
16+
use Yiisoft\Queue\Stubs\StubQueue;
17+
use Yiisoft\Queue\Tests\Unit\Support\StringEnum;
18+
use Yiisoft\Test\Support\Container\SimpleContainer;
19+
20+
use function sprintf;
21+
22+
final class QueueFactoryProviderTest extends TestCase
23+
{
24+
public function testBase(): void
25+
{
26+
$provider = new QueueFactoryProvider(
27+
[
28+
'queue1' => StubQueue::class,
29+
],
30+
);
31+
32+
$queue = $provider->get('queue1');
33+
34+
$this->assertInstanceOf(StubQueue::class, $queue);
35+
$this->assertTrue($provider->has('queue1'));
36+
$this->assertFalse($provider->has('not-exist-queue'));
37+
}
38+
39+
public function testGetTwice(): void
40+
{
41+
$provider = new QueueFactoryProvider(
42+
[
43+
'queue1' => StubQueue::class,
44+
],
45+
);
46+
47+
$queue1 = $provider->get('queue1');
48+
$queue2 = $provider->get('queue1');
49+
50+
$this->assertSame($queue1, $queue2);
51+
}
52+
53+
public function testGetNotExistQueue(): void
54+
{
55+
$provider = new QueueFactoryProvider(
56+
[
57+
'queue1' => StubQueue::class,
58+
],
59+
);
60+
61+
$this->expectException(QueueNotFoundException::class);
62+
$this->expectExceptionMessage('Queue with name "not-exist-queue" not found.');
63+
$provider->get('not-exist-queue');
64+
}
65+
66+
public function testInvalidQueueConfig(): void
67+
{
68+
$definitions = [
69+
'queue1' => [
70+
'class' => StubQueue::class,
71+
'__construct()' => 'hello',
72+
],
73+
];
74+
75+
$this->expectException(InvalidQueueConfigException::class);
76+
$this->expectExceptionMessage(
77+
'Invalid definition: incorrect constructor arguments. Expected array, got string.',
78+
);
79+
new QueueFactoryProvider($definitions);
80+
}
81+
82+
public function testInvalidQueueConfigOnGet(): void
83+
{
84+
$provider = new QueueFactoryProvider(
85+
[
86+
'queue1' => StubLoop::class,
87+
],
88+
);
89+
90+
$this->expectException(InvalidQueueConfigException::class);
91+
$this->expectExceptionMessage(
92+
sprintf(
93+
'Queue must implement "%s". For queue "%s" got "%s" instead.',
94+
QueueInterface::class,
95+
'queue1',
96+
StubLoop::class,
97+
),
98+
);
99+
$provider->get('queue1');
100+
}
101+
102+
public function testGetHasByStringEnum(): void
103+
{
104+
$provider = new QueueFactoryProvider(
105+
[
106+
'red' => StubQueue::class,
107+
],
108+
);
109+
110+
$queue = $provider->get(StringEnum::RED);
111+
112+
$this->assertInstanceOf(StubQueue::class, $queue);
113+
$this->assertTrue($provider->has(StringEnum::RED));
114+
$this->assertFalse($provider->has(StringEnum::GREEN));
115+
}
116+
117+
public function testWithContainer(): void
118+
{
119+
$adapter = new StubAdapter();
120+
$container = new SimpleContainer([
121+
AdapterInterface::class => $adapter,
122+
]);
123+
124+
$provider = new QueueFactoryProvider(
125+
[
126+
'queue1' => [
127+
'class' => StubQueue::class,
128+
'__construct()' => [
129+
'adapter' => Reference::to(AdapterInterface::class),
130+
],
131+
],
132+
],
133+
$container,
134+
);
135+
136+
$queue = $provider->get('queue1');
137+
138+
$this->assertInstanceOf(StubQueue::class, $queue);
139+
$this->assertSame($adapter, $queue->getAdapter());
140+
}
141+
142+
public function testValidateFalse(): void
143+
{
144+
$provider = new QueueFactoryProvider(
145+
[
146+
'queue1' => [
147+
'class' => StubQueue::class,
148+
'__construct()' => 'hello',
149+
],
150+
],
151+
validate: false,
152+
);
153+
154+
$this->assertTrue($provider->has('queue1'));
155+
}
156+
}

0 commit comments

Comments
 (0)