Skip to content

Commit

Permalink
add argument resolver
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidBadura committed Mar 26, 2024
1 parent e3da365 commit 00d280c
Show file tree
Hide file tree
Showing 21 changed files with 320 additions and 63 deletions.
26 changes: 3 additions & 23 deletions baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,14 @@
</PossiblyNullPropertyFetch>
</file>
<file src="src/Subscription/Subscriber/MetadataSubscriberAccessor.php">
<MixedAssignment>
<code><![CDATA[$arguments[]]]></code>
</MixedAssignment>
<MixedMethodCall>
<code><![CDATA[$method]]></code>
<code><![CDATA[$method]]></code>
<code><![CDATA[$method]]></code>
</MixedMethodCall>
<MixedReturnTypeCoercion>
<code><![CDATA[$this->subscriber->$method(...)]]></code>
<code><![CDATA[Closure(Message):void]]></code>
</MixedReturnTypeCoercion>
</file>
<file src="tests/Benchmark/BasicImplementation/Profile.php">
<PropertyNotSetInConstructor>
Expand All @@ -134,15 +133,13 @@
</file>
<file src="tests/Benchmark/PersonalDataBench.php">
<MissingConstructor>
<code><![CDATA[$bus]]></code>
<code><![CDATA[$id]]></code>
<code><![CDATA[$repository]]></code>
<code><![CDATA[$store]]></code>
</MissingConstructor>
</file>
<file src="tests/Benchmark/SimpleSetupBench.php">
<MissingConstructor>
<code><![CDATA[$bus]]></code>
<code><![CDATA[$id]]></code>
<code><![CDATA[$repository]]></code>
<code><![CDATA[$store]]></code>
Expand All @@ -151,7 +148,6 @@
<file src="tests/Benchmark/SnapshotsBench.php">
<MissingConstructor>
<code><![CDATA[$adapter]]></code>
<code><![CDATA[$bus]]></code>
<code><![CDATA[$id]]></code>
<code><![CDATA[$repository]]></code>
<code><![CDATA[$snapshotStore]]></code>
Expand All @@ -163,15 +159,13 @@
<code><![CDATA[$this->id]]></code>
</ArgumentTypeCoercion>
<MissingConstructor>
<code><![CDATA[$bus]]></code>
<code><![CDATA[$id]]></code>
<code><![CDATA[$repository]]></code>
<code><![CDATA[$store]]></code>
</MissingConstructor>
</file>
<file src="tests/Benchmark/SubscriptionEngineBench.php">
<MissingConstructor>
<code><![CDATA[$bus]]></code>
<code><![CDATA[$id]]></code>
<code><![CDATA[$repository]]></code>
<code><![CDATA[$store]]></code>
Expand Down Expand Up @@ -202,13 +196,6 @@
<code><![CDATA[$name]]></code>
</PropertyNotSetInConstructor>
</file>
<file src="tests/Integration/Pipeline/Aggregate/Profile.php">
<PropertyNotSetInConstructor>
<code><![CDATA[$id]]></code>
<code><![CDATA[$privacy]]></code>
<code><![CDATA[$visited]]></code>
</PropertyNotSetInConstructor>
</file>
<file src="tests/Integration/Store/Profile.php">
<PropertyNotSetInConstructor>
<code><![CDATA[$id]]></code>
Expand Down Expand Up @@ -291,13 +278,6 @@
<code><![CDATA[$id]]></code>
</PropertyNotSetInConstructor>
</file>
<file src="tests/Unit/Pipeline/Source/InMemorySourceTest.php">
<InvalidMethodCall>
<code><![CDATA[current]]></code>
<code><![CDATA[current]]></code>
<code><![CDATA[next]]></code>
</InvalidMethodCall>
</file>
<file src="tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php">
<PossiblyUndefinedArrayOffset>
<code><![CDATA[$update1]]></code>
Expand Down
1 change: 1 addition & 0 deletions deptrac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ deptrac:
- Metadata
- Subscription
Subscription:
- Aggregate
- Attribute
- Clock
- Message
Expand Down
14 changes: 14 additions & 0 deletions src/Metadata/Subscriber/ArgumentMetadata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Metadata\Subscriber;

final class ArgumentMetadata
{
public function __construct(
public readonly string $name,
public readonly string $type,
) {
}
}
28 changes: 27 additions & 1 deletion src/Metadata/Subscriber/AttributeSubscriberMetadataFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
use Patchlevel\EventSourcing\Attribute\Teardown;
use ReflectionAttribute;
use ReflectionClass;
use ReflectionMethod;
use ReflectionNamedType;
use RuntimeException;

use function array_key_exists;

Expand Down Expand Up @@ -48,7 +51,7 @@ public function metadata(string $subscriber): SubscriberMetadata
$instance = $attribute->newInstance();
$eventClass = $instance->eventClass;

$subscribeMethods[$eventClass][] = $method->getName();
$subscribeMethods[$eventClass][] = $this->subscribeMethod($method);
}

if ($method->getAttributes(Setup::class)) {
Expand Down Expand Up @@ -91,4 +94,27 @@ public function metadata(string $subscriber): SubscriberMetadata

return $metadata;
}

private function subscribeMethod(ReflectionMethod $method): SubscribeMethodMetadata
{
$arguments = [];

foreach ($method->getParameters() as $parameter) {
$type = $parameter->getType();

if (!$type instanceof ReflectionNamedType) {
throw new RuntimeException('parameter type is required');
}

$arguments[] = new ArgumentMetadata(
$parameter->getName(),
$type->getName(),
);
}

return new SubscribeMethodMetadata(
$method->getName(),
$arguments,
);
}
}
15 changes: 15 additions & 0 deletions src/Metadata/Subscriber/SubscribeMethodMetadata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Metadata\Subscriber;

final class SubscribeMethodMetadata
{
/** @param list<ArgumentMetadata> $arguments */
public function __construct(
public readonly string $name,
public readonly array $arguments = [],
) {
}
}
2 changes: 1 addition & 1 deletion src/Metadata/Subscriber/SubscriberMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public function __construct(
public readonly string $id,
public readonly string $group = Subscription::DEFAULT_GROUP,
public readonly RunMode $runMode = RunMode::FromBeginning,
/** @var array<class-string|"*", list<string>> */
/** @var array<class-string|"*", list<SubscribeMethodMetadata>> */
public readonly array $subscribeMethods = [],
public readonly string|null $setupMethod = null,
public readonly string|null $teardownMethod = null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Subscription\Subscriber\ArgumentResolver;

use Patchlevel\EventSourcing\Aggregate\AggregateHeader;
use Patchlevel\EventSourcing\Message\Message;
use Patchlevel\EventSourcing\Metadata\Subscriber\ArgumentMetadata;

use function in_array;

final class AggregateIdArgumentResolver implements ArgumentResolver
{
public function resolve(ArgumentMetadata $argument, Message $message): string
{
return $message->header(AggregateHeader::class)->aggregateId;
}

public function support(ArgumentMetadata $argument, string $eventClass): bool
{
return $argument->type === 'string' && in_array($argument->name, ['aggregateId', 'aggregateRootId']);
}
}
15 changes: 15 additions & 0 deletions src/Subscription/Subscriber/ArgumentResolver/ArgumentResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Subscription\Subscriber\ArgumentResolver;

use Patchlevel\EventSourcing\Message\Message;
use Patchlevel\EventSourcing\Metadata\Subscriber\ArgumentMetadata;

interface ArgumentResolver
{
public function resolve(ArgumentMetadata $argument, Message $message): mixed;

public function support(ArgumentMetadata $argument, string $eventClass): bool;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Subscription\Subscriber\ArgumentResolver;

use Patchlevel\EventSourcing\Message\Message;
use Patchlevel\EventSourcing\Metadata\Subscriber\ArgumentMetadata;

use function class_exists;
use function is_a;

final class EventArgumentResolver implements ArgumentResolver
{
public function resolve(ArgumentMetadata $argument, Message $message): object
{
return $message->event();
}

public function support(ArgumentMetadata $argument, string $eventClass): bool
{
return class_exists($argument->type) && is_a($eventClass, $argument->type, true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Subscription\Subscriber\ArgumentResolver;

use Patchlevel\EventSourcing\Message\Message;
use Patchlevel\EventSourcing\Metadata\Subscriber\ArgumentMetadata;

final class MessageArgumentResolver implements ArgumentResolver
{
public function resolve(ArgumentMetadata $argument, Message $message): Message
{
return $message;
}

public function support(ArgumentMetadata $argument, string $eventClass): bool
{
return $argument->type === Message::class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Subscription\Subscriber\ArgumentResolver;

use DateTimeImmutable;
use Patchlevel\EventSourcing\Aggregate\AggregateHeader;
use Patchlevel\EventSourcing\Message\Message;
use Patchlevel\EventSourcing\Metadata\Subscriber\ArgumentMetadata;

final class RecordedOnArgumentResolver implements ArgumentResolver
{
public function resolve(ArgumentMetadata $argument, Message $message): DateTimeImmutable
{
return $message->header(AggregateHeader::class)->recordedOn;
}

public function support(ArgumentMetadata $argument, string $eventClass): bool
{
return $argument->type === DateTimeImmutable::class;
}
}
56 changes: 54 additions & 2 deletions src/Subscription/Subscriber/MetadataSubscriberAccessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
use Closure;
use Patchlevel\EventSourcing\Attribute\Subscribe;
use Patchlevel\EventSourcing\Message\Message;
use Patchlevel\EventSourcing\Metadata\Subscriber\SubscribeMethodMetadata;
use Patchlevel\EventSourcing\Metadata\Subscriber\SubscriberMetadata;
use Patchlevel\EventSourcing\Subscription\RunMode;
use Patchlevel\EventSourcing\Subscription\Subscriber\ArgumentResolver\ArgumentResolver;

use function array_key_exists;
use function array_map;
Expand All @@ -19,9 +21,11 @@ final class MetadataSubscriberAccessor implements SubscriberAccessor
/** @var array<class-string, list<Closure(Message):void>> */
private array $subscribeCache = [];

/** @param list<ArgumentResolver> $argumentResolvers */
public function __construct(
private readonly object $subscriber,
private readonly SubscriberMetadata $metadata,
private readonly array $argumentResolvers,
) {
}

Expand Down Expand Up @@ -79,11 +83,59 @@ public function subscribeMethods(string $eventClass): array
);

$this->subscribeCache[$eventClass] = array_map(
/** @return Closure(Message):void */
fn (string $method) => $this->subscriber->$method(...),
fn (SubscribeMethodMetadata $method): Closure => $this->createClosure($eventClass, $method),
$methods,
);

return $this->subscribeCache[$eventClass];
}

/**
* @param class-string $eventClass
*
* @return Closure(Message):void
*/
private function createClosure(string $eventClass, SubscribeMethodMetadata $method): Closure
{
$resolvers = $this->resolvers($eventClass, $method);
$methodName = $method->name;

return function (Message $message) use ($methodName, $resolvers): void {
$arguments = [];

foreach ($resolvers as $resolver) {
$arguments[] = $resolver($message);
}

$this->subscriber->$methodName(...$arguments);

Check failure on line 110 in src/Subscription/Subscriber/MetadataSubscriberAccessor.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

MixedMethodCall

src/Subscription/Subscriber/MetadataSubscriberAccessor.php:110:32: MixedMethodCall: Cannot determine the type of $this->subscriber (see https://psalm.dev/015)
};
}

/**
* @param class-string $eventClass
*
* @return list<Closure(Message):mixed>
*/
private function resolvers(string $eventClass, SubscribeMethodMetadata $method): array
{
$resolvers = [];

foreach ($method->arguments as $argument) {
foreach ($this->argumentResolvers as $resolver) {
if (!$resolver->support($argument, $eventClass)) {
continue;
}

$resolvers[] = static function (Message $message) use ($resolver, $argument): mixed {
return $resolver->resolve($argument, $message);
};

continue 2;
}

throw new NoSuitableResolver($this->subscriber::class, $method->name, $argument->name);
}

return $resolvers;
}
}
Loading

0 comments on commit 00d280c

Please sign in to comment.