Skip to content

Commit 657473d

Browse files
authored
Merge pull request #814 from patchlevel/use-type-info-in-argument-resolver
use type info in argument resolver
2 parents 841b1f3 + 3800afe commit 657473d

15 files changed

Lines changed: 63 additions & 92 deletions

docs/subscription.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ final class DoStuffSubscriber
219219
}
220220
}
221221
```
222+
223+
:::tip
224+
You can also subscribe to multiple events and specify your argument using union type.
225+
:::
226+
222227
##### Lookup Resolver
223228

224229
Sometimes you need to query previous events to build a projection.

phpstan-baseline.neon

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -294,12 +294,6 @@ parameters:
294294
count: 1
295295
path: tests/Integration/Subscription/SubscriptionTest.php
296296

297-
-
298-
message: '#^Instantiated class Patchlevel\\EventSourcing\\Tests\\Integration\\Subscription\\MigrateAggregateToStreamStoreSubscriber not found\.$#'
299-
identifier: class.notFound
300-
count: 1
301-
path: tests/Integration/Subscription/SubscriptionTest.php
302-
303297
-
304298
message: '#^Parameter \#4 \$maxAttempts of class Patchlevel\\EventSourcing\\Subscription\\RetryStrategy\\ClockBasedRetryStrategy constructor expects int\<1, max\>, 0 given\.$#'
305299
identifier: argument.type
@@ -433,7 +427,7 @@ parameters:
433427
path: tests/Unit/Message/Translator/ReplaceEventTranslatorTest.php
434428

435429
-
436-
message: '#^Method class@anonymous/tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest\.php\:236\:\:profileVisited\(\) has parameter \$message with no type specified\.$#'
430+
message: '#^Method class@anonymous/tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest\.php\:234\:\:profileVisited\(\) has parameter \$message with no type specified\.$#'
437431
identifier: missingType.parameter
438432
count: 1
439433
path: tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest.php

src/Metadata/Subscriber/ArgumentMetadata.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44

55
namespace Patchlevel\EventSourcing\Metadata\Subscriber;
66

7+
use Symfony\Component\TypeInfo\Type;
8+
79
final class ArgumentMetadata
810
{
911
public function __construct(
1012
public readonly string $name,
11-
public readonly string $type,
12-
public readonly bool $allowsNull = false,
13+
public readonly Type $type,
1314
) {
1415
}
1516
}

src/Metadata/Subscriber/ArgumentTypeNotSupported.php

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,4 @@ public static function missingType(string $class, string $method, string $argume
2121
),
2222
);
2323
}
24-
25-
public static function onlyNamedTypeSupported(string $class, string $method, string $argumentName): self
26-
{
27-
return new self(
28-
sprintf(
29-
'Argument type for method "%s" in class "%s" is not supported. Argument "%s" must not have a union or intersection type.',
30-
$method,
31-
$class,
32-
$argumentName,
33-
),
34-
);
35-
}
3624
}

src/Metadata/Subscriber/AttributeSubscriberMetadataFactory.php

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,20 @@
1414
use ReflectionAttribute;
1515
use ReflectionClass;
1616
use ReflectionMethod;
17-
use ReflectionNamedType;
17+
use Symfony\Component\TypeInfo\TypeResolver\TypeResolver;
1818

1919
use function array_key_exists;
2020
use function count;
2121

2222
final class AttributeSubscriberMetadataFactory implements SubscriberMetadataFactory
2323
{
24+
private readonly TypeResolver $typeResolver;
25+
26+
public function __construct()
27+
{
28+
$this->typeResolver = TypeResolver::create();
29+
}
30+
2431
/** @var array<class-string, SubscriberMetadata> */
2532
private array $subscriberMetadata = [];
2633

@@ -171,18 +178,9 @@ private function subscribeMethod(ReflectionMethod $method): SubscribeMethodMetad
171178
);
172179
}
173180

174-
if (!$type instanceof ReflectionNamedType) {
175-
throw ArgumentTypeNotSupported::onlyNamedTypeSupported(
176-
$method->getDeclaringClass()->getName(),
177-
$method->getName(),
178-
$parameter->getName(),
179-
);
180-
}
181-
182181
$arguments[] = new ArgumentMetadata(
183182
$parameter->getName(),
184-
$type->getName(),
185-
$parameter->allowsNull(),
183+
$this->typeResolver->resolve($type),
186184
);
187185
}
188186

src/Subscription/Subscriber/ArgumentResolver/EventArgumentResolver.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77
use Patchlevel\EventSourcing\Message\Message;
88
use Patchlevel\EventSourcing\Metadata\Subscriber\ArgumentMetadata;
99

10-
use function class_exists;
11-
use function is_a;
12-
1310
final class EventArgumentResolver implements ArgumentResolver
1411
{
1512
public function resolve(ArgumentMetadata $argument, Message $message): object
@@ -19,6 +16,6 @@ public function resolve(ArgumentMetadata $argument, Message $message): object
1916

2017
public function support(ArgumentMetadata $argument, string $eventClass): bool
2118
{
22-
return class_exists($argument->type) && is_a($eventClass, $argument->type, true);
19+
return $argument->type->isIdentifiedBy($eventClass);
2320
}
2421
}

src/Subscription/Subscriber/ArgumentResolver/LookupResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ public function resolve(ArgumentMetadata $argument, Message $message): Lookup
2929

3030
public function support(ArgumentMetadata $argument, string $eventClass): bool
3131
{
32-
return $argument->type === Lookup::class;
32+
return $argument->type->isIdentifiedBy(Lookup::class);
3333
}
3434
}

src/Subscription/Subscriber/ArgumentResolver/MessageArgumentResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ public function resolve(ArgumentMetadata $argument, Message $message): Message
1616

1717
public function support(ArgumentMetadata $argument, string $eventClass): bool
1818
{
19-
return $argument->type === Message::class;
19+
return $argument->type->isIdentifiedBy(Message::class);
2020
}
2121
}

src/Subscription/Subscriber/ArgumentResolver/RecordedOnArgumentResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ public function resolve(ArgumentMetadata $argument, Message $message): DateTimeI
1818

1919
public function support(ArgumentMetadata $argument, string $eventClass): bool
2020
{
21-
return $argument->type === DateTimeImmutable::class;
21+
return $argument->type->isIdentifiedBy(DateTimeImmutable::class);
2222
}
2323
}

tests/Unit/Metadata/Subscriber/AttributeSubscriberMetadataFactoryTest.php

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileVisited;
2525
use PHPUnit\Framework\Attributes\CoversClass;
2626
use PHPUnit\Framework\TestCase;
27-
use Stringable;
27+
use Symfony\Component\TypeInfo\Type;
2828

2929
#[CoversClass(AttributeSubscriberMetadataFactory::class)]
3030
final class AttributeSubscriberMetadataFactoryTest extends TestCase
@@ -189,14 +189,14 @@ public function profileCreated(ProfileCreated $profileCreated, string $aggregate
189189
[
190190
ProfileVisited::class => new SubscribeMethodMetadata(
191191
'profileVisited',
192-
[new ArgumentMetadata('message', Message::class)],
192+
[new ArgumentMetadata('message', Type::object(Message::class))],
193193
),
194194

195195
ProfileCreated::class => new SubscribeMethodMetadata(
196196
'profileCreated',
197197
[
198-
new ArgumentMetadata('profileCreated', ProfileCreated::class),
199-
new ArgumentMetadata('aggregateId', 'string'),
198+
new ArgumentMetadata('profileCreated', Type::object(ProfileCreated::class)),
199+
new ArgumentMetadata('aggregateId', Type::string()),
200200
],
201201
),
202202
],
@@ -220,7 +220,7 @@ public function profileVisited(ProfileVisited|null $message): void
220220
self::assertEquals(
221221
[
222222
ProfileVisited::class => new SubscribeMethodMetadata('profileVisited', [
223-
new ArgumentMetadata('message', ProfileVisited::class, true),
223+
new ArgumentMetadata('message', Type::nullable(Type::object(ProfileVisited::class))),
224224
]),
225225
],
226226
$metadata->subscribeMethods,
@@ -245,38 +245,6 @@ public function profileVisited($message): void
245245
$metadataFactory->metadata($subscriber::class);
246246
}
247247

248-
public function testUnionTypeNotSupported(): void
249-
{
250-
$this->expectException(ArgumentTypeNotSupported::class);
251-
252-
$subscriber = new #[Subscriber('foo', RunMode::FromBeginning)]
253-
class {
254-
#[Subscribe(ProfileVisited::class)]
255-
public function profileVisited(ProfileVisited|ProfileCreated $event): void
256-
{
257-
}
258-
};
259-
260-
$metadataFactory = new AttributeSubscriberMetadataFactory();
261-
$metadataFactory->metadata($subscriber::class);
262-
}
263-
264-
public function testIntersectionTypeNotSupported(): void
265-
{
266-
$this->expectException(ArgumentTypeNotSupported::class);
267-
268-
$subscriber = new #[Subscriber('foo', RunMode::FromBeginning)]
269-
class {
270-
#[Subscribe(ProfileVisited::class)]
271-
public function profileVisited(ProfileVisited&Stringable $event): void
272-
{
273-
}
274-
};
275-
276-
$metadataFactory = new AttributeSubscriberMetadataFactory();
277-
$metadataFactory->metadata($subscriber::class);
278-
}
279-
280248
public function testSubscribeAllWithExplicitSubscribeMethod(): void
281249
{
282250
$this->expectException(DuplicateSubscribeMethod::class);

0 commit comments

Comments
 (0)