From ce6fb1330ba939cb06fe8d5de59508556e3d7274 Mon Sep 17 00:00:00 2001 From: David Badura Date: Wed, 11 Feb 2026 22:57:01 +0100 Subject: [PATCH 01/59] allow to refresh subscriptions --- .../Command/SubscriptionRefreshCommand.php | 34 ++++++++++ .../Engine/CatchUpSubscriptionEngine.php | 16 ++++- .../Engine/DefaultSubscriptionEngine.php | 64 ++++++++++++++++++- .../Engine/SubscriptionRefreshable.php | 8 +++ .../Engine/ThrowOnErrorSubscriptionEngine.php | 16 ++++- src/Subscription/Subscription.php | 14 +++- 6 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 src/Console/Command/SubscriptionRefreshCommand.php create mode 100644 src/Subscription/Engine/SubscriptionRefreshable.php diff --git a/src/Console/Command/SubscriptionRefreshCommand.php b/src/Console/Command/SubscriptionRefreshCommand.php new file mode 100644 index 000000000..3a526c22e --- /dev/null +++ b/src/Console/Command/SubscriptionRefreshCommand.php @@ -0,0 +1,34 @@ +engine instanceof SubscriptionRefreshable) { + throw new LogicException(sprintf( + '"%s" does not implement "%s" and can therefore not refresh subscriptions.', + $this->engine::class, + SubscriptionRefreshable::class, + )); + } + + $criteria = $this->subscriptionEngineCriteria($input); + $this->engine->refreshSubscriptions($criteria); + + return 0; + } +} diff --git a/src/Subscription/Engine/CatchUpSubscriptionEngine.php b/src/Subscription/Engine/CatchUpSubscriptionEngine.php index 118dd6fe9..1574ebb36 100644 --- a/src/Subscription/Engine/CatchUpSubscriptionEngine.php +++ b/src/Subscription/Engine/CatchUpSubscriptionEngine.php @@ -4,13 +4,14 @@ namespace Patchlevel\EventSourcing\Subscription\Engine; +use LogicException; use Patchlevel\EventSourcing\Subscription\Subscription; use function array_merge; use const PHP_INT_MAX; -final class CatchUpSubscriptionEngine implements SubscriptionEngine +final class CatchUpSubscriptionEngine implements SubscriptionEngine, SubscriptionRefreshable { public function __construct( private readonly SubscriptionEngine $parent, @@ -86,6 +87,19 @@ public function subscriptions(SubscriptionEngineCriteria|null $criteria = null): return $this->parent->subscriptions($criteria); } + public function refreshSubscriptions(SubscriptionEngineCriteria|null $criteria = null): Result + { + if (!$this->parent instanceof SubscriptionRefreshable) { + throw new LogicException(sprintf( + '"%s" does not implement "%s" and can therefore not refresh subscriptions.', + $this->parent::class, + SubscriptionRefreshable::class, + )); + } + + return $this->parent->refreshSubscriptions($criteria); + } + private function mergeResult(ProcessedResult ...$results): ProcessedResult { $processedMessages = 0; diff --git a/src/Subscription/Engine/DefaultSubscriptionEngine.php b/src/Subscription/Engine/DefaultSubscriptionEngine.php index 8c8771733..c13a9c3ac 100644 --- a/src/Subscription/Engine/DefaultSubscriptionEngine.php +++ b/src/Subscription/Engine/DefaultSubscriptionEngine.php @@ -29,7 +29,7 @@ use function count; use function sprintf; -final class DefaultSubscriptionEngine implements SubscriptionEngine +final class DefaultSubscriptionEngine implements SubscriptionEngine, SubscriptionRefreshable { private SubscriptionManager $subscriptionManager; @@ -823,6 +823,68 @@ public function subscriptions(SubscriptionEngineCriteria|null $criteria = null): ); } + public function refreshSubscriptions(SubscriptionEngineCriteria|null $criteria = null): Result + { + $criteria ??= new SubscriptionEngineCriteria(); + + $this->discoverNewSubscriptions(); + + $subscriptions = $this->subscriptionManager->find(new SubscriptionCriteria( + ids: $criteria->ids, + groups: $criteria->groups, + )); + + foreach ($subscriptions as $subscription) { + $subscriber = $this->subscriber($subscription->id()); + + if (!$subscriber) { + continue; + } + + $changed = false; + + if ($subscription->runMode() !== $subscriber->runMode()) { + $changed = true; + $oldRunMode = $subscription->runMode(); + $subscription->changeRunMode($subscriber->runMode()); + + $this->logger?->info( + sprintf( + 'Subscription Engine: Subscription "%s" run mode changed from "%s" to "%s".', + $subscription->id(), + $oldRunMode->value, + $subscription->runMode()->value, + ), + ); + } + + if ($subscription->group() !== $subscriber->group()) { + $changed = true; + $oldGroup = $subscription->group(); + $subscription->changeGroup($subscriber->group()); + + $this->logger?->info( + sprintf( + 'Subscription Engine: Subscription "%s" group changed from "%s" to "%s".', + $subscription->id(), + $oldGroup, + $subscription->group(), + ), + ); + } + + if (!$changed) { + continue; + } + + $this->subscriptionManager->update($subscription); + } + + $this->subscriptionManager->flush(); + + return new Result(); + } + private function handleMessage(int $index, Message $message, Subscription $subscription): Error|null { $subscriber = $this->subscriber($subscription->id()); diff --git a/src/Subscription/Engine/SubscriptionRefreshable.php b/src/Subscription/Engine/SubscriptionRefreshable.php new file mode 100644 index 000000000..33fa602c9 --- /dev/null +++ b/src/Subscription/Engine/SubscriptionRefreshable.php @@ -0,0 +1,8 @@ +parent->subscriptions($criteria); } + public function refreshSubscriptions(SubscriptionEngineCriteria|null $criteria = null): Result + { + if (!$this->parent instanceof SubscriptionRefreshable) { + throw new LogicException(sprintf( + '"%s" does not implement "%s" and can therefore not refresh subscriptions.', + $this->parent::class, + SubscriptionRefreshable::class, + )); + } + + return $this->throwOnError($this->parent->refreshSubscriptions($criteria)); + } + /** * @param T $result * diff --git a/src/Subscription/Subscription.php b/src/Subscription/Subscription.php index c2e1d56b4..014804c83 100644 --- a/src/Subscription/Subscription.php +++ b/src/Subscription/Subscription.php @@ -14,8 +14,8 @@ final class Subscription /** @param list|null $cleanupTasks */ public function __construct( private readonly string $id, - private readonly string $group = self::DEFAULT_GROUP, - private readonly RunMode $runMode = RunMode::FromBeginning, + private string $group = self::DEFAULT_GROUP, + private RunMode $runMode = RunMode::FromBeginning, private Status $status = Status::New, private int $position = 0, private SubscriptionError|null $error = null, @@ -35,11 +35,21 @@ public function group(): string return $this->group; } + public function changeGroup(string $group): void + { + $this->group = $group; + } + public function runMode(): RunMode { return $this->runMode; } + public function changeRunMode(RunMode $runMode): void + { + $this->runMode = $runMode; + } + public function status(): Status { return $this->status; From 395bc1c8c048135c92d536b3349da14531922a84 Mon Sep 17 00:00:00 2001 From: David Badura Date: Sun, 15 Feb 2026 11:17:29 +0100 Subject: [PATCH 02/59] refresh cleanup tasks --- src/Console/Command/SubscriptionRefreshCommand.php | 2 ++ .../Engine/CatchUpSubscriptionEngine.php | 1 + .../Engine/DefaultSubscriptionEngine.php | 14 ++++++++++++++ .../Engine/SubscriptionRefreshable.php | 4 +++- .../Engine/ThrowOnErrorSubscriptionEngine.php | 2 ++ src/Subscription/Subscription.php | 6 ++++++ 6 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Console/Command/SubscriptionRefreshCommand.php b/src/Console/Command/SubscriptionRefreshCommand.php index 3a526c22e..49720881c 100644 --- a/src/Console/Command/SubscriptionRefreshCommand.php +++ b/src/Console/Command/SubscriptionRefreshCommand.php @@ -10,6 +10,8 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use function sprintf; + #[AsCommand( 'event-sourcing:subscription:refresh', 'Refresh subscriptions (run-mode, group)', diff --git a/src/Subscription/Engine/CatchUpSubscriptionEngine.php b/src/Subscription/Engine/CatchUpSubscriptionEngine.php index 1574ebb36..8c47a0a26 100644 --- a/src/Subscription/Engine/CatchUpSubscriptionEngine.php +++ b/src/Subscription/Engine/CatchUpSubscriptionEngine.php @@ -8,6 +8,7 @@ use Patchlevel\EventSourcing\Subscription\Subscription; use function array_merge; +use function sprintf; use const PHP_INT_MAX; diff --git a/src/Subscription/Engine/DefaultSubscriptionEngine.php b/src/Subscription/Engine/DefaultSubscriptionEngine.php index c13a9c3ac..8463a5e20 100644 --- a/src/Subscription/Engine/DefaultSubscriptionEngine.php +++ b/src/Subscription/Engine/DefaultSubscriptionEngine.php @@ -873,6 +873,20 @@ public function refreshSubscriptions(SubscriptionEngineCriteria|null $criteria = ); } + $cleanupTasks = $this->cleanupTasks($subscriber); + + if ($subscription->cleanupTasks() !== $cleanupTasks) { + $changed = true; + $subscription->replaceCleanupTasks($cleanupTasks); + + $this->logger?->info( + sprintf( + 'Subscription Engine: Subscription "%s" cleanup tasks changed.', + $subscription->id(), + ), + ); + } + if (!$changed) { continue; } diff --git a/src/Subscription/Engine/SubscriptionRefreshable.php b/src/Subscription/Engine/SubscriptionRefreshable.php index 33fa602c9..7525f7ba2 100644 --- a/src/Subscription/Engine/SubscriptionRefreshable.php +++ b/src/Subscription/Engine/SubscriptionRefreshable.php @@ -1,8 +1,10 @@ lastSavedAt = $lastSavedAt; } + /** @param list|null $cleanupTask */ + public function replaceCleanupTasks(array|null $cleanupTask): void + { + $this->cleanupTasks = $cleanupTask; + } + /** @return list|null */ public function cleanupTasks(): array|null { From e2d297965c37233791c80d91b62023a30d1f7fc9 Mon Sep 17 00:00:00 2001 From: David Badura Date: Sun, 15 Feb 2026 11:40:14 +0100 Subject: [PATCH 03/59] rename interface into CanRefreshSubscriptions --- src/Console/Command/SubscriptionRefreshCommand.php | 6 +++--- ...scriptionRefreshable.php => CanRefreshSubscriptions.php} | 2 +- src/Subscription/Engine/CatchUpSubscriptionEngine.php | 6 +++--- src/Subscription/Engine/DefaultSubscriptionEngine.php | 2 +- src/Subscription/Engine/ThrowOnErrorSubscriptionEngine.php | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) rename src/Subscription/Engine/{SubscriptionRefreshable.php => CanRefreshSubscriptions.php} (85%) diff --git a/src/Console/Command/SubscriptionRefreshCommand.php b/src/Console/Command/SubscriptionRefreshCommand.php index 49720881c..23eac9de2 100644 --- a/src/Console/Command/SubscriptionRefreshCommand.php +++ b/src/Console/Command/SubscriptionRefreshCommand.php @@ -5,7 +5,7 @@ namespace Patchlevel\EventSourcing\Console\Command; use LogicException; -use Patchlevel\EventSourcing\Subscription\Engine\SubscriptionRefreshable; +use Patchlevel\EventSourcing\Subscription\Engine\CanRefreshSubscriptions; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -20,11 +20,11 @@ final class SubscriptionRefreshCommand extends SubscriptionCommand { protected function execute(InputInterface $input, OutputInterface $output): int { - if (!$this->engine instanceof SubscriptionRefreshable) { + if (!$this->engine instanceof CanRefreshSubscriptions) { throw new LogicException(sprintf( '"%s" does not implement "%s" and can therefore not refresh subscriptions.', $this->engine::class, - SubscriptionRefreshable::class, + CanRefreshSubscriptions::class, )); } diff --git a/src/Subscription/Engine/SubscriptionRefreshable.php b/src/Subscription/Engine/CanRefreshSubscriptions.php similarity index 85% rename from src/Subscription/Engine/SubscriptionRefreshable.php rename to src/Subscription/Engine/CanRefreshSubscriptions.php index 7525f7ba2..2e22a15d4 100644 --- a/src/Subscription/Engine/SubscriptionRefreshable.php +++ b/src/Subscription/Engine/CanRefreshSubscriptions.php @@ -4,7 +4,7 @@ namespace Patchlevel\EventSourcing\Subscription\Engine; -interface SubscriptionRefreshable +interface CanRefreshSubscriptions { public function refreshSubscriptions(SubscriptionEngineCriteria|null $criteria = null): Result; } diff --git a/src/Subscription/Engine/CatchUpSubscriptionEngine.php b/src/Subscription/Engine/CatchUpSubscriptionEngine.php index 8c47a0a26..f3077a5f6 100644 --- a/src/Subscription/Engine/CatchUpSubscriptionEngine.php +++ b/src/Subscription/Engine/CatchUpSubscriptionEngine.php @@ -12,7 +12,7 @@ use const PHP_INT_MAX; -final class CatchUpSubscriptionEngine implements SubscriptionEngine, SubscriptionRefreshable +final class CatchUpSubscriptionEngine implements SubscriptionEngine, CanRefreshSubscriptions { public function __construct( private readonly SubscriptionEngine $parent, @@ -90,11 +90,11 @@ public function subscriptions(SubscriptionEngineCriteria|null $criteria = null): public function refreshSubscriptions(SubscriptionEngineCriteria|null $criteria = null): Result { - if (!$this->parent instanceof SubscriptionRefreshable) { + if (!$this->parent instanceof CanRefreshSubscriptions) { throw new LogicException(sprintf( '"%s" does not implement "%s" and can therefore not refresh subscriptions.', $this->parent::class, - SubscriptionRefreshable::class, + CanRefreshSubscriptions::class, )); } diff --git a/src/Subscription/Engine/DefaultSubscriptionEngine.php b/src/Subscription/Engine/DefaultSubscriptionEngine.php index 8463a5e20..ffab8a2d0 100644 --- a/src/Subscription/Engine/DefaultSubscriptionEngine.php +++ b/src/Subscription/Engine/DefaultSubscriptionEngine.php @@ -29,7 +29,7 @@ use function count; use function sprintf; -final class DefaultSubscriptionEngine implements SubscriptionEngine, SubscriptionRefreshable +final class DefaultSubscriptionEngine implements SubscriptionEngine, CanRefreshSubscriptions { private SubscriptionManager $subscriptionManager; diff --git a/src/Subscription/Engine/ThrowOnErrorSubscriptionEngine.php b/src/Subscription/Engine/ThrowOnErrorSubscriptionEngine.php index c544deb26..462d2e9a3 100644 --- a/src/Subscription/Engine/ThrowOnErrorSubscriptionEngine.php +++ b/src/Subscription/Engine/ThrowOnErrorSubscriptionEngine.php @@ -9,7 +9,7 @@ use function sprintf; -final class ThrowOnErrorSubscriptionEngine implements SubscriptionEngine, SubscriptionRefreshable +final class ThrowOnErrorSubscriptionEngine implements SubscriptionEngine, CanRefreshSubscriptions { public function __construct( private readonly SubscriptionEngine $parent, @@ -59,11 +59,11 @@ public function subscriptions(SubscriptionEngineCriteria|null $criteria = null): public function refreshSubscriptions(SubscriptionEngineCriteria|null $criteria = null): Result { - if (!$this->parent instanceof SubscriptionRefreshable) { + if (!$this->parent instanceof CanRefreshSubscriptions) { throw new LogicException(sprintf( '"%s" does not implement "%s" and can therefore not refresh subscriptions.', $this->parent::class, - SubscriptionRefreshable::class, + CanRefreshSubscriptions::class, )); } From 905b6c52539b9340e5aa3fc80c7fb15aafa95ea6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 21:07:12 +0000 Subject: [PATCH 04/59] Update dependency pymdown-extensions to v10.21 | datasource | package | from | to | | ---------- | ------------------ | ------- | ----- | | pypi | pymdown-extensions | 10.20.1 | 10.21 | Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 91f2ec792..43cd91175 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -5,7 +5,7 @@ mkdocs-material==9.7.1 # Markdown extensions Pygments==2.19.2 -pymdown-extensions==10.20.1 +pymdown-extensions==10.21 # MkDocs plugins mkdocs-material-extensions==1.3.1 From 3a37e456a4a6d08c93a8d8a712a6b9b7f2af8a6f Mon Sep 17 00:00:00 2001 From: David Badura Date: Tue, 17 Feb 2026 17:03:59 +0100 Subject: [PATCH 05/59] add shared apply context --- docs/pages/aggregate.md | 46 +++++++++++++++++++ phpstan-baseline.neon | 6 +++ src/Attribute/SharedApplyContext.php | 18 ++++++++ .../AttributeAggregateRootMetadataFactory.php | 32 ++++++++++--- .../MicroAggregate/PersonalInformation.php | 2 + tests/Integration/MicroAggregate/Profile.php | 5 +- tests/Unit/Fixture/OtherAggregate.php | 18 ++++++++ tests/Unit/Fixture/Profile.php | 3 +- .../Fixture/ProfileWithSharedApplyContext.php | 18 ++++++++ .../AttributeAggregateMetadataFactoryTest.php | 20 ++++++++ 10 files changed, 156 insertions(+), 12 deletions(-) create mode 100644 src/Attribute/SharedApplyContext.php create mode 100644 tests/Unit/Fixture/OtherAggregate.php create mode 100644 tests/Unit/Fixture/ProfileWithSharedApplyContext.php diff --git a/docs/pages/aggregate.md b/docs/pages/aggregate.md index 8439d427c..57992320c 100644 --- a/docs/pages/aggregate.md +++ b/docs/pages/aggregate.md @@ -303,6 +303,11 @@ final class Profile extends BasicAggregateRoot } } ``` +!!! tip + + You don't necessarily need to define multiple `Apply` attributes with the event class + if you define the event types in the method using a union type. + ## Suppress missing apply methods Sometimes you have events that do not change the state of the aggregate itself, @@ -358,6 +363,38 @@ final class Profile extends BasicAggregateRoot When all events are suppressed, debugging becomes more difficult if you forget an apply method. +## Shared apply context + +When working with [micro-aggregates](./aggregate.md#micro-aggregates), +it’s common that events are applied by different aggregates. +As a result, an aggregate may receive events it does not handle, which can lead to multiple “missing apply” warnings. + +The `SharedApplyContext` attribute allows you to declare that several aggregates share the same apply context. +With this configuration, a missing apply is only reported if none of the shared aggregates handle the event. + +```php +use Patchlevel\EventSourcing\Aggregate\BasicAggregateRoot; +use Patchlevel\EventSourcing\Attribute\Aggregate; +use Patchlevel\EventSourcing\Attribute\SharedApplyContext; +use Patchlevel\EventSourcing\Attribute\Stream; + +#[Aggregate('profile')] +#[SharedApplyContext([PersonalInformation::class])] +final class Profile extends BasicAggregateRoot +{ +} + +#[Aggregate('personal_information')] +#[Stream(Profile::class)] +#[SharedApplyContext([Profile::class])] +final class PersonalInformation extends BasicAggregateRoot +{ +} +``` +!!! warning + + You need to define the `SharedApplyContext` attribute on all aggregates that share the apply context. + ## Stream Name !!! warning @@ -675,8 +712,10 @@ use Patchlevel\EventSourcing\Aggregate\Uuid; use Patchlevel\EventSourcing\Attribute\Aggregate; use Patchlevel\EventSourcing\Attribute\Apply; use Patchlevel\EventSourcing\Attribute\Id; +use Patchlevel\EventSourcing\Attribute\SharedApplyContext; #[Aggregate('order')] +#[SharedApplyContext([Shipping::class])] final class Order extends BasicAggregateRoot { #[Id] @@ -706,10 +745,12 @@ use Patchlevel\EventSourcing\Aggregate\Uuid; use Patchlevel\EventSourcing\Attribute\Aggregate; use Patchlevel\EventSourcing\Attribute\Apply; use Patchlevel\EventSourcing\Attribute\Id; +use Patchlevel\EventSourcing\Attribute\SharedApplyContext; use Patchlevel\EventSourcing\Attribute\Stream; #[Aggregate('shipping')] #[Stream(Order::class)] +#[SharedApplyContext([Order::class])] final class Shipping extends BasicAggregateRoot { #[Id] @@ -740,6 +781,11 @@ final class Shipping extends BasicAggregateRoot } } ``` +!!! tip + + With the [SharedApplyContext](./aggregate.md#shared-apply-context) attribute, + you can suppress missing applies for events that are handled by other aggregates. + ### Child Aggregates ??? example "Experimental" diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 070a80330..72cd7456f 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -444,6 +444,12 @@ parameters: count: 1 path: tests/Unit/Fixture/ProfileWithHandler.php + - + message: '#^Property Patchlevel\\EventSourcing\\Tests\\Unit\\Fixture\\ProfileWithSharedApplyContext\:\:\$id is unused\.$#' + identifier: property.unused + count: 1 + path: tests/Unit/Fixture/ProfileWithSharedApplyContext.php + - message: '#^Property Patchlevel\\EventSourcing\\Tests\\Unit\\Fixture\\ProfileWithSuppressAll\:\:\$id is unused\.$#' identifier: property.unused diff --git a/src/Attribute/SharedApplyContext.php b/src/Attribute/SharedApplyContext.php new file mode 100644 index 000000000..597ba768d --- /dev/null +++ b/src/Attribute/SharedApplyContext.php @@ -0,0 +1,18 @@ +> $aggregates */ + public function __construct( + public array $aggregates, + ) { + } +} diff --git a/src/Metadata/AggregateRoot/AttributeAggregateRootMetadataFactory.php b/src/Metadata/AggregateRoot/AttributeAggregateRootMetadataFactory.php index e622b4910..db328eca7 100644 --- a/src/Metadata/AggregateRoot/AttributeAggregateRootMetadataFactory.php +++ b/src/Metadata/AggregateRoot/AttributeAggregateRootMetadataFactory.php @@ -9,6 +9,7 @@ use Patchlevel\EventSourcing\Attribute\Apply; use Patchlevel\EventSourcing\Attribute\ChildAggregate; use Patchlevel\EventSourcing\Attribute\Id; +use Patchlevel\EventSourcing\Attribute\SharedApplyContext; use Patchlevel\EventSourcing\Attribute\Snapshot as AttributeSnapshot; use Patchlevel\EventSourcing\Attribute\Stream; use Patchlevel\EventSourcing\Attribute\SuppressMissingApply; @@ -73,17 +74,14 @@ public function metadata(string $aggregate): AggregateRootMetadata private function findSuppressMissingApply(ReflectionClass $reflector): array { $suppressEvents = []; - $suppressAll = false; $attributes = $reflector->getAttributes(SuppressMissingApply::class); - foreach ($attributes as $attribute) { - $instance = $attribute->newInstance(); + if ($attributes !== []) { + $instance = $attributes[0]->newInstance(); if ($instance->suppressAll) { - $suppressAll = true; - - continue; + return [[], true]; } foreach ($instance->suppressEvents as $event) { @@ -91,7 +89,27 @@ private function findSuppressMissingApply(ReflectionClass $reflector): array } } - return [$suppressEvents, $suppressAll]; + $attributes = $reflector->getAttributes(SharedApplyContext::class); + + if ($attributes !== []) { + $instance = $attributes[0]->newInstance(); + + foreach ($instance->aggregates as $aggregateClass) { + $reflectionClass = new ReflectionClass($aggregateClass); + + $applyMethods = $this->findApplyMethods( + $reflectionClass, + $aggregateClass, + $this->findChildAggregates($reflectionClass), + ); + + foreach ($applyMethods as $eventClass => $method) { + $suppressEvents[$eventClass] = true; + } + } + } + + return [$suppressEvents, false]; } private function findAggregateName(ReflectionClass $reflector): string diff --git a/tests/Integration/MicroAggregate/PersonalInformation.php b/tests/Integration/MicroAggregate/PersonalInformation.php index b5cf941e9..8b8de651d 100644 --- a/tests/Integration/MicroAggregate/PersonalInformation.php +++ b/tests/Integration/MicroAggregate/PersonalInformation.php @@ -8,12 +8,14 @@ use Patchlevel\EventSourcing\Attribute\Aggregate; use Patchlevel\EventSourcing\Attribute\Apply; use Patchlevel\EventSourcing\Attribute\Id; +use Patchlevel\EventSourcing\Attribute\SharedApplyContext; use Patchlevel\EventSourcing\Attribute\Stream; use Patchlevel\EventSourcing\Tests\Integration\MicroAggregate\Events\NameChanged; use Patchlevel\EventSourcing\Tests\Integration\MicroAggregate\Events\ProfileCreated; #[Aggregate('personal_information')] #[Stream(Profile::class)] +#[SharedApplyContext([Profile::class])] final class PersonalInformation extends BasicAggregateRoot { #[Id] diff --git a/tests/Integration/MicroAggregate/Profile.php b/tests/Integration/MicroAggregate/Profile.php index 42e52e7bc..ef62b9c37 100644 --- a/tests/Integration/MicroAggregate/Profile.php +++ b/tests/Integration/MicroAggregate/Profile.php @@ -8,14 +8,13 @@ use Patchlevel\EventSourcing\Attribute\Aggregate; use Patchlevel\EventSourcing\Attribute\Apply; use Patchlevel\EventSourcing\Attribute\Id; +use Patchlevel\EventSourcing\Attribute\SharedApplyContext; use Patchlevel\EventSourcing\Attribute\Snapshot; -use Patchlevel\EventSourcing\Attribute\SuppressMissingApply; -use Patchlevel\EventSourcing\Tests\Integration\MicroAggregate\Events\NameChanged; use Patchlevel\EventSourcing\Tests\Integration\MicroAggregate\Events\ProfileCreated; #[Aggregate('profile')] #[Snapshot('default', 1)] -#[SuppressMissingApply([NameChanged::class])] +#[SharedApplyContext([PersonalInformation::class])] final class Profile extends BasicAggregateRoot { #[Id] diff --git a/tests/Unit/Fixture/OtherAggregate.php b/tests/Unit/Fixture/OtherAggregate.php new file mode 100644 index 000000000..61f5a86ae --- /dev/null +++ b/tests/Unit/Fixture/OtherAggregate.php @@ -0,0 +1,18 @@ +recordThat(new SplittingEvent($this->email, $this->visits)); } - #[Apply(ProfileCreated::class)] - #[Apply(ProfileVisited::class)] + #[Apply] protected function applyProfileCreated(ProfileCreated|ProfileVisited $event): void { if ($event instanceof ProfileCreated) { diff --git a/tests/Unit/Fixture/ProfileWithSharedApplyContext.php b/tests/Unit/Fixture/ProfileWithSharedApplyContext.php new file mode 100644 index 000000000..34eb11971 --- /dev/null +++ b/tests/Unit/Fixture/ProfileWithSharedApplyContext.php @@ -0,0 +1,18 @@ +metadata(ProfileWithBrokenApplyBothUsage::class); } + + public function testSuppressAll(): void + { + $metadataFactory = new AttributeAggregateRootMetadataFactory(); + $metadata = $metadataFactory->metadata(ProfileWithSuppressAll::class); + + self::assertTrue($metadata->suppressAll); + self::assertSame([], $metadata->suppressEvents); + } + + public function testSharedApplyContext(): void + { + $metadataFactory = new AttributeAggregateRootMetadataFactory(); + $metadata = $metadataFactory->metadata(ProfileWithSharedApplyContext::class); + + self::assertFalse($metadata->suppressAll); + self::assertSame([ProfileCreated::class => true], $metadata->suppressEvents); + } } From 424f3c4ab329cfe1775df44db0ea7fbd378b225a Mon Sep 17 00:00:00 2001 From: David Badura Date: Tue, 17 Feb 2026 18:43:09 +0100 Subject: [PATCH 06/59] add tests for CanRefreshSubscriptions --- composer.lock | 386 ++++++++++-------- phpstan-baseline.neon | 2 +- .../Subscription/SubscriptionTest.php | 67 +++ .../Engine/CatchUpSubscriptionEngineTest.php | 30 ++ .../Engine/DefaultSubscriptionEngineTest.php | 259 ++++++++++++ .../ThrowOnErrorSubscriptionEngineTest.php | 50 +++ 6 files changed, 612 insertions(+), 182 deletions(-) diff --git a/composer.lock b/composer.lock index 4e3dabfad..2a4b1a849 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "brick/math", - "version": "0.14.1", + "version": "0.14.8", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "f05858549e5f9d7bb45875a75583240a38a281d0" + "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/f05858549e5f9d7bb45875a75583240a38a281d0", - "reference": "f05858549e5f9d7bb45875a75583240a38a281d0", + "url": "https://api.github.com/repos/brick/math/zipball/63422359a44b7f06cae63c3b429b59e8efcc0629", + "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629", "shasum": "" }, "require": { @@ -56,7 +56,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.14.1" + "source": "https://github.com/brick/math/tree/0.14.8" }, "funding": [ { @@ -64,7 +64,7 @@ "type": "github" } ], - "time": "2025-11-24T14:40:29+00:00" + "time": "2026-02-10T14:33:43+00:00" }, { "name": "doctrine/dbal", @@ -174,29 +174,29 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.5", + "version": "1.1.6", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "conflict": { - "phpunit/phpunit": "<=7.5 || >=13" + "phpunit/phpunit": "<=7.5 || >=14" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^13", - "phpstan/phpstan": "1.4.10 || 2.1.11", + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", "psr/log": "^1 || ^2 || ^3" }, "suggest": { @@ -216,22 +216,22 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" }, - "time": "2025-04-07T20:06:18+00:00" + "time": "2026-02-07T07:09:04+00:00" }, { "name": "doctrine/event-manager", - "version": "2.1.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/doctrine/event-manager.git", - "reference": "c07799fcf5ad362050960a0fd068dded40b1e312" + "reference": "dda33921b198841ca8dbad2eaa5d4d34769d18cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/c07799fcf5ad362050960a0fd068dded40b1e312", - "reference": "c07799fcf5ad362050960a0fd068dded40b1e312", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/dda33921b198841ca8dbad2eaa5d4d34769d18cf", + "reference": "dda33921b198841ca8dbad2eaa5d4d34769d18cf", "shasum": "" }, "require": { @@ -293,7 +293,7 @@ ], "support": { "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/2.1.0" + "source": "https://github.com/doctrine/event-manager/tree/2.1.1" }, "funding": [ { @@ -309,20 +309,20 @@ "type": "tidelift" } ], - "time": "2026-01-17T22:40:21+00:00" + "time": "2026-01-29T07:11:08+00:00" }, { "name": "doctrine/migrations", - "version": "3.9.5", + "version": "3.9.6", "source": { "type": "git", "url": "https://github.com/doctrine/migrations.git", - "reference": "1b823afbc40f932dae8272574faee53f2755eac5" + "reference": "ffd8355cdd8505fc650d9604f058bf62aedd80a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/1b823afbc40f932dae8272574faee53f2755eac5", - "reference": "1b823afbc40f932dae8272574faee53f2755eac5", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/ffd8355cdd8505fc650d9604f058bf62aedd80a1", + "reference": "ffd8355cdd8505fc650d9604f058bf62aedd80a1", "shasum": "" }, "require": { @@ -396,7 +396,7 @@ ], "support": { "issues": "https://github.com/doctrine/migrations/issues", - "source": "https://github.com/doctrine/migrations/tree/3.9.5" + "source": "https://github.com/doctrine/migrations/tree/3.9.6" }, "funding": [ { @@ -412,20 +412,20 @@ "type": "tidelift" } ], - "time": "2025-11-20T11:15:36+00:00" + "time": "2026-02-11T06:46:11+00:00" }, { "name": "patchlevel/hydrator", - "version": "1.13.0", + "version": "1.16.0", "source": { "type": "git", "url": "https://github.com/patchlevel/hydrator.git", - "reference": "dc6fe9a835edaa2d6972ad148738eaeb95a6cb35" + "reference": "27b29f806f366194752cbaaa241e426bed2d942f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/patchlevel/hydrator/zipball/dc6fe9a835edaa2d6972ad148738eaeb95a6cb35", - "reference": "dc6fe9a835edaa2d6972ad148738eaeb95a6cb35", + "url": "https://api.github.com/repos/patchlevel/hydrator/zipball/27b29f806f366194752cbaaa241e426bed2d942f", + "reference": "27b29f806f366194752cbaaa241e426bed2d942f", "shasum": "" }, "require": { @@ -437,13 +437,13 @@ "symfony/type-info": "^7.3.0 || ^8.0.0" }, "require-dev": { - "infection/infection": "^0.31.9", + "infection/infection": "^0.32.4", "patchlevel/coding-standard": "^1.3.0", - "phpat/phpat": "^0.12.0", - "phpbench/phpbench": "^1.2.15", - "phpstan/phpstan": "^2.1.32", - "phpstan/phpstan-phpunit": "^2.0.8", - "phpunit/phpunit": "^11.5.17", + "phpat/phpat": "^0.12.2", + "phpbench/phpbench": "^1.4.3", + "phpstan/phpstan": "^2.1.39", + "phpstan/phpstan-phpunit": "^2.0.15", + "phpunit/phpunit": "^11.5.53", "symfony/var-dumper": "^5.4.29 || ^6.4.0 || ^7.0.0 || ^8.0.0" }, "type": "library", @@ -474,9 +474,9 @@ ], "support": { "issues": "https://github.com/patchlevel/hydrator/issues", - "source": "https://github.com/patchlevel/hydrator/tree/1.13.0" + "source": "https://github.com/patchlevel/hydrator/tree/1.16.0" }, - "time": "2025-11-27T16:58:40+00:00" + "time": "2026-02-17T11:56:35+00:00" }, { "name": "patchlevel/worker", @@ -995,16 +995,16 @@ }, { "name": "symfony/console", - "version": "v8.0.3", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "6145b304a5c1ea0bdbd0b04d297a5864f9a7d587" + "reference": "ace03c4cf9805080ff40cbeec69fca180c339a3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/6145b304a5c1ea0bdbd0b04d297a5864f9a7d587", - "reference": "6145b304a5c1ea0bdbd0b04d297a5864f9a7d587", + "url": "https://api.github.com/repos/symfony/console/zipball/ace03c4cf9805080ff40cbeec69fca180c339a3b", + "reference": "ace03c4cf9805080ff40cbeec69fca180c339a3b", "shasum": "" }, "require": { @@ -1061,7 +1061,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v8.0.3" + "source": "https://github.com/symfony/console/tree/v8.0.4" }, "funding": [ { @@ -1081,7 +1081,7 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:52:06+00:00" + "time": "2026-01-13T13:06:50+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1152,16 +1152,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v8.0.0", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "573f95783a2ec6e38752979db139f09fec033f03" + "reference": "99301401da182b6cfaa4700dbe9987bb75474b47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/573f95783a2ec6e38752979db139f09fec033f03", - "reference": "573f95783a2ec6e38752979db139f09fec033f03", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/99301401da182b6cfaa4700dbe9987bb75474b47", + "reference": "99301401da182b6cfaa4700dbe9987bb75474b47", "shasum": "" }, "require": { @@ -1213,7 +1213,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.4" }, "funding": [ { @@ -1233,7 +1233,7 @@ "type": "tidelift" } ], - "time": "2025-10-30T14:17:19+00:00" + "time": "2026-01-05T11:45:55+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -1313,16 +1313,16 @@ }, { "name": "symfony/finder", - "version": "v8.0.3", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "dd3a2953570a283a2ba4e17063bb98c734cf5b12" + "reference": "8bd576e97c67d45941365bf824e18dc8538e6eb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/dd3a2953570a283a2ba4e17063bb98c734cf5b12", - "reference": "dd3a2953570a283a2ba4e17063bb98c734cf5b12", + "url": "https://api.github.com/repos/symfony/finder/zipball/8bd576e97c67d45941365bf824e18dc8538e6eb0", + "reference": "8bd576e97c67d45941365bf824e18dc8538e6eb0", "shasum": "" }, "require": { @@ -1357,7 +1357,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v8.0.3" + "source": "https://github.com/symfony/finder/tree/v8.0.5" }, "funding": [ { @@ -1377,7 +1377,7 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:52:06+00:00" + "time": "2026-01-26T15:08:38+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1869,16 +1869,16 @@ }, { "name": "symfony/string", - "version": "v8.0.1", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc" + "reference": "758b372d6882506821ed666032e43020c4f57194" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ba65a969ac918ce0cc3edfac6cdde847eba231dc", - "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc", + "url": "https://api.github.com/repos/symfony/string/zipball/758b372d6882506821ed666032e43020c4f57194", + "reference": "758b372d6882506821ed666032e43020c4f57194", "shasum": "" }, "require": { @@ -1935,7 +1935,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.1" + "source": "https://github.com/symfony/string/tree/v8.0.4" }, "funding": [ { @@ -1955,20 +1955,20 @@ "type": "tidelift" } ], - "time": "2025-12-01T09:13:36+00:00" + "time": "2026-01-12T12:37:40+00:00" }, { "name": "symfony/type-info", - "version": "v8.0.1", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/type-info.git", - "reference": "bb091cec1f70383538c7d000699781813f8d1a6a" + "reference": "106a2d3bbf0d4576b2f70e6ca866fa420956ed0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/bb091cec1f70383538c7d000699781813f8d1a6a", - "reference": "bb091cec1f70383538c7d000699781813f8d1a6a", + "url": "https://api.github.com/repos/symfony/type-info/zipball/106a2d3bbf0d4576b2f70e6ca866fa420956ed0d", + "reference": "106a2d3bbf0d4576b2f70e6ca866fa420956ed0d", "shasum": "" }, "require": { @@ -2017,7 +2017,7 @@ "type" ], "support": { - "source": "https://github.com/symfony/type-info/tree/v8.0.1" + "source": "https://github.com/symfony/type-info/tree/v8.0.4" }, "funding": [ { @@ -2037,7 +2037,7 @@ "type": "tidelift" } ], - "time": "2025-12-05T14:08:45+00:00" + "time": "2026-01-09T12:15:10+00:00" }, { "name": "symfony/var-exporter", @@ -2926,16 +2926,16 @@ }, { "name": "doctrine/orm", - "version": "3.6.1", + "version": "3.6.2", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "2148940290e4c44b9101095707e71fb590832fa5" + "reference": "4262eb495b4d2a53b45de1ac58881e0091f2970f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/2148940290e4c44b9101095707e71fb590832fa5", - "reference": "2148940290e4c44b9101095707e71fb590832fa5", + "url": "https://api.github.com/repos/doctrine/orm/zipball/4262eb495b4d2a53b45de1ac58881e0091f2970f", + "reference": "4262eb495b4d2a53b45de1ac58881e0091f2970f", "shasum": "" }, "require": { @@ -3008,9 +3008,9 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/3.6.1" + "source": "https://github.com/doctrine/orm/tree/3.6.2" }, - "time": "2026-01-09T05:28:15+00:00" + "time": "2026-01-30T21:41:41+00:00" }, { "name": "doctrine/persistence", @@ -3345,16 +3345,16 @@ }, { "name": "infection/infection", - "version": "0.32.3", + "version": "0.32.4", "source": { "type": "git", "url": "https://github.com/infection/infection.git", - "reference": "3654db483619b63b9bcb04c24caeb03677c6d057" + "reference": "a2b0a3e47b56bd2f27ca13caecae47baa7e5abe8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/infection/infection/zipball/3654db483619b63b9bcb04c24caeb03677c6d057", - "reference": "3654db483619b63b9bcb04c24caeb03677c6d057", + "url": "https://api.github.com/repos/infection/infection/zipball/a2b0a3e47b56bd2f27ca13caecae47baa7e5abe8", + "reference": "a2b0a3e47b56bd2f27ca13caecae47baa7e5abe8", "shasum": "" }, "require": { @@ -3375,11 +3375,11 @@ "ondram/ci-detector": "^4.1.0", "php": "^8.2", "psr/log": "^2.0 || ^3.0", - "sanmai/di-container": "^0.1.4", + "sanmai/di-container": "^0.1.12", "sanmai/duoclock": "^0.1.0", "sanmai/later": "^0.1.7", - "sanmai/pipeline": "^7.0", - "sebastian/diff": "^4.0 || ^5.0 || ^6.0 || ^7.0", + "sanmai/pipeline": "^7.2", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0", "symfony/console": "^6.4 || ^7.0 || ^8.0", "symfony/filesystem": "^6.4 || ^7.0 || ^8.0", "symfony/finder": "^6.4 || ^7.0 || ^8.0", @@ -3465,7 +3465,7 @@ ], "support": { "issues": "https://github.com/infection/infection/issues", - "source": "https://github.com/infection/infection/tree/0.32.3" + "source": "https://github.com/infection/infection/tree/0.32.4" }, "funding": [ { @@ -3477,7 +3477,7 @@ "type": "open_collective" } ], - "time": "2026-01-13T14:23:38+00:00" + "time": "2026-02-09T13:24:18+00:00" }, { "name": "infection/mutator", @@ -3534,16 +3534,16 @@ }, { "name": "justinrainbow/json-schema", - "version": "6.6.4", + "version": "v6.7.2", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "2eeb75d21cf73211335888e7f5e6fd7440723ec7" + "reference": "6fea66c7204683af437864e7c4e7abf383d14bc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/2eeb75d21cf73211335888e7f5e6fd7440723ec7", - "reference": "2eeb75d21cf73211335888e7f5e6fd7440723ec7", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/6fea66c7204683af437864e7c4e7abf383d14bc0", + "reference": "6fea66c7204683af437864e7c4e7abf383d14bc0", "shasum": "" }, "require": { @@ -3603,9 +3603,9 @@ ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/6.6.4" + "source": "https://github.com/jsonrainbow/json-schema/tree/v6.7.2" }, - "time": "2025-12-19T15:01:32+00:00" + "time": "2026-02-15T15:06:22+00:00" }, { "name": "league/commonmark", @@ -3931,16 +3931,16 @@ }, { "name": "nette/schema", - "version": "v1.3.3", + "version": "v1.3.4", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004" + "reference": "086497a2f34b82fede9b5a41cc8e131d087cd8f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/2befc2f42d7c715fd9d95efc31b1081e5d765004", - "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004", + "url": "https://api.github.com/repos/nette/schema/zipball/086497a2f34b82fede9b5a41cc8e131d087cd8f7", + "reference": "086497a2f34b82fede9b5a41cc8e131d087cd8f7", "shasum": "" }, "require": { @@ -3948,8 +3948,8 @@ "php": "8.1 - 8.5" }, "require-dev": { - "nette/tester": "^2.5.2", - "phpstan/phpstan-nette": "^2.0@stable", + "nette/tester": "^2.6", + "phpstan/phpstan": "^2.0@stable", "tracy/tracy": "^2.8" }, "type": "library", @@ -3990,22 +3990,22 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.3.3" + "source": "https://github.com/nette/schema/tree/v1.3.4" }, - "time": "2025-10-30T22:57:59+00:00" + "time": "2026-02-08T02:54:00+00:00" }, { "name": "nette/utils", - "version": "v4.1.1", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "c99059c0315591f1a0db7ad6002000288ab8dc72" + "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/c99059c0315591f1a0db7ad6002000288ab8dc72", - "reference": "c99059c0315591f1a0db7ad6002000288ab8dc72", + "url": "https://api.github.com/repos/nette/utils/zipball/bb3ea637e3d131d72acc033cfc2746ee893349fe", + "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe", "shasum": "" }, "require": { @@ -4017,8 +4017,10 @@ }, "require-dev": { "jetbrains/phpstorm-attributes": "^1.2", + "nette/phpstan-rules": "^1.0", "nette/tester": "^2.5", - "phpstan/phpstan-nette": "^2.0@stable", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1@stable", "tracy/tracy": "^2.9" }, "suggest": { @@ -4079,9 +4081,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.1.1" + "source": "https://github.com/nette/utils/tree/v4.1.3" }, - "time": "2025-12-22T12:14:32+00:00" + "time": "2026-02-13T03:05:33+00:00" }, { "name": "nikic/php-parser", @@ -4394,16 +4396,16 @@ }, { "name": "phpat/phpat", - "version": "0.12.1", + "version": "0.12.2", "source": { "type": "git", "url": "https://github.com/carlosas/phpat.git", - "reference": "67b9e179757fbaa8b87e1469a66f103725858ee0" + "reference": "fe9caef4f8633a57c1d19643d37b58050b11806c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/carlosas/phpat/zipball/67b9e179757fbaa8b87e1469a66f103725858ee0", - "reference": "67b9e179757fbaa8b87e1469a66f103725858ee0", + "url": "https://api.github.com/repos/carlosas/phpat/zipball/fe9caef4f8633a57c1d19643d37b58050b11806c", + "reference": "fe9caef4f8633a57c1d19643d37b58050b11806c", "shasum": "" }, "require": { @@ -4445,9 +4447,9 @@ "description": "PHP Architecture Tester", "support": { "issues": "https://github.com/carlosas/phpat/issues", - "source": "https://github.com/carlosas/phpat/tree/0.12.1" + "source": "https://github.com/carlosas/phpat/tree/0.12.2" }, - "time": "2025-12-25T17:53:07+00:00" + "time": "2026-01-27T11:41:37+00:00" }, { "name": "phpbench/container", @@ -4600,16 +4602,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "2.3.1", + "version": "2.3.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "16dbf9937da8d4528ceb2145c9c7c0bd29e26374" + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/16dbf9937da8d4528ceb2145c9c7c0bd29e26374", - "reference": "16dbf9937da8d4528ceb2145c9c7c0bd29e26374", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", "shasum": "" }, "require": { @@ -4641,17 +4643,17 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" }, - "time": "2026-01-12T11:33:04+00:00" + "time": "2026-01-25T14:56:51+00:00" }, { "name": "phpstan/phpstan", - "version": "2.1.33", + "version": "2.1.39", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f", - "reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", + "reference": "c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", "shasum": "" }, "require": { @@ -4696,20 +4698,20 @@ "type": "github" } ], - "time": "2025-12-05T10:24:31+00:00" + "time": "2026-02-11T14:48:56+00:00" }, { "name": "phpstan/phpstan-phpunit", - "version": "2.0.11", + "version": "2.0.16", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "5e30669bef866eff70db8b58d72a5c185aa82414" + "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/5e30669bef866eff70db8b58d72a5c185aa82414", - "reference": "5e30669bef866eff70db8b58d72a5c185aa82414", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/6ab598e1bc106e6827fd346ae4a12b4a5d634c32", + "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32", "shasum": "" }, "require": { @@ -4745,11 +4747,14 @@ "MIT" ], "description": "PHPUnit extensions and rules for PHPStan", + "keywords": [ + "static analysis" + ], "support": { "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.11" + "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.16" }, - "time": "2025-12-19T09:05:35+00:00" + "time": "2026-02-14T09:05:21+00:00" }, { "name": "phpunit/php-code-coverage", @@ -4843,28 +4848,28 @@ }, { "name": "phpunit/php-file-iterator", - "version": "5.1.0", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -4892,15 +4897,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" } ], - "time": "2024-08-27T05:02:59+00:00" + "time": "2026-02-02T13:52:54+00:00" }, { "name": "phpunit/php-invoker", @@ -5088,16 +5105,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.50", + "version": "11.5.53", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "fdfc727f0fcacfeb8fcb30c7e5da173125b58be3" + "reference": "a997a653a82845f1240d73ee73a8a4e97e4b0607" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fdfc727f0fcacfeb8fcb30c7e5da173125b58be3", - "reference": "fdfc727f0fcacfeb8fcb30c7e5da173125b58be3", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a997a653a82845f1240d73ee73a8a4e97e4b0607", + "reference": "a997a653a82845f1240d73ee73a8a4e97e4b0607", "shasum": "" }, "require": { @@ -5112,7 +5129,7 @@ "phar-io/version": "^3.2.1", "php": ">=8.2", "phpunit/php-code-coverage": "^11.0.12", - "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-file-iterator": "^5.1.1", "phpunit/php-invoker": "^5.0.1", "phpunit/php-text-template": "^4.0.1", "phpunit/php-timer": "^7.0.1", @@ -5124,6 +5141,7 @@ "sebastian/exporter": "^6.3.2", "sebastian/global-state": "^7.0.2", "sebastian/object-enumerator": "^6.0.1", + "sebastian/recursion-context": "^6.0.3", "sebastian/type": "^5.1.3", "sebastian/version": "^5.0.2", "staabm/side-effects-detector": "^1.0.5" @@ -5169,7 +5187,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.50" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.53" }, "funding": [ { @@ -5193,20 +5211,20 @@ "type": "tidelift" } ], - "time": "2026-01-27T05:59:18+00:00" + "time": "2026-02-10T12:28:25+00:00" }, { "name": "sanmai/di-container", - "version": "0.1.11", + "version": "0.1.12", "source": { "type": "git", "url": "https://github.com/sanmai/di-container.git", - "reference": "4723e235e04589f88c0a114a4e438ff57a7cbb8a" + "reference": "8b9ad72f6ac1f9e185e5bd060dc9479cb5191d8b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sanmai/di-container/zipball/4723e235e04589f88c0a114a4e438ff57a7cbb8a", - "reference": "4723e235e04589f88c0a114a4e438ff57a7cbb8a", + "url": "https://api.github.com/repos/sanmai/di-container/zipball/8b9ad72f6ac1f9e185e5bd060dc9479cb5191d8b", + "reference": "8b9ad72f6ac1f9e185e5bd060dc9479cb5191d8b", "shasum": "" }, "require": { @@ -5264,7 +5282,7 @@ ], "support": { "issues": "https://github.com/sanmai/di-container/issues", - "source": "https://github.com/sanmai/di-container/tree/0.1.11" + "source": "https://github.com/sanmai/di-container/tree/0.1.12" }, "funding": [ { @@ -5272,7 +5290,7 @@ "type": "github" } ], - "time": "2026-01-13T07:38:17+00:00" + "time": "2026-01-27T08:25:46+00:00" }, { "name": "sanmai/duoclock", @@ -6864,16 +6882,16 @@ }, { "name": "symfony/messenger", - "version": "v8.0.3", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "b56b89aee16ceb623f76c8739ab62202ec198190" + "reference": "3483db96bcc33310cd1807d2b962e7e01d9f41c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/b56b89aee16ceb623f76c8739ab62202ec198190", - "reference": "b56b89aee16ceb623f76c8739ab62202ec198190", + "url": "https://api.github.com/repos/symfony/messenger/zipball/3483db96bcc33310cd1807d2b962e7e01d9f41c2", + "reference": "3483db96bcc33310cd1807d2b962e7e01d9f41c2", "shasum": "" }, "require": { @@ -6883,7 +6901,9 @@ }, "conflict": { "symfony/console": "<7.4", - "symfony/event-dispatcher-contracts": "<2.5" + "symfony/event-dispatcher-contracts": "<2.5", + "symfony/lock": "<7.4", + "symfony/serializer": "<7.4.4|>=8.0,<8.0.4" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", @@ -6896,7 +6916,7 @@ "symfony/property-access": "^7.4|^8.0", "symfony/rate-limiter": "^7.4|^8.0", "symfony/routing": "^7.4|^8.0", - "symfony/serializer": "^7.4|^8.0", + "symfony/serializer": "^7.4.4|^8.0.4", "symfony/service-contracts": "^2.5|^3", "symfony/stopwatch": "^7.4|^8.0", "symfony/validator": "^7.4|^8.0", @@ -6928,7 +6948,7 @@ "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/messenger/tree/v8.0.3" + "source": "https://github.com/symfony/messenger/tree/v8.0.4" }, "funding": [ { @@ -6948,7 +6968,7 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:52:06+00:00" + "time": "2026-01-08T22:36:47+00:00" }, { "name": "symfony/options-resolver", @@ -7267,16 +7287,16 @@ }, { "name": "symfony/process", - "version": "v8.0.3", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "0cbbd88ec836f8757641c651bb995335846abb78" + "reference": "b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/0cbbd88ec836f8757641c651bb995335846abb78", - "reference": "0cbbd88ec836f8757641c651bb995335846abb78", + "url": "https://api.github.com/repos/symfony/process/zipball/b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674", + "reference": "b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674", "shasum": "" }, "require": { @@ -7308,7 +7328,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v8.0.3" + "source": "https://github.com/symfony/process/tree/v8.0.5" }, "funding": [ { @@ -7328,20 +7348,20 @@ "type": "tidelift" } ], - "time": "2025-12-19T10:01:18+00:00" + "time": "2026-01-26T15:08:38+00:00" }, { "name": "symfony/var-dumper", - "version": "v8.0.3", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "3bc368228532ad538cc216768caa8968be95a8d6" + "reference": "326e0406fc315eca57ef5740fa4a280b7a068c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/3bc368228532ad538cc216768caa8968be95a8d6", - "reference": "3bc368228532ad538cc216768caa8968be95a8d6", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/326e0406fc315eca57ef5740fa4a280b7a068c82", + "reference": "326e0406fc315eca57ef5740fa4a280b7a068c82", "shasum": "" }, "require": { @@ -7395,7 +7415,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v8.0.3" + "source": "https://github.com/symfony/var-dumper/tree/v8.0.4" }, "funding": [ { @@ -7415,20 +7435,20 @@ "type": "tidelift" } ], - "time": "2025-12-18T11:23:51+00:00" + "time": "2026-01-01T23:07:29+00:00" }, { "name": "thecodingmachine/safe", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/thecodingmachine/safe.git", - "reference": "2cdd579eeaa2e78e51c7509b50cc9fb89a956236" + "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/2cdd579eeaa2e78e51c7509b50cc9fb89a956236", - "reference": "2cdd579eeaa2e78e51c7509b50cc9fb89a956236", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/705683a25bacf0d4860c7dea4d7947bfd09eea19", + "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19", "shasum": "" }, "require": { @@ -7538,7 +7558,7 @@ "description": "PHP core functions that throw exceptions instead of returning FALSE on error", "support": { "issues": "https://github.com/thecodingmachine/safe/issues", - "source": "https://github.com/thecodingmachine/safe/tree/v3.3.0" + "source": "https://github.com/thecodingmachine/safe/tree/v3.4.0" }, "funding": [ { @@ -7549,12 +7569,16 @@ "url": "https://github.com/shish", "type": "github" }, + { + "url": "https://github.com/silasjoisten", + "type": "github" + }, { "url": "https://github.com/staabm", "type": "github" } ], - "time": "2025-05-14T06:15:44+00:00" + "time": "2026-02-04T18:08:13+00:00" }, { "name": "theseer/tokenizer", @@ -7608,16 +7632,16 @@ }, { "name": "webmozart/assert", - "version": "2.1.2", + "version": "2.1.4", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649" + "reference": "b39f1870fc7c3e9e4a26106df5053354b9260a33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", - "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/b39f1870fc7c3e9e4a26106df5053354b9260a33", + "reference": "b39f1870fc7c3e9e4a26106df5053354b9260a33", "shasum": "" }, "require": { @@ -7664,9 +7688,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/2.1.2" + "source": "https://github.com/webmozarts/assert/tree/2.1.4" }, - "time": "2026-01-13T14:02:24+00:00" + "time": "2026-02-17T12:17:51+00:00" }, { "name": "webmozart/glob", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 070a80330..eef98508a 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,7 +1,7 @@ parameters: ignoreErrors: - - message: '#^Cannot unset offset ''url'' on array\{application_name\?\: string, charset\?\: string, dbname\?\: string, defaultTableOptions\?\: array\, driver\?\: ''ibm_db2''\|''mysqli''\|''oci8''\|''pdo_mysql''\|''pdo_oci''\|''pdo_pgsql''\|''pdo_sqlite''\|''pdo_sqlsrv''\|''pgsql''\|''sqlite3''\|''sqlsrv'', driverClass\?\: class\-string\, driverOptions\?\: array\, host\?\: string, \.\.\.\}\.$#' + message: '#^Cannot unset offset ''url'' on array\{application_name\?\: string, charset\?\: string, defaultTableOptions\?\: array\, driver\?\: ''ibm_db2''\|''mysqli''\|''oci8''\|''pdo_mysql''\|''pdo_oci''\|''pdo_pgsql''\|''pdo_sqlite''\|''pdo_sqlsrv''\|''pgsql''\|''sqlite3''\|''sqlsrv'', driverClass\?\: class\-string\, driverOptions\?\: array\, host\?\: string, keepReplica\?\: bool, \.\.\.\}\.$#' identifier: unset.offset count: 1 path: src/Console/DoctrineHelper.php diff --git a/tests/Integration/Subscription/SubscriptionTest.php b/tests/Integration/Subscription/SubscriptionTest.php index a65f51b87..23fb16e1d 100644 --- a/tests/Integration/Subscription/SubscriptionTest.php +++ b/tests/Integration/Subscription/SubscriptionTest.php @@ -28,6 +28,7 @@ use Patchlevel\EventSourcing\Subscription\Engine\DefaultSubscriptionEngine; use Patchlevel\EventSourcing\Subscription\Engine\EventFilteredStoreMessageLoader; use Patchlevel\EventSourcing\Subscription\Engine\GapResolverStoreMessageLoader; +use Patchlevel\EventSourcing\Subscription\Engine\MessageLoader; use Patchlevel\EventSourcing\Subscription\Engine\StoreMessageLoader; use Patchlevel\EventSourcing\Subscription\Engine\SubscriptionEngineCriteria; use Patchlevel\EventSourcing\Subscription\RetryStrategy\ClockBasedRetryStrategy; @@ -1565,6 +1566,72 @@ public function testLookup(): void self::assertSame('Hans', $result['name']); } + public function testRefreshSubscriptions(): void + { + $store = new DoctrineDbalStore( + $this->connection, + DefaultEventSerializer::createFromPaths([__DIR__ . '/Events']), + ); + + $clock = new FrozenClock(new DateTimeImmutable('2021-01-01T00:00:00')); + + $subscriptionStore = new DoctrineSubscriptionStore( + $this->connection, + $clock, + ); + + $schemaDirector = new DoctrineSchemaDirector( + $this->connection, + new ChainDoctrineSchemaConfigurator([ + $store, + $subscriptionStore, + ]), + ); + + $schemaDirector->create(); + + $subscriber = new #[Subscriber('test', RunMode::FromBeginning, group: 'default')] + class { + }; + + $subscriberRepository = new MetadataSubscriberAccessorRepository([$subscriber]); + + $engine = new DefaultSubscriptionEngine( + $this->createMock(MessageLoader::class), + $subscriptionStore, + $subscriberRepository, + ); + + $engine->setup(); + + $subscriptions = $engine->subscriptions(); + self::assertCount(1, $subscriptions); + self::assertEquals('test', $subscriptions[0]->id()); + self::assertEquals('default', $subscriptions[0]->group()); + self::assertEquals(RunMode::FromBeginning, $subscriptions[0]->runMode()); + + // change subscriber metadata + $newSubscriber = new #[Subscriber('test', RunMode::FromNow, group: 'new-group')] + class { + }; + + $newSubscriberRepository = new MetadataSubscriberAccessorRepository([$newSubscriber]); + + $engine = new DefaultSubscriptionEngine( + $this->createMock(MessageLoader::class), + $subscriptionStore, + $newSubscriberRepository, + ); + + $engine->refreshSubscriptions(); + + $subscriptions = $engine->subscriptions(); + self::assertCount(1, $subscriptions); + self::assertEquals('test', $subscriptions[0]->id()); + self::assertEquals('new-group', $subscriptions[0]->group()); + self::assertEquals(RunMode::FromNow, $subscriptions[0]->runMode()); + } + /** @param list $subscriptions */ private static function findSubscription(array $subscriptions, string $id): Subscription { diff --git a/tests/Unit/Subscription/Engine/CatchUpSubscriptionEngineTest.php b/tests/Unit/Subscription/Engine/CatchUpSubscriptionEngineTest.php index 47d04d2fc..a17d99c64 100644 --- a/tests/Unit/Subscription/Engine/CatchUpSubscriptionEngineTest.php +++ b/tests/Unit/Subscription/Engine/CatchUpSubscriptionEngineTest.php @@ -4,6 +4,8 @@ namespace Patchlevel\EventSourcing\Tests\Unit\Subscription\Engine; +use LogicException; +use Patchlevel\EventSourcing\Subscription\Engine\CanRefreshSubscriptions; use Patchlevel\EventSourcing\Subscription\Engine\CatchUpSubscriptionEngine; use Patchlevel\EventSourcing\Subscription\Engine\Error; use Patchlevel\EventSourcing\Subscription\Engine\ProcessedResult; @@ -216,4 +218,32 @@ public function testSubscriptions(): void self::assertEquals($expectedSubscriptions, $subscriptions); } + + public function testRefreshSubscriptions(): void + { + $parent = $this->createMockForIntersectionOfInterfaces([ + SubscriptionEngine::class, + CanRefreshSubscriptions::class, + ]); + + $engine = new CatchUpSubscriptionEngine($parent); + $criteria = new SubscriptionEngineCriteria(); + + $expectedResult = new Result(); + + $parent->expects($this->once())->method('refreshSubscriptions')->with($criteria)->willReturn($expectedResult); + $result = $engine->refreshSubscriptions($criteria); + + self::assertSame($expectedResult, $result); + } + + public function testRefreshSubscriptionsNotSupported(): void + { + $parent = $this->createMock(SubscriptionEngine::class); + + $engine = new CatchUpSubscriptionEngine($parent); + + $this->expectException(LogicException::class); + $engine->refreshSubscriptions(); + } } diff --git a/tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php b/tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php index adbdcc843..39b37f8dc 100644 --- a/tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php +++ b/tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php @@ -17,6 +17,7 @@ use Patchlevel\EventSourcing\Store\Criteria\Criteria; use Patchlevel\EventSourcing\Store\Criteria\FromIndexCriterion; use Patchlevel\EventSourcing\Store\Store; +use Patchlevel\EventSourcing\Subscription\Cleanup\Cleaner; use Patchlevel\EventSourcing\Subscription\Cleanup\CleanupFailed; use Patchlevel\EventSourcing\Subscription\Cleanup\CleanupTaskHandler; use Patchlevel\EventSourcing\Subscription\Cleanup\Dbal\DropTableTask; @@ -4572,4 +4573,262 @@ private function criteria(int $fromIndex = 0): Criteria { return new Criteria(new FromIndexCriterion($fromIndex)); } + + public function testRefreshSubscriptionsNoChanges(): void + { + $subscriber = new #[Subscriber('test', RunMode::FromBeginning, group: 'default')] + class { + }; + + $subscription = new Subscription( + 'test', + 'default', + RunMode::FromBeginning, + Status::Active, + ); + + $subscriptionStore = new DummySubscriptionStore([$subscription]); + + $engine = new DefaultSubscriptionEngine( + $this->createMock(Store::class), + $subscriptionStore, + new MetadataSubscriberAccessorRepository([$subscriber]), + logger: new NullLogger(), + cleaner: $this->createMock(Cleaner::class), + ); + + $engine->refreshSubscriptions(); + + $subscriptionStore->assertNoChanges(); + } + + public function testRefreshSubscriptionsChangeRunMode(): void + { + $subscriber = new #[Subscriber('test', RunMode::FromNow)] + class { + }; + + $subscription = new Subscription( + 'test', + 'default', + RunMode::FromBeginning, + Status::Active, + ); + + $subscriptionStore = new DummySubscriptionStore([$subscription]); + + $engine = new DefaultSubscriptionEngine( + $this->createMock(Store::class), + $subscriptionStore, + new MetadataSubscriberAccessorRepository([$subscriber]), + logger: new NullLogger(), + cleaner: $this->createMock(Cleaner::class), + ); + + $engine->refreshSubscriptions(); + + $subscriptionStore->assertUpdated( + new Subscription( + 'test', + 'default', + RunMode::FromNow, + Status::Active, + ), + ); + } + + public function testRefreshSubscriptionsChangeGroup(): void + { + $subscriber = new #[Subscriber('test', RunMode::FromBeginning, group: 'new-group')] + class { + }; + + $subscription = new Subscription( + 'test', + 'default', + RunMode::FromBeginning, + Status::Active, + ); + + $subscriptionStore = new DummySubscriptionStore([$subscription]); + + $engine = new DefaultSubscriptionEngine( + $this->createMock(Store::class), + $subscriptionStore, + new MetadataSubscriberAccessorRepository([$subscriber]), + logger: new NullLogger(), + cleaner: $this->createMock(Cleaner::class), + ); + + $engine->refreshSubscriptions(); + + $subscriptionStore->assertUpdated( + new Subscription( + 'test', + 'new-group', + RunMode::FromBeginning, + Status::Active, + ), + ); + } + + public function testRefreshSubscriptionsChangeCleanupTasks(): void + { + $subscriber = new #[Subscriber('test', RunMode::FromBeginning)] + class { + /** @return iterable */ + #[Cleanup] + public function cleanup(): iterable + { + yield new DropTableTask('test'); + } + }; + + $subscription = new Subscription( + 'test', + 'default', + RunMode::FromBeginning, + Status::Active, + ); + + $subscriptionStore = new DummySubscriptionStore([$subscription]); + + $engine = new DefaultSubscriptionEngine( + $this->createMock(Store::class), + $subscriptionStore, + new MetadataSubscriberAccessorRepository([$subscriber]), + logger: new NullLogger(), + cleaner: $this->createMock(Cleaner::class), + ); + + $engine->refreshSubscriptions(); + + $subscriptionStore->assertUpdated( + new Subscription( + 'test', + 'default', + RunMode::FromBeginning, + Status::Active, + cleanupTasks: [new DropTableTask('test')], + ), + ); + } + + public function testRefreshSubscriptionsMultipleChanges(): void + { + $subscriber = new #[Subscriber('test', RunMode::FromNow, group: 'new-group')] + class { + /** @return iterable */ + #[Cleanup] + public function cleanup(): iterable + { + yield new DropTableTask('test'); + } + }; + + $subscription = new Subscription( + 'test', + 'default', + RunMode::FromBeginning, + Status::Active, + ); + + $subscriptionStore = new DummySubscriptionStore([$subscription]); + + $engine = new DefaultSubscriptionEngine( + $this->createMock(Store::class), + $subscriptionStore, + new MetadataSubscriberAccessorRepository([$subscriber]), + logger: new NullLogger(), + cleaner: $this->createMock(Cleaner::class), + ); + + $engine->refreshSubscriptions(); + + $subscriptionStore->assertUpdated( + new Subscription( + 'test', + 'new-group', + RunMode::FromNow, + Status::Active, + cleanupTasks: [new DropTableTask('test')], + ), + ); + } + + public function testRefreshSubscriptionsWithCriteria(): void + { + $subscriber1 = new #[Subscriber('test1', RunMode::FromNow)] + class { + }; + + $subscriber2 = new #[Subscriber('test2', RunMode::FromNow)] + class { + }; + + $subscription1 = new Subscription( + 'test1', + 'default', + RunMode::FromBeginning, + Status::Active, + ); + + $subscription2 = new Subscription( + 'test2', + 'default', + RunMode::FromBeginning, + Status::Active, + ); + + $subscriptionStore = new DummySubscriptionStore([$subscription1, $subscription2]); + + $engine = new DefaultSubscriptionEngine( + $this->createMock(Store::class), + $subscriptionStore, + new MetadataSubscriberAccessorRepository([$subscriber1, $subscriber2]), + logger: new NullLogger(), + cleaner: $this->createMock(Cleaner::class), + ); + + $engine->refreshSubscriptions(new SubscriptionEngineCriteria(['test1'])); + + $subscriptionStore->assertUpdated( + new Subscription( + 'test1', + 'default', + RunMode::FromNow, + Status::Active, + ), + ); + + self::assertCount(1, $subscriptionStore->updatedSubscriptions); + } + + public function testRefreshSubscriptionsDiscoverNewSubscribers(): void + { + $subscriber = new #[Subscriber('test', RunMode::FromBeginning)] + class { + }; + + $subscriptionStore = new DummySubscriptionStore(); + + $engine = new DefaultSubscriptionEngine( + $this->createMock(Store::class), + $subscriptionStore, + new MetadataSubscriberAccessorRepository([$subscriber]), + logger: new NullLogger(), + cleaner: $this->createMock(Cleaner::class), + ); + + $engine->refreshSubscriptions(); + + $subscriptionStore->assertAdded( + new Subscription( + 'test', + 'default', + RunMode::FromBeginning, + Status::New, + ), + ); + } } diff --git a/tests/Unit/Subscription/Engine/ThrowOnErrorSubscriptionEngineTest.php b/tests/Unit/Subscription/Engine/ThrowOnErrorSubscriptionEngineTest.php index e3941270b..ac1f9f5df 100644 --- a/tests/Unit/Subscription/Engine/ThrowOnErrorSubscriptionEngineTest.php +++ b/tests/Unit/Subscription/Engine/ThrowOnErrorSubscriptionEngineTest.php @@ -4,6 +4,8 @@ namespace Patchlevel\EventSourcing\Tests\Unit\Subscription\Engine; +use LogicException; +use Patchlevel\EventSourcing\Subscription\Engine\CanRefreshSubscriptions; use Patchlevel\EventSourcing\Subscription\Engine\Error; use Patchlevel\EventSourcing\Subscription\Engine\ErrorDetected; use Patchlevel\EventSourcing\Subscription\Engine\ProcessedResult; @@ -261,4 +263,52 @@ public function testSubscriptions(): void self::assertSame([], $result); } + + public function testRefreshSubscriptionsSuccess(): void + { + $parent = $this->createMockForIntersectionOfInterfaces([ + SubscriptionEngine::class, + CanRefreshSubscriptions::class, + ]); + + $engine = new ThrowOnErrorSubscriptionEngine($parent); + $criteria = new SubscriptionEngineCriteria(); + + $expectedResult = new Result(); + + $parent->expects($this->once())->method('refreshSubscriptions')->with($criteria)->willReturn($expectedResult); + $result = $engine->refreshSubscriptions($criteria); + + self::assertSame($expectedResult, $result); + } + + public function testRefreshSubscriptionsError(): void + { + $this->expectException(ErrorDetected::class); + + $parent = $this->createMockForIntersectionOfInterfaces([ + SubscriptionEngine::class, + CanRefreshSubscriptions::class, + ]); + + $engine = new ThrowOnErrorSubscriptionEngine($parent); + $criteria = new SubscriptionEngineCriteria(); + + $expectedResult = new Result([ + new Error('id1', 'error1', new RuntimeException('error1')), + ]); + + $parent->expects($this->once())->method('refreshSubscriptions')->with($criteria)->willReturn($expectedResult); + $engine->refreshSubscriptions($criteria); + } + + public function testRefreshSubscriptionsNotSupported(): void + { + $parent = $this->createMock(SubscriptionEngine::class); + + $engine = new ThrowOnErrorSubscriptionEngine($parent); + + $this->expectException(LogicException::class); + $engine->refreshSubscriptions(); + } } From bbc0a184963be40262fbdfe4e3924e4135dcb20e Mon Sep 17 00:00:00 2001 From: David Badura Date: Tue, 17 Feb 2026 19:13:06 +0100 Subject: [PATCH 07/59] rename method into 'refresh' --- src/Console/Command/SubscriptionRefreshCommand.php | 4 ++-- .../Engine/CanRefreshSubscriptions.php | 2 +- .../Engine/CatchUpSubscriptionEngine.php | 6 +++--- .../Engine/DefaultSubscriptionEngine.php | 2 +- .../Engine/ThrowOnErrorSubscriptionEngine.php | 6 +++--- .../Integration/Subscription/SubscriptionTest.php | 2 +- .../Engine/CatchUpSubscriptionEngineTest.php | 6 +++--- .../Engine/DefaultSubscriptionEngineTest.php | 14 +++++++------- .../Engine/ThrowOnErrorSubscriptionEngineTest.php | 10 +++++----- 9 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Console/Command/SubscriptionRefreshCommand.php b/src/Console/Command/SubscriptionRefreshCommand.php index 23eac9de2..fa6b0cdab 100644 --- a/src/Console/Command/SubscriptionRefreshCommand.php +++ b/src/Console/Command/SubscriptionRefreshCommand.php @@ -22,14 +22,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int { if (!$this->engine instanceof CanRefreshSubscriptions) { throw new LogicException(sprintf( - '"%s" does not implement "%s" and can therefore not refresh subscriptions.', + '"%s" does not implement "%s" and cannot call refresh.', $this->engine::class, CanRefreshSubscriptions::class, )); } $criteria = $this->subscriptionEngineCriteria($input); - $this->engine->refreshSubscriptions($criteria); + $this->engine->refresh($criteria); return 0; } diff --git a/src/Subscription/Engine/CanRefreshSubscriptions.php b/src/Subscription/Engine/CanRefreshSubscriptions.php index 2e22a15d4..895a1d7d6 100644 --- a/src/Subscription/Engine/CanRefreshSubscriptions.php +++ b/src/Subscription/Engine/CanRefreshSubscriptions.php @@ -6,5 +6,5 @@ interface CanRefreshSubscriptions { - public function refreshSubscriptions(SubscriptionEngineCriteria|null $criteria = null): Result; + public function refresh(SubscriptionEngineCriteria|null $criteria = null): Result; } diff --git a/src/Subscription/Engine/CatchUpSubscriptionEngine.php b/src/Subscription/Engine/CatchUpSubscriptionEngine.php index f3077a5f6..88d57e5b9 100644 --- a/src/Subscription/Engine/CatchUpSubscriptionEngine.php +++ b/src/Subscription/Engine/CatchUpSubscriptionEngine.php @@ -88,17 +88,17 @@ public function subscriptions(SubscriptionEngineCriteria|null $criteria = null): return $this->parent->subscriptions($criteria); } - public function refreshSubscriptions(SubscriptionEngineCriteria|null $criteria = null): Result + public function refresh(SubscriptionEngineCriteria|null $criteria = null): Result { if (!$this->parent instanceof CanRefreshSubscriptions) { throw new LogicException(sprintf( - '"%s" does not implement "%s" and can therefore not refresh subscriptions.', + '"%s" does not implement "%s" and cannot call refresh.', $this->parent::class, CanRefreshSubscriptions::class, )); } - return $this->parent->refreshSubscriptions($criteria); + return $this->parent->refresh($criteria); } private function mergeResult(ProcessedResult ...$results): ProcessedResult diff --git a/src/Subscription/Engine/DefaultSubscriptionEngine.php b/src/Subscription/Engine/DefaultSubscriptionEngine.php index ffab8a2d0..98281c626 100644 --- a/src/Subscription/Engine/DefaultSubscriptionEngine.php +++ b/src/Subscription/Engine/DefaultSubscriptionEngine.php @@ -823,7 +823,7 @@ public function subscriptions(SubscriptionEngineCriteria|null $criteria = null): ); } - public function refreshSubscriptions(SubscriptionEngineCriteria|null $criteria = null): Result + public function refresh(SubscriptionEngineCriteria|null $criteria = null): Result { $criteria ??= new SubscriptionEngineCriteria(); diff --git a/src/Subscription/Engine/ThrowOnErrorSubscriptionEngine.php b/src/Subscription/Engine/ThrowOnErrorSubscriptionEngine.php index 462d2e9a3..2066bd1c2 100644 --- a/src/Subscription/Engine/ThrowOnErrorSubscriptionEngine.php +++ b/src/Subscription/Engine/ThrowOnErrorSubscriptionEngine.php @@ -57,17 +57,17 @@ public function subscriptions(SubscriptionEngineCriteria|null $criteria = null): return $this->parent->subscriptions($criteria); } - public function refreshSubscriptions(SubscriptionEngineCriteria|null $criteria = null): Result + public function refresh(SubscriptionEngineCriteria|null $criteria = null): Result { if (!$this->parent instanceof CanRefreshSubscriptions) { throw new LogicException(sprintf( - '"%s" does not implement "%s" and can therefore not refresh subscriptions.', + '"%s" does not implement "%s" and cannot call refresh.', $this->parent::class, CanRefreshSubscriptions::class, )); } - return $this->throwOnError($this->parent->refreshSubscriptions($criteria)); + return $this->throwOnError($this->parent->refresh($criteria)); } /** diff --git a/tests/Integration/Subscription/SubscriptionTest.php b/tests/Integration/Subscription/SubscriptionTest.php index 23fb16e1d..4e1684b2c 100644 --- a/tests/Integration/Subscription/SubscriptionTest.php +++ b/tests/Integration/Subscription/SubscriptionTest.php @@ -1623,7 +1623,7 @@ class { $newSubscriberRepository, ); - $engine->refreshSubscriptions(); + $engine->refresh(); $subscriptions = $engine->subscriptions(); self::assertCount(1, $subscriptions); diff --git a/tests/Unit/Subscription/Engine/CatchUpSubscriptionEngineTest.php b/tests/Unit/Subscription/Engine/CatchUpSubscriptionEngineTest.php index a17d99c64..f871c0d69 100644 --- a/tests/Unit/Subscription/Engine/CatchUpSubscriptionEngineTest.php +++ b/tests/Unit/Subscription/Engine/CatchUpSubscriptionEngineTest.php @@ -231,8 +231,8 @@ public function testRefreshSubscriptions(): void $expectedResult = new Result(); - $parent->expects($this->once())->method('refreshSubscriptions')->with($criteria)->willReturn($expectedResult); - $result = $engine->refreshSubscriptions($criteria); + $parent->expects($this->once())->method('refresh')->with($criteria)->willReturn($expectedResult); + $result = $engine->refresh($criteria); self::assertSame($expectedResult, $result); } @@ -244,6 +244,6 @@ public function testRefreshSubscriptionsNotSupported(): void $engine = new CatchUpSubscriptionEngine($parent); $this->expectException(LogicException::class); - $engine->refreshSubscriptions(); + $engine->refresh(); } } diff --git a/tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php b/tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php index 39b37f8dc..27417d652 100644 --- a/tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php +++ b/tests/Unit/Subscription/Engine/DefaultSubscriptionEngineTest.php @@ -4597,7 +4597,7 @@ class { cleaner: $this->createMock(Cleaner::class), ); - $engine->refreshSubscriptions(); + $engine->refresh(); $subscriptionStore->assertNoChanges(); } @@ -4625,7 +4625,7 @@ class { cleaner: $this->createMock(Cleaner::class), ); - $engine->refreshSubscriptions(); + $engine->refresh(); $subscriptionStore->assertUpdated( new Subscription( @@ -4660,7 +4660,7 @@ class { cleaner: $this->createMock(Cleaner::class), ); - $engine->refreshSubscriptions(); + $engine->refresh(); $subscriptionStore->assertUpdated( new Subscription( @@ -4701,7 +4701,7 @@ public function cleanup(): iterable cleaner: $this->createMock(Cleaner::class), ); - $engine->refreshSubscriptions(); + $engine->refresh(); $subscriptionStore->assertUpdated( new Subscription( @@ -4743,7 +4743,7 @@ public function cleanup(): iterable cleaner: $this->createMock(Cleaner::class), ); - $engine->refreshSubscriptions(); + $engine->refresh(); $subscriptionStore->assertUpdated( new Subscription( @@ -4790,7 +4790,7 @@ class { cleaner: $this->createMock(Cleaner::class), ); - $engine->refreshSubscriptions(new SubscriptionEngineCriteria(['test1'])); + $engine->refresh(new SubscriptionEngineCriteria(['test1'])); $subscriptionStore->assertUpdated( new Subscription( @@ -4820,7 +4820,7 @@ class { cleaner: $this->createMock(Cleaner::class), ); - $engine->refreshSubscriptions(); + $engine->refresh(); $subscriptionStore->assertAdded( new Subscription( diff --git a/tests/Unit/Subscription/Engine/ThrowOnErrorSubscriptionEngineTest.php b/tests/Unit/Subscription/Engine/ThrowOnErrorSubscriptionEngineTest.php index ac1f9f5df..50d888f7c 100644 --- a/tests/Unit/Subscription/Engine/ThrowOnErrorSubscriptionEngineTest.php +++ b/tests/Unit/Subscription/Engine/ThrowOnErrorSubscriptionEngineTest.php @@ -276,8 +276,8 @@ public function testRefreshSubscriptionsSuccess(): void $expectedResult = new Result(); - $parent->expects($this->once())->method('refreshSubscriptions')->with($criteria)->willReturn($expectedResult); - $result = $engine->refreshSubscriptions($criteria); + $parent->expects($this->once())->method('refresh')->with($criteria)->willReturn($expectedResult); + $result = $engine->refresh($criteria); self::assertSame($expectedResult, $result); } @@ -298,8 +298,8 @@ public function testRefreshSubscriptionsError(): void new Error('id1', 'error1', new RuntimeException('error1')), ]); - $parent->expects($this->once())->method('refreshSubscriptions')->with($criteria)->willReturn($expectedResult); - $engine->refreshSubscriptions($criteria); + $parent->expects($this->once())->method('refresh')->with($criteria)->willReturn($expectedResult); + $engine->refresh($criteria); } public function testRefreshSubscriptionsNotSupported(): void @@ -309,6 +309,6 @@ public function testRefreshSubscriptionsNotSupported(): void $engine = new ThrowOnErrorSubscriptionEngine($parent); $this->expectException(LogicException::class); - $engine->refreshSubscriptions(); + $engine->refresh(); } } From 2211cfa593f16390b9a3d5c80bb93f0d98b12eec Mon Sep 17 00:00:00 2001 From: David Badura Date: Tue, 17 Feb 2026 19:15:27 +0100 Subject: [PATCH 08/59] add docs --- docs/pages/subscription.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/pages/subscription.md b/docs/pages/subscription.md index b89aec427..e1512adc5 100644 --- a/docs/pages/subscription.md +++ b/docs/pages/subscription.md @@ -1315,6 +1315,18 @@ foreach ($subscriptions as $subscription) { echo $subscription->status()->value; } ``` +### Refresh + +If you change the metadata of a subscriber in the code (e.g. `runMode`, `group` or `cleanupTasks`), +you can use the `refresh` method to update the existing subscriptions in the store. + +```php +use Patchlevel\EventSourcing\Subscription\Engine\SubscriptionEngine; +use Patchlevel\EventSourcing\Subscription\Engine\SubscriptionEngineCriteria; + +/** @var SubscriptionEngine $subscriptionEngine */ +$subscriptionEngine->refresh(new SubscriptionEngineCriteria()); +``` ## Learn more * [How to use CLI commands](./cli.md) From dd49672851ab9c0388926d908fdad9c260890d91 Mon Sep 17 00:00:00 2001 From: David Badura Date: Tue, 17 Feb 2026 22:03:33 +0100 Subject: [PATCH 09/59] fix deptrac --- deptrac-baseline.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deptrac-baseline.yaml b/deptrac-baseline.yaml index 83ffb4da8..eb6a9bb9d 100644 --- a/deptrac-baseline.yaml +++ b/deptrac-baseline.yaml @@ -10,6 +10,8 @@ deptrac: - Patchlevel\EventSourcing\Subscription\RunMode Patchlevel\EventSourcing\Attribute\Projector: - Patchlevel\EventSourcing\Subscription\RunMode + Patchlevel\EventSourcing\Attribute\SharedApplyContext: + - Patchlevel\EventSourcing\Aggregate\AggregateRoot Patchlevel\EventSourcing\Attribute\Stream: - Patchlevel\EventSourcing\Aggregate\AggregateRoot Patchlevel\EventSourcing\Attribute\Subscriber: From 62ae8bf4b5bf1677e3587580097e44dbb4b48709 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:09:26 +0000 Subject: [PATCH 10/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- tools/composer.lock | 382 ++++++++++++++++++++++---------------------- 1 file changed, 187 insertions(+), 195 deletions(-) diff --git a/tools/composer.lock b/tools/composer.lock index bdfc8f877..4d8aed241 100644 --- a/tools/composer.lock +++ b/tools/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "azjezz/psl", - "version": "4.2.0", + "version": "4.2.1", "source": { "type": "git", "url": "https://github.com/azjezz/psl.git", - "reference": "15153a64c9824335ce11654522e7d88de762d39e" + "reference": "28c6752857597a1bb6fa8be16678c144b9097ab8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/azjezz/psl/zipball/15153a64c9824335ce11654522e7d88de762d39e", - "reference": "15153a64c9824335ce11654522e7d88de762d39e", + "url": "https://api.github.com/repos/azjezz/psl/zipball/28c6752857597a1bb6fa8be16678c144b9097ab8", + "reference": "28c6752857597a1bb6fa8be16678c144b9097ab8", "shasum": "" }, "require": { @@ -30,7 +30,7 @@ "revolt/event-loop": "^1.0.7" }, "require-dev": { - "carthage-software/mago": "^1.0.0-beta.32", + "carthage-software/mago": "^1.3.0", "infection/infection": "^0.31.2", "php-coveralls/php-coveralls": "^2.7.0", "phpbench/phpbench": "^1.4.0", @@ -68,7 +68,7 @@ "description": "PHP Standard Library", "support": { "issues": "https://github.com/azjezz/psl/issues", - "source": "https://github.com/azjezz/psl/tree/4.2.0" + "source": "https://github.com/azjezz/psl/tree/4.2.1" }, "funding": [ { @@ -80,7 +80,7 @@ "type": "github" } ], - "time": "2025-10-25T08:31:40+00:00" + "time": "2026-01-29T12:38:33+00:00" }, { "name": "beberlei/assert", @@ -292,16 +292,16 @@ }, { "name": "composer/composer", - "version": "2.9.3", + "version": "2.9.5", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "fb3bee27676fd852a8a11ebbb1de19b4dada5aba" + "reference": "72a8f8e653710e18d83e5dd531eb5a71fc3223e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/fb3bee27676fd852a8a11ebbb1de19b4dada5aba", - "reference": "fb3bee27676fd852a8a11ebbb1de19b4dada5aba", + "url": "https://api.github.com/repos/composer/composer/zipball/72a8f8e653710e18d83e5dd531eb5a71fc3223e6", + "reference": "72a8f8e653710e18d83e5dd531eb5a71fc3223e6", "shasum": "" }, "require": { @@ -389,7 +389,7 @@ "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/composer/issues", "security": "https://github.com/composer/composer/security/policy", - "source": "https://github.com/composer/composer/tree/2.9.3" + "source": "https://github.com/composer/composer/tree/2.9.5" }, "funding": [ { @@ -401,7 +401,7 @@ "type": "github" } ], - "time": "2025-12-30T12:40:17+00:00" + "time": "2026-01-29T10:40:53+00:00" }, { "name": "composer/metadata-minifier", @@ -776,43 +776,43 @@ }, { "name": "deptrac/deptrac", - "version": "4.4.0", + "version": "4.6.0", "source": { "type": "git", "url": "https://github.com/deptrac/deptrac.git", - "reference": "3058cb1b5908a25711ccc0f4c96b64e6fd704114" + "reference": "b27bfc5291a1cbe1c0ebf514716b1d25c68c63fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/deptrac/deptrac/zipball/3058cb1b5908a25711ccc0f4c96b64e6fd704114", - "reference": "3058cb1b5908a25711ccc0f4c96b64e6fd704114", + "url": "https://api.github.com/repos/deptrac/deptrac/zipball/b27bfc5291a1cbe1c0ebf514716b1d25c68c63fd", + "reference": "b27bfc5291a1cbe1c0ebf514716b1d25c68c63fd", "shasum": "" }, "require": { "composer/xdebug-handler": "^3.0", - "jetbrains/phpstorm-stubs": "2024.3", + "jetbrains/phpstorm-stubs": "2024.3 || 2025.3", "nikic/php-parser": "^5", - "php": "^8.1", + "php": "^8.2", "phpdocumentor/graphviz": "^2.1", - "phpdocumentor/type-resolver": "^1.9.0", - "phpstan/phpdoc-parser": "^1.5.0|^2.1.0", + "phpdocumentor/type-resolver": "^1.9.0 || ^2.0.0", + "phpstan/phpdoc-parser": "^1.5.0 || ^2.1.0", "phpstan/phpstan": "^2.0", "psr/container": "^2.0", "psr/event-dispatcher": "^1.0", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/config": "^6.4 || ^7.4 || ^8.0", + "symfony/console": "^6.4 || ^7.4 || ^8.0", + "symfony/dependency-injection": "^6.4 || ^7.4 || ^8.0", + "symfony/event-dispatcher": "^6.4 || ^7.4 || ^8.0", "symfony/event-dispatcher-contracts": "^3.4", - "symfony/filesystem": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "symfony/filesystem": "^6.4 || ^7.4 || ^8.0", + "symfony/finder": "^6.4 || ^7.4 || ^8.0", + "symfony/yaml": "^6.4 || ^7.4 || ^8.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8", "ergebnis/composer-normalize": "^2.45", "ext-libxml": "*", - "symfony/stopwatch": "^6.4" + "symfony/stopwatch": "^6.4 || ^7.4 || ^8.0" }, "suggest": { "ext-dom": "For using the JUnit output formatter" @@ -855,35 +855,35 @@ ], "support": { "issues": "https://github.com/deptrac/deptrac/issues", - "source": "https://github.com/deptrac/deptrac/tree/4.4.0" + "source": "https://github.com/deptrac/deptrac/tree/4.6.0" }, - "time": "2025-12-08T08:14:18+00:00" + "time": "2026-02-02T09:44:37+00:00" }, { "name": "doctrine/deprecations", - "version": "1.1.5", + "version": "1.1.6", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "conflict": { - "phpunit/phpunit": "<=7.5 || >=13" + "phpunit/phpunit": "<=7.5 || >=14" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^13", - "phpstan/phpstan": "1.4.10 || 2.1.11", + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", "psr/log": "^1 || ^2 || ^3" }, "suggest": { @@ -903,29 +903,29 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" }, - "time": "2025-04-07T20:06:18+00:00" + "time": "2026-02-07T07:09:04+00:00" }, { "name": "jetbrains/phpstorm-stubs", - "version": "v2024.3", + "version": "v2025.3", "source": { "type": "git", - "url": "https://github.com/JetBrains/phpstorm-stubs.git", - "reference": "0e82bdfe850c71857ee4ee3501ed82a9fc5d043c" + "url": "https://github.com/JetBrains/phpstorm-stubs", + "reference": "d1ee5e570343bd4276a3d5959e6e1c2530b006d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JetBrains/phpstorm-stubs/zipball/0e82bdfe850c71857ee4ee3501ed82a9fc5d043c", - "reference": "0e82bdfe850c71857ee4ee3501ed82a9fc5d043c", + "url": "https://api.github.com/repos/JetBrains/phpstorm-stubs/zipball/d1ee5e570343bd4276a3d5959e6e1c2530b006d0", + "reference": "d1ee5e570343bd4276a3d5959e6e1c2530b006d0", "shasum": "" }, "require-dev": { - "friendsofphp/php-cs-fixer": "v3.64.0", - "nikic/php-parser": "v5.3.1", - "phpdocumentor/reflection-docblock": "5.6.0", - "phpunit/phpunit": "11.4.3" + "friendsofphp/php-cs-fixer": "^v3.86", + "nikic/php-parser": "^v5.6", + "phpdocumentor/reflection-docblock": "^5.6", + "phpunit/phpunit": "^12.3" }, "type": "library", "autoload": { @@ -949,23 +949,20 @@ "stubs", "type" ], - "support": { - "source": "https://github.com/JetBrains/phpstorm-stubs/tree/v2024.3" - }, - "time": "2024-12-14T08:03:12+00:00" + "time": "2025-09-18T15:47:24+00:00" }, { "name": "justinrainbow/json-schema", - "version": "6.6.4", + "version": "v6.7.2", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "2eeb75d21cf73211335888e7f5e6fd7440723ec7" + "reference": "6fea66c7204683af437864e7c4e7abf383d14bc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/2eeb75d21cf73211335888e7f5e6fd7440723ec7", - "reference": "2eeb75d21cf73211335888e7f5e6fd7440723ec7", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/6fea66c7204683af437864e7c4e7abf383d14bc0", + "reference": "6fea66c7204683af437864e7c4e7abf383d14bc0", "shasum": "" }, "require": { @@ -1025,9 +1022,9 @@ ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/6.6.4" + "source": "https://github.com/jsonrainbow/json-schema/tree/v6.7.2" }, - "time": "2025-12-19T15:01:32+00:00" + "time": "2026-02-15T15:06:22+00:00" }, { "name": "marc-mabe/php-enum", @@ -1392,38 +1389,38 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.12.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195" + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195", - "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", - "php": "^7.3 || ^8.0", + "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.18|^2.0" + "phpstan/phpdoc-parser": "^2.0" }, "require-dev": { "ext-tokenizer": "*", "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" + "psalm/phar": "^4" }, "type": "library", "extra": { "branch-alias": { - "dev-1.x": "1.x-dev" + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev" } }, "autoload": { @@ -1444,22 +1441,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0" }, - "time": "2025-11-21T15:09:14+00:00" + "time": "2026-01-06T21:53:42+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "2.3.1", + "version": "2.3.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "16dbf9937da8d4528ceb2145c9c7c0bd29e26374" + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/16dbf9937da8d4528ceb2145c9c7c0bd29e26374", - "reference": "16dbf9937da8d4528ceb2145c9c7c0bd29e26374", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", "shasum": "" }, "require": { @@ -1491,17 +1488,17 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" }, - "time": "2026-01-12T11:33:04+00:00" + "time": "2026-01-25T14:56:51+00:00" }, { "name": "phpstan/phpstan", - "version": "2.1.33", + "version": "2.1.39", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f", - "reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", + "reference": "c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", "shasum": "" }, "require": { @@ -1546,7 +1543,7 @@ "type": "github" } ], - "time": "2025-12-05T10:24:31+00:00" + "time": "2026-02-11T14:48:56+00:00" }, { "name": "psr/container", @@ -1848,31 +1845,31 @@ }, { "name": "roave/backward-compatibility-check", - "version": "8.17.0", + "version": "8.19.0", "source": { "type": "git", "url": "https://github.com/Roave/BackwardCompatibilityCheck.git", - "reference": "b3aab0b917d127c3e048b4f96a7f588388e30ac9" + "reference": "810eb88eeff37ef300653bcfb77ca51a314777a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/BackwardCompatibilityCheck/zipball/b3aab0b917d127c3e048b4f96a7f588388e30ac9", - "reference": "b3aab0b917d127c3e048b4f96a7f588388e30ac9", + "url": "https://api.github.com/repos/Roave/BackwardCompatibilityCheck/zipball/810eb88eeff37ef300653bcfb77ca51a314777a2", + "reference": "810eb88eeff37ef300653bcfb77ca51a314777a2", "shasum": "" }, "require": { - "azjezz/psl": "^4.2.0", - "composer/composer": "^2.9.2", + "azjezz/psl": "^4.2.1", + "composer/composer": "^2.9.5", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-simplexml": "*", - "nikic/php-parser": "^5.6.2", + "nikic/php-parser": "^5.7.0", "nikolaposa/version": "^4.2.1", "ocramius/package-versions": "^2.11.0", "php": "~8.3.0 || ~8.4.0 || ~8.5.0", - "roave/better-reflection": "^6.66.0", - "symfony/console": "^7.4.0" + "roave/better-reflection": "^6.69.0", + "symfony/console": "^7.4.4" }, "conflict": { "marc-mabe/php-enum": "<4.7.2", @@ -1881,14 +1878,14 @@ }, "require-dev": { "doctrine/coding-standard": "^14.0.0", - "justinrainbow/json-schema": "^6.6.2", + "justinrainbow/json-schema": "^6.6.4", "php-standard-library/psalm-plugin": "^2.3.0", - "phpunit/phpunit": "^12.4.4", + "phpunit/phpunit": "^12.5.11", "psalm/plugin-phpunit": "^0.19.5", - "roave/infection-static-analysis-plugin": "^1.41.0", + "roave/infection-static-analysis-plugin": "^1.43.0", "roave/security-advisories": "dev-master", "squizlabs/php_codesniffer": "^4.0.1", - "vimeo/psalm": "^6.13.1" + "vimeo/psalm": "^6.15.1" }, "bin": [ "bin/roave-backward-compatibility-check" @@ -1916,27 +1913,27 @@ "description": "Tool to compare two revisions of a public API to check for BC breaks", "support": { "issues": "https://github.com/Roave/BackwardCompatibilityCheck/issues", - "source": "https://github.com/Roave/BackwardCompatibilityCheck/tree/8.17.0" + "source": "https://github.com/Roave/BackwardCompatibilityCheck/tree/8.19.0" }, - "time": "2025-11-29T01:02:22+00:00" + "time": "2026-02-13T19:14:25+00:00" }, { "name": "roave/better-reflection", - "version": "6.67.0", + "version": "6.69.0", "source": { "type": "git", "url": "https://github.com/Roave/BetterReflection.git", - "reference": "59ed7abcddba98b3a0533cbfbf6d033b9d239fc4" + "reference": "dccbd10b5b3da8718f2945ad50faf90a1cb2db55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/BetterReflection/zipball/59ed7abcddba98b3a0533cbfbf6d033b9d239fc4", - "reference": "59ed7abcddba98b3a0533cbfbf6d033b9d239fc4", + "url": "https://api.github.com/repos/Roave/BetterReflection/zipball/dccbd10b5b3da8718f2945ad50faf90a1cb2db55", + "reference": "dccbd10b5b3da8718f2945ad50faf90a1cb2db55", "shasum": "" }, "require": { "ext-json": "*", - "jetbrains/phpstorm-stubs": "2024.3", + "jetbrains/phpstorm-stubs": "2025.3", "nikic/php-parser": "^5.7.0", "php": "~8.3.2 || ~8.4.1 || ~8.5.0" }, @@ -1945,7 +1942,7 @@ }, "require-dev": { "phpbench/phpbench": "^1.4.3", - "phpunit/phpunit": "^11.5.46" + "phpunit/phpunit": "^12.5.8" }, "suggest": { "composer/composer": "Required to use the ComposerSourceLocator" @@ -1985,9 +1982,9 @@ "description": "Better Reflection - an improved code reflection API", "support": { "issues": "https://github.com/Roave/BetterReflection/issues", - "source": "https://github.com/Roave/BetterReflection/tree/6.67.0" + "source": "https://github.com/Roave/BetterReflection/tree/6.69.0" }, - "time": "2026-01-13T16:19:15+00:00" + "time": "2026-02-01T13:20:30+00:00" }, { "name": "seld/jsonlint", @@ -2164,34 +2161,33 @@ }, { "name": "symfony/config", - "version": "v7.4.3", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "800ce889e358a53a9678b3212b0c8cecd8c6aace" + "reference": "8f45af92f08f82902827a8b6f403aaf49d893539" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/800ce889e358a53a9678b3212b0c8cecd8c6aace", - "reference": "800ce889e358a53a9678b3212b0c8cecd8c6aace", + "url": "https://api.github.com/repos/symfony/config/zipball/8f45af92f08f82902827a8b6f403aaf49d893539", + "reference": "8f45af92f08f82902827a8b6f403aaf49d893539", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/filesystem": "^7.1|^8.0", - "symfony/polyfill-ctype": "~1.8" + "symfony/filesystem": "^7.4|^8.0", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/finder": "<6.4", "symfony/service-contracts": "<2.5" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0|^8.0" + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -2219,7 +2215,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v7.4.3" + "source": "https://github.com/symfony/config/tree/v8.0.4" }, "funding": [ { @@ -2239,20 +2235,20 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:24:27+00:00" + "time": "2026-01-13T13:06:50+00:00" }, { "name": "symfony/console", - "version": "v7.4.3", + "version": "v7.4.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6" + "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/732a9ca6cd9dfd940c639062d5edbde2f6727fb6", - "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6", + "url": "https://api.github.com/repos/symfony/console/zipball/41e38717ac1dd7a46b6bda7d6a82af2d98a78894", + "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894", "shasum": "" }, "require": { @@ -2317,7 +2313,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.3" + "source": "https://github.com/symfony/console/tree/v7.4.4" }, "funding": [ { @@ -2337,43 +2333,40 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:50:43+00:00" + "time": "2026-01-13T11:36:38+00:00" }, { "name": "symfony/dependency-injection", - "version": "v7.4.3", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "54122901b6d772e94f1e71a75e0533bc16563499" + "reference": "40a6c455ade7e3bf25900d6b746d40cfa2573e26" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/54122901b6d772e94f1e71a75e0533bc16563499", - "reference": "54122901b6d772e94f1e71a75e0533bc16563499", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/40a6c455ade7e3bf25900d6b746d40cfa2573e26", + "reference": "40a6c455ade7e3bf25900d6b746d40cfa2573e26", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/container": "^1.1|^2.0", "symfony/deprecation-contracts": "^2.5|^3", "symfony/service-contracts": "^3.6", - "symfony/var-exporter": "^6.4.20|^7.2.5|^8.0" + "symfony/var-exporter": "^7.4|^8.0" }, "conflict": { - "ext-psr": "<1.1|>=2", - "symfony/config": "<6.4", - "symfony/finder": "<6.4", - "symfony/yaml": "<6.4" + "ext-psr": "<1.1|>=2" }, "provide": { "psr/container-implementation": "1.1|2.0", "symfony/service-implementation": "1.1|2.0|3.0" }, "require-dev": { - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/yaml": "^6.4|^7.0|^8.0" + "symfony/config": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -2401,7 +2394,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.4.3" + "source": "https://github.com/symfony/dependency-injection/tree/v8.0.5" }, "funding": [ { @@ -2421,7 +2414,7 @@ "type": "tidelift" } ], - "time": "2025-12-28T10:55:46+00:00" + "time": "2026-01-27T16:18:07+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2492,24 +2485,24 @@ }, { "name": "symfony/event-dispatcher", - "version": "v7.4.0", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d" + "reference": "99301401da182b6cfaa4700dbe9987bb75474b47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9dddcddff1ef974ad87b3708e4b442dc38b2261d", - "reference": "9dddcddff1ef974ad87b3708e4b442dc38b2261d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/99301401da182b6cfaa4700dbe9987bb75474b47", + "reference": "99301401da182b6cfaa4700dbe9987bb75474b47", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<6.4", + "symfony/security-http": "<7.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -2518,14 +2511,14 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/error-handler": "^6.4|^7.0|^8.0", - "symfony/expression-language": "^6.4|^7.0|^8.0", - "symfony/framework-bundle": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0|^8.0" + "symfony/stopwatch": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -2553,7 +2546,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.4" }, "funding": [ { @@ -2573,7 +2566,7 @@ "type": "tidelift" } ], - "time": "2025-10-28T09:38:46+00:00" + "time": "2026-01-05T11:45:55+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -2653,25 +2646,25 @@ }, { "name": "symfony/filesystem", - "version": "v7.4.0", + "version": "v8.0.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "d551b38811096d0be9c4691d406991b47c0c630a" + "reference": "d937d400b980523dc9ee946bb69972b5e619058d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/d551b38811096d0be9c4691d406991b47c0c630a", - "reference": "d551b38811096d0be9c4691d406991b47c0c630a", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/d937d400b980523dc9ee946bb69972b5e619058d", + "reference": "d937d400b980523dc9ee946bb69972b5e619058d", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^6.4|^7.0|^8.0" + "symfony/process": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -2699,7 +2692,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.4.0" + "source": "https://github.com/symfony/filesystem/tree/v8.0.1" }, "funding": [ { @@ -2719,27 +2712,27 @@ "type": "tidelift" } ], - "time": "2025-11-27T13:27:24+00:00" + "time": "2025-12-01T09:13:36+00:00" }, { "name": "symfony/finder", - "version": "v7.4.3", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "fffe05569336549b20a1be64250b40516d6e8d06" + "reference": "8bd576e97c67d45941365bf824e18dc8538e6eb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/fffe05569336549b20a1be64250b40516d6e8d06", - "reference": "fffe05569336549b20a1be64250b40516d6e8d06", + "url": "https://api.github.com/repos/symfony/finder/zipball/8bd576e97c67d45941365bf824e18dc8538e6eb0", + "reference": "8bd576e97c67d45941365bf824e18dc8538e6eb0", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0|^8.0" + "symfony/filesystem": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -2767,7 +2760,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.3" + "source": "https://github.com/symfony/finder/tree/v8.0.5" }, "funding": [ { @@ -2787,7 +2780,7 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:50:43+00:00" + "time": "2026-01-26T15:08:38+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3450,16 +3443,16 @@ }, { "name": "symfony/process", - "version": "v8.0.3", + "version": "v8.0.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "0cbbd88ec836f8757641c651bb995335846abb78" + "reference": "b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/0cbbd88ec836f8757641c651bb995335846abb78", - "reference": "0cbbd88ec836f8757641c651bb995335846abb78", + "url": "https://api.github.com/repos/symfony/process/zipball/b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674", + "reference": "b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674", "shasum": "" }, "require": { @@ -3491,7 +3484,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v8.0.3" + "source": "https://github.com/symfony/process/tree/v8.0.5" }, "funding": [ { @@ -3511,7 +3504,7 @@ "type": "tidelift" } ], - "time": "2025-12-19T10:01:18+00:00" + "time": "2026-01-26T15:08:38+00:00" }, { "name": "symfony/service-contracts", @@ -3602,16 +3595,16 @@ }, { "name": "symfony/string", - "version": "v8.0.1", + "version": "v8.0.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc" + "reference": "758b372d6882506821ed666032e43020c4f57194" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ba65a969ac918ce0cc3edfac6cdde847eba231dc", - "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc", + "url": "https://api.github.com/repos/symfony/string/zipball/758b372d6882506821ed666032e43020c4f57194", + "reference": "758b372d6882506821ed666032e43020c4f57194", "shasum": "" }, "require": { @@ -3668,7 +3661,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.1" + "source": "https://github.com/symfony/string/tree/v8.0.4" }, "funding": [ { @@ -3688,7 +3681,7 @@ "type": "tidelift" } ], - "time": "2025-12-01T09:13:36+00:00" + "time": "2026-01-12T12:37:40+00:00" }, { "name": "symfony/var-exporter", @@ -3772,28 +3765,27 @@ }, { "name": "symfony/yaml", - "version": "v7.4.1", + "version": "v8.0.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "24dd4de28d2e3988b311751ac49e684d783e2345" + "reference": "7a1a90ba1df6e821a6b53c4cabdc32a56cabfb14" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/24dd4de28d2e3988b311751ac49e684d783e2345", - "reference": "24dd4de28d2e3988b311751ac49e684d783e2345", + "url": "https://api.github.com/repos/symfony/yaml/zipball/7a1a90ba1df6e821a6b53c4cabdc32a56cabfb14", + "reference": "7a1a90ba1df6e821a6b53c4cabdc32a56cabfb14", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.4", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<6.4" + "symfony/console": "<7.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0|^8.0" + "symfony/console": "^7.4|^8.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -3824,7 +3816,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.4.1" + "source": "https://github.com/symfony/yaml/tree/v8.0.1" }, "funding": [ { @@ -3844,7 +3836,7 @@ "type": "tidelift" } ], - "time": "2025-12-04T18:11:45+00:00" + "time": "2025-12-04T18:17:06+00:00" } ], "packages-dev": [], From 240018200105d3b5a296f42ff7537548360b1242 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Feb 2026 19:05:46 +0000 Subject: [PATCH 11/59] Update dependency mkdocs-material to v9.7.2 | datasource | package | from | to | | ---------- | --------------- | ----- | ----- | | pypi | mkdocs-material | 9.7.1 | 9.7.2 | Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 43cd91175..68da3c4d0 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,7 +1,7 @@ mkdocs==1.6.1 mike==2.1.3 markdown==3.10.2 -mkdocs-material==9.7.1 +mkdocs-material==9.7.2 # Markdown extensions Pygments==2.19.2 From 4c37ff09e8a580a123dff4423719ad3ed81a2cdc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 01:38:05 +0000 Subject: [PATCH 12/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.lock b/composer.lock index 2a4b1a849..68ef55426 100644 --- a/composer.lock +++ b/composer.lock @@ -5105,16 +5105,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.53", + "version": "11.5.55", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a997a653a82845f1240d73ee73a8a4e97e4b0607" + "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a997a653a82845f1240d73ee73a8a4e97e4b0607", - "reference": "a997a653a82845f1240d73ee73a8a4e97e4b0607", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/adc7262fccc12de2b30f12a8aa0b33775d814f00", + "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00", "shasum": "" }, "require": { @@ -5187,7 +5187,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.53" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.55" }, "funding": [ { @@ -5211,7 +5211,7 @@ "type": "tidelift" } ], - "time": "2026-02-10T12:28:25+00:00" + "time": "2026-02-18T12:37:06+00:00" }, { "name": "sanmai/di-container", @@ -7632,16 +7632,16 @@ }, { "name": "webmozart/assert", - "version": "2.1.4", + "version": "2.1.5", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "b39f1870fc7c3e9e4a26106df5053354b9260a33" + "reference": "79155f94852fa27e2f73b459f6503f5e87e2c188" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/b39f1870fc7c3e9e4a26106df5053354b9260a33", - "reference": "b39f1870fc7c3e9e4a26106df5053354b9260a33", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/79155f94852fa27e2f73b459f6503f5e87e2c188", + "reference": "79155f94852fa27e2f73b459f6503f5e87e2c188", "shasum": "" }, "require": { @@ -7688,9 +7688,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/2.1.4" + "source": "https://github.com/webmozarts/assert/tree/2.1.5" }, - "time": "2026-02-17T12:17:51+00:00" + "time": "2026-02-18T14:09:36+00:00" }, { "name": "webmozart/glob", From b05a448cde2bb4c17ccf09f892dd4944d26ad13e Mon Sep 17 00:00:00 2001 From: David Badura Date: Wed, 18 Feb 2026 12:36:07 +0100 Subject: [PATCH 13/59] allow to pass conection registry in dbal cleanup task handler --- docs/pages/aggregate.md | 2 +- docs/pages/subscription.md | 36 ++++++++++++- .../Dbal/ConnectionNameNotSupported.php | 22 ++++++++ .../Cleanup/Dbal/DbalCleanupTaskHandler.php | 26 ++++++++-- .../Cleanup/Dbal/DropIndexTask.php | 1 + .../Cleanup/Dbal/DropTableTask.php | 1 + .../Cleanup/Dbal/UnexpectedConnectionType.php | 28 +++++++++++ .../Dbal/DbalCleanupTaskHandlerTest.php | 50 +++++++++++++++++++ 8 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 src/Subscription/Cleanup/Dbal/ConnectionNameNotSupported.php create mode 100644 src/Subscription/Cleanup/Dbal/UnexpectedConnectionType.php diff --git a/docs/pages/aggregate.md b/docs/pages/aggregate.md index 57992320c..85655319c 100644 --- a/docs/pages/aggregate.md +++ b/docs/pages/aggregate.md @@ -365,7 +365,7 @@ final class Profile extends BasicAggregateRoot ## Shared apply context -When working with [micro-aggregates](./aggregate.md#micro-aggregates), +When working with [micro-aggregates](./aggregate.md#micro-aggregates), it’s common that events are applied by different aggregates. As a result, an aggregate may receive events it does not handle, which can lead to multiple “missing apply” warnings. diff --git a/docs/pages/subscription.md b/docs/pages/subscription.md index e1512adc5..65f6aa6ae 100644 --- a/docs/pages/subscription.md +++ b/docs/pages/subscription.md @@ -422,6 +422,11 @@ Default, we provide the following cleanup tasks for `doctrine/dbal`: | `DropIndexTask` | Drops an index from a table. | | `DropTableTask` | Drops a table. | +!!! note + + If you are passing connection registry, you can use the connection name as parameter. + The `connectionName` parameter is optional and defaults to the default connection. + !!! tip You can create your own cleanup tasks and handler. @@ -1054,11 +1059,9 @@ Lastly, we have to add the new handler to `DefaultCleaner`, which is responsible for cleaning up subscriptions. ```php -use Patchlevel\EventSourcing\Subscription\Cleanup\Dbal\DbalCleanupTaskHandler; use Patchlevel\EventSourcing\Subscription\Cleanup\DefaultCleaner; $cleaner = new DefaultCleaner([ - new DbalCleanupTaskHandler($projectionConnection), new MongodbCleanupTaskHandler($mongodbDatabase), ]); ``` @@ -1066,6 +1069,35 @@ $cleaner = new DefaultCleaner([ You need to pass the Cleaner to the Subscription Engine. +#### Dbal Cleanup Task Handler + +We provide a Dbal cleanup task handler by default. +More information about the available tasks can be found in the [Dbal Cleanup Tasks](#dbal-cleanup-tasks) documentation. + +```php +use Doctrine\Dbal\Connection; +use Patchlevel\EventSourcing\Subscription\Cleanup\Dbal\DbalCleanupTaskHandler; +use Patchlevel\EventSourcing\Subscription\Cleanup\DefaultCleaner; + +/** @var Connection $connection */ +$cleaner = new DefaultCleaner([ + new DbalCleanupTaskHandler($connection), +]); +``` +If you have multiple database connections and want to use the `DbalCleanupTaskHandler` to clean up the respective databases, +you can also pass a `ConnectionRegistry` (from `doctrine/persistence`) to the `DbalCleanupTaskHandler`. +Then you can pass the connection name as parameter in the cleanup task and the handler will use the corresponding connection to execute the task. + +```php +use Doctrine\Persistence\ConnectionRegistry; +use Patchlevel\EventSourcing\Subscription\Cleanup\Dbal\DbalCleanupTaskHandler; +use Patchlevel\EventSourcing\Subscription\Cleanup\DefaultCleaner; + +/** @var ConnectionRegistry $connectionRegistry */ +$cleaner = new DefaultCleaner([ + new DbalCleanupTaskHandler($connectionRegistry), +]); +``` ### Subscriber Accessor The subscriber accessor repository is responsible for providing the subscribers to the subscription engine. diff --git a/src/Subscription/Cleanup/Dbal/ConnectionNameNotSupported.php b/src/Subscription/Cleanup/Dbal/ConnectionNameNotSupported.php new file mode 100644 index 000000000..638478d0d --- /dev/null +++ b/src/Subscription/Cleanup/Dbal/ConnectionNameNotSupported.php @@ -0,0 +1,22 @@ +connection->createSchemaManager()->dropTable($task->table); + $this->connection($task->connectionName)->createSchemaManager()->dropTable($task->table); return; } if ($task instanceof DropIndexTask) { - $this->connection->createSchemaManager()->dropIndex($task->index, $task->table); + $this->connection($task->connectionName)->createSchemaManager()->dropIndex($task->index, $task->table); return; } @@ -36,4 +37,23 @@ public function supports(object $task): bool { return $task instanceof DropTableTask || $task instanceof DropIndexTask; } + + private function connection(string|null $connectionName): Connection + { + if ($this->connection instanceof ConnectionRegistry) { + $connection = $this->connection->getConnection($connectionName); + + if (!$connection instanceof Connection) { + throw new UnexpectedConnectionType($connectionName, Connection::class, $connection::class); + } + + return $connection; + } + + if ($connectionName === null) { + return $this->connection; + } + + throw new ConnectionNameNotSupported($connectionName); + } } diff --git a/src/Subscription/Cleanup/Dbal/DropIndexTask.php b/src/Subscription/Cleanup/Dbal/DropIndexTask.php index 227f2c558..6dcad5066 100644 --- a/src/Subscription/Cleanup/Dbal/DropIndexTask.php +++ b/src/Subscription/Cleanup/Dbal/DropIndexTask.php @@ -9,6 +9,7 @@ final class DropIndexTask public function __construct( public readonly string $index, public readonly string $table, + public readonly string|null $connectionName = null, ) { } } diff --git a/src/Subscription/Cleanup/Dbal/DropTableTask.php b/src/Subscription/Cleanup/Dbal/DropTableTask.php index 46e62b4e2..73a363da5 100644 --- a/src/Subscription/Cleanup/Dbal/DropTableTask.php +++ b/src/Subscription/Cleanup/Dbal/DropTableTask.php @@ -8,6 +8,7 @@ final class DropTableTask { public function __construct( public readonly string $table, + public readonly string|null $connectionName = null, ) { } } diff --git a/src/Subscription/Cleanup/Dbal/UnexpectedConnectionType.php b/src/Subscription/Cleanup/Dbal/UnexpectedConnectionType.php new file mode 100644 index 000000000..cad8b2d1b --- /dev/null +++ b/src/Subscription/Cleanup/Dbal/UnexpectedConnectionType.php @@ -0,0 +1,28 @@ +createMock(AbstractSchemaManager::class); + $schemaManager->expects($this->once())->method('dropTable')->with('test'); + + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->once()) + ->method('createSchemaManager') + ->willReturn($schemaManager); + + $registry = $this->createMock(ConnectionRegistry::class); + $registry + ->expects($this->once()) + ->method('getConnection') + ->with('foo') + ->willReturn($connection); + + $handler = new DbalCleanupTaskHandler($registry); + + $handler(new DropTableTask('test', 'foo')); + } + + public function testHandleWithConnectionRegistryAndUnexpectedType(): void + { + $registry = $this->createMock(ConnectionRegistry::class); + $registry + ->expects($this->once()) + ->method('getConnection') + ->with('foo') + ->willReturn(new stdClass()); + + $handler = new DbalCleanupTaskHandler($registry); + + $this->expectException(UnexpectedConnectionType::class); + $handler(new DropTableTask('test', 'foo')); + } + + public function testHandleWithConnectionAndConnectionNameNotSupported(): void + { + $connection = $this->createMock(Connection::class); + $handler = new DbalCleanupTaskHandler($connection); + + $this->expectException(ConnectionNameNotSupported::class); + $handler(new DropTableTask('test', 'foo')); + } } From 4c79ed57f033116929d2ff02b2f88ed4d216dfdb Mon Sep 17 00:00:00 2001 From: David Badura Date: Thu, 19 Feb 2026 18:46:57 +0100 Subject: [PATCH 14/59] allow multiple hander, union types and inheritance in command bus --- docs/pages/command_bus.md | 53 ++++++++ src/Attribute/Handle.php | 2 +- src/CommandBus/AggregateHandlerProvider.php | 22 +++- src/CommandBus/HandlerFinder.php | 114 ++++++++++++------ src/CommandBus/ServiceHandlerProvider.php | 22 +++- .../AggregateHandlerProviderTest.php | 57 ++++++++- tests/Unit/CommandBus/HandlerFinderTest.php | 36 ++++++ .../CommandBus/ServiceHandlerProviderTest.php | 48 +++++++- tests/Unit/Fixture/BaseCommand.php | 9 ++ .../Fixture/ProfileWithInheritanceHandler.php | 33 +++++ tests/Unit/Fixture/SomeCommand.php | 9 ++ 11 files changed, 358 insertions(+), 47 deletions(-) create mode 100644 tests/Unit/Fixture/BaseCommand.php create mode 100644 tests/Unit/Fixture/ProfileWithInheritanceHandler.php create mode 100644 tests/Unit/Fixture/SomeCommand.php diff --git a/docs/pages/command_bus.md b/docs/pages/command_bus.md index 75d23da1c..15010dde6 100644 --- a/docs/pages/command_bus.md +++ b/docs/pages/command_bus.md @@ -45,6 +45,59 @@ final class CreateProfileHandler !!! tip A class can have multiple handle methods. + +### Multiple Handle Attributes + +A method can also have multiple `#[Handle]` attributes. +This is useful if you want to handle different commands with the same method. + +```php +use Patchlevel\EventSourcing\Attribute\Handle; + +final class CreateProfileHandler +{ + #[Handle(CreateProfile::class)] + #[Handle(UpdateProfile::class)] + public function __invoke(object $command): void + { + // handle both commands + } +} +``` + +### Union Types + +You can also use union types to handle multiple commands and the library will automatically detect the commands. + +```php +use Patchlevel\EventSourcing\Attribute\Handle; + +final class CreateProfileHandler +{ + #[Handle] + public function __invoke(CreateProfile|UpdateProfile $command): void + { + // handle both commands + } +} +``` + +### Inheritance + +The handler will also be invoked if the command implements an interface or extends a class that the handler expects. + +```php +use Patchlevel\EventSourcing\Attribute\Handle; + +final class CreateProfileHandler +{ + #[Handle] + public function __invoke(CommandInterface $command): void + { + // handle all commands that implement CommandInterface + } +} +``` ### Aggregate Handler diff --git a/src/Attribute/Handle.php b/src/Attribute/Handle.php index 65a5bdfa3..bea54fe8c 100644 --- a/src/Attribute/Handle.php +++ b/src/Attribute/Handle.php @@ -6,7 +6,7 @@ use Attribute; -#[Attribute(Attribute::TARGET_METHOD)] +#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] final class Handle { /** @param class-string|null $commandClass */ diff --git a/src/CommandBus/AggregateHandlerProvider.php b/src/CommandBus/AggregateHandlerProvider.php index bdab44f70..13d050da0 100644 --- a/src/CommandBus/AggregateHandlerProvider.php +++ b/src/CommandBus/AggregateHandlerProvider.php @@ -11,6 +11,9 @@ use Patchlevel\EventSourcing\Repository\RepositoryManager; use Psr\Container\ContainerInterface; +use function class_implements; +use function class_parents; + final class AggregateHandlerProvider implements HandlerProvider { private bool $initialized = false; @@ -36,7 +39,9 @@ public function handlerForCommand(string $commandClass): iterable $this->initialize(); } - return $this->handlers[$commandClass] ?? []; + foreach (self::resolveClasses($commandClass) as $class) { + yield from $this->handlers[$class] ?? []; + } } private function initialize(): void @@ -69,4 +74,19 @@ private function initialize(): void $this->initialized = true; } + + /** + * @param class-string $class + * + * @return array + */ + private static function resolveClasses(string $class): array + { + /** @var array $classes */ + $classes = [$class => $class] + + class_parents($class) + + class_implements($class); + + return $classes; + } } diff --git a/src/CommandBus/HandlerFinder.php b/src/CommandBus/HandlerFinder.php index f00af8609..69c0f12d6 100644 --- a/src/CommandBus/HandlerFinder.php +++ b/src/CommandBus/HandlerFinder.php @@ -6,11 +6,15 @@ use Patchlevel\EventSourcing\Attribute\Handle; use ReflectionClass; +use ReflectionMethod; use Symfony\Component\TypeInfo\Type\ObjectType; +use Symfony\Component\TypeInfo\Type\UnionType; use Symfony\Component\TypeInfo\TypeResolver\TypeResolver; final class HandlerFinder { + private static TypeResolver $typeResolver; + /** * @param class-string $classString * @@ -18,7 +22,6 @@ final class HandlerFinder */ public static function findInClass(string $classString): iterable { - $typeResolver = TypeResolver::create(); $reflectionClass = new ReflectionClass($classString); foreach ($reflectionClass->getMethods() as $reflectionMethod) { @@ -28,53 +31,88 @@ public static function findInClass(string $classString): iterable continue; } - $handle = $handleAttributes[0]->newInstance(); + foreach ($handleAttributes as $attribute) { + $handle = $attribute->newInstance(); + + if ($handle->commandClass !== null) { + yield new HandlerReference( + $handle->commandClass, + $reflectionMethod->getName(), + $reflectionMethod->isStatic(), + ); + + continue; + } + + foreach (self::guessHandledClasses($reflectionMethod) as $class) { + yield new HandlerReference( + $class, + $reflectionMethod->getName(), + $reflectionMethod->isStatic(), + ); + } + } + } + } - if ($handle->commandClass !== null) { - yield new HandlerReference( - $handle->commandClass, - $reflectionMethod->getName(), - $reflectionMethod->isStatic(), - ); + /** @return list */ + private static function guessHandledClasses(ReflectionMethod $reflectionMethod): array + { + $parameters = $reflectionMethod->getParameters(); - continue; - } + if ($parameters === []) { + throw InvalidHandleMethod::noParameters( + $reflectionMethod->getDeclaringClass()->getName(), + $reflectionMethod->getName(), + ); + } - $parameters = $reflectionMethod->getParameters(); + $reflectionType = $parameters[0]->getType(); - if ($parameters === []) { - throw InvalidHandleMethod::noParameters( - $reflectionMethod->getDeclaringClass()->getName(), - $reflectionMethod->getName(), - ); - } + if ($reflectionType === null) { + throw InvalidHandleMethod::incompatibleType( + $reflectionMethod->getDeclaringClass()->getName(), + $reflectionMethod->getName(), + ); + } - $reflectionType = $parameters[0]->getType(); + $type = self::typeResolver()->resolve($reflectionType); - if ($reflectionType === null) { - throw InvalidHandleMethod::incompatibleType( - $reflectionMethod->getDeclaringClass()->getName(), - $reflectionMethod->getName(), - ); - } + if ($type instanceof ObjectType) { + /** @var class-string $className */ + $className = $type->getClassName(); - $type = $typeResolver->resolve($reflectionType); + return [$className]; + } - if (!$type instanceof ObjectType) { - throw InvalidHandleMethod::incompatibleType( - $reflectionMethod->getDeclaringClass()->getName(), - $reflectionMethod->getName(), - ); - } + if ($type instanceof UnionType) { + $types = []; - /** @var class-string $commandClass */ - $commandClass = $type->getClassName(); + foreach ($type->getTypes() as $unionType) { + if (!$unionType instanceof ObjectType) { + throw InvalidHandleMethod::incompatibleType( + $reflectionMethod->getDeclaringClass()->getName(), + $reflectionMethod->getName(), + ); + } - yield new HandlerReference( - $commandClass, - $reflectionMethod->getName(), - $reflectionMethod->isStatic(), - ); + /** @var class-string $className */ + $className = $unionType->getClassName(); + + $types[] = $className; + } + + return $types; } + + throw InvalidHandleMethod::incompatibleType( + $reflectionMethod->getDeclaringClass()->getName(), + $reflectionMethod->getName(), + ); + } + + private static function typeResolver(): TypeResolver + { + return self::$typeResolver ??= TypeResolver::create(); } } diff --git a/src/CommandBus/ServiceHandlerProvider.php b/src/CommandBus/ServiceHandlerProvider.php index 656a936ee..82beb319d 100644 --- a/src/CommandBus/ServiceHandlerProvider.php +++ b/src/CommandBus/ServiceHandlerProvider.php @@ -4,6 +4,9 @@ namespace Patchlevel\EventSourcing\CommandBus; +use function class_implements; +use function class_parents; + final class ServiceHandlerProvider implements HandlerProvider { private bool $initialized = false; @@ -28,7 +31,9 @@ public function handlerForCommand(string $commandClass): iterable $this->initialize(); } - return $this->handlers[$commandClass] ?? []; + foreach (self::resolveClasses($commandClass) as $class) { + yield from $this->handlers[$class] ?? []; + } } private function initialize(): void @@ -51,4 +56,19 @@ private function initialize(): void $this->initialized = true; } + + /** + * @param class-string $class + * + * @return array + */ + private static function resolveClasses(string $class): array + { + /** @var array $classes */ + $classes = [$class => $class] + + class_parents($class) + + class_implements($class); + + return $classes; + } } diff --git a/tests/Unit/CommandBus/AggregateHandlerProviderTest.php b/tests/Unit/CommandBus/AggregateHandlerProviderTest.php index 5db923df9..e4ece227d 100644 --- a/tests/Unit/CommandBus/AggregateHandlerProviderTest.php +++ b/tests/Unit/CommandBus/AggregateHandlerProviderTest.php @@ -10,9 +10,12 @@ use Patchlevel\EventSourcing\CommandBus\Handler\UpdateAggregateHandler; use Patchlevel\EventSourcing\Metadata\AggregateRoot\AggregateRootRegistry; use Patchlevel\EventSourcing\Repository\RepositoryManager; +use Patchlevel\EventSourcing\Tests\Unit\Fixture\BaseCommand; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ChangeProfileName; use Patchlevel\EventSourcing\Tests\Unit\Fixture\CreateProfile; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileWithHandler; +use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileWithInheritanceHandler; +use Patchlevel\EventSourcing\Tests\Unit\Fixture\SomeCommand; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; @@ -28,7 +31,7 @@ public function testEmpty(): void $repositoryManager, ); - $result = $provider->handlerForCommand(CreateProfile::class); + $result = [...$provider->handlerForCommand(CreateProfile::class)]; self::assertCount(0, $result); } @@ -42,7 +45,7 @@ public function testGetCreateHandler(): void $repositoryManager, ); - $result = $provider->handlerForCommand(CreateProfile::class); + $result = [...$provider->handlerForCommand(CreateProfile::class)]; $handler = new CreateAggregateHandler( $repositoryManager, @@ -64,7 +67,7 @@ public function testGetUpdateHandler(): void $repositoryManager, ); - $result = $provider->handlerForCommand(ChangeProfileName::class); + $result = [...$provider->handlerForCommand(ChangeProfileName::class)]; $handler = new UpdateAggregateHandler( $repositoryManager, @@ -76,4 +79,52 @@ public function testGetUpdateHandler(): void self::assertCount(1, $result); self::assertEquals($handler->__invoke(...), $result[0]->callable()); } + + public function testGetHandlerByInterface(): void + { + $repositoryManager = $this->createMock(RepositoryManager::class); + $command = new class () implements SomeCommand { + }; + + $provider = new AggregateHandlerProvider( + new AggregateRootRegistry(['profile' => ProfileWithInheritanceHandler::class]), + $repositoryManager, + ); + + $result = [...$provider->handlerForCommand($command::class)]; + + $handler = new UpdateAggregateHandler( + $repositoryManager, + ProfileWithInheritanceHandler::class, + 'handleInterface', + new DefaultParameterResolver(), + ); + + self::assertCount(1, $result); + self::assertEquals($handler->__invoke(...), $result[0]->callable()); + } + + public function testGetHandlerByAbstractClass(): void + { + $repositoryManager = $this->createMock(RepositoryManager::class); + $command = new class () extends BaseCommand { + }; + + $provider = new AggregateHandlerProvider( + new AggregateRootRegistry(['profile' => ProfileWithInheritanceHandler::class]), + $repositoryManager, + ); + + $result = [...$provider->handlerForCommand($command::class)]; + + $handler = new UpdateAggregateHandler( + $repositoryManager, + ProfileWithInheritanceHandler::class, + 'handleAbstract', + new DefaultParameterResolver(), + ); + + self::assertCount(1, $result); + self::assertEquals($handler->__invoke(...), $result[0]->callable()); + } } diff --git a/tests/Unit/CommandBus/HandlerFinderTest.php b/tests/Unit/CommandBus/HandlerFinderTest.php index 6a038def0..5b11014d0 100644 --- a/tests/Unit/CommandBus/HandlerFinderTest.php +++ b/tests/Unit/CommandBus/HandlerFinderTest.php @@ -8,6 +8,7 @@ use Patchlevel\EventSourcing\CommandBus\HandlerFinder; use Patchlevel\EventSourcing\CommandBus\HandlerReference; use Patchlevel\EventSourcing\CommandBus\InvalidHandleMethod; +use Patchlevel\EventSourcing\Tests\Unit\Fixture\ChangeProfileName; use Patchlevel\EventSourcing\Tests\Unit\Fixture\CreateProfile; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; @@ -105,4 +106,39 @@ public function handle(CreateProfile $command): void new HandlerReference(CreateProfile::class, 'handle', false), ], $result); } + + public function testWithUnionTypeGuessing(): void + { + $class = new class () { + #[Handle] + public function handle(CreateProfile|ChangeProfileName $command): void + { + } + }; + + $result = [...HandlerFinder::findInClass($class::class)]; + + self::assertEquals([ + new HandlerReference(ChangeProfileName::class, 'handle', false), + new HandlerReference(CreateProfile::class, 'handle', false), + ], $result); + } + + public function testWithMultipleAttributes(): void + { + $class = new class () { + #[Handle(CreateProfile::class)] + #[Handle(ChangeProfileName::class)] + public function handle(CreateProfile $command): void + { + } + }; + + $result = [...HandlerFinder::findInClass($class::class)]; + + self::assertEquals([ + new HandlerReference(CreateProfile::class, 'handle', false), + new HandlerReference(ChangeProfileName::class, 'handle', false), + ], $result); + } } diff --git a/tests/Unit/CommandBus/ServiceHandlerProviderTest.php b/tests/Unit/CommandBus/ServiceHandlerProviderTest.php index 571f874a6..06a06a780 100644 --- a/tests/Unit/CommandBus/ServiceHandlerProviderTest.php +++ b/tests/Unit/CommandBus/ServiceHandlerProviderTest.php @@ -6,7 +6,9 @@ use Patchlevel\EventSourcing\Attribute\Handle; use Patchlevel\EventSourcing\CommandBus\ServiceHandlerProvider; +use Patchlevel\EventSourcing\Tests\Unit\Fixture\BaseCommand; use Patchlevel\EventSourcing\Tests\Unit\Fixture\CreateProfile; +use Patchlevel\EventSourcing\Tests\Unit\Fixture\SomeCommand; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; @@ -16,7 +18,7 @@ final class ServiceHandlerProviderTest extends TestCase public function testEmpty(): void { $provider = new ServiceHandlerProvider([]); - $result = $provider->handlerForCommand(CreateProfile::class); + $result = [...$provider->handlerForCommand(CreateProfile::class)]; self::assertCount(0, $result); } @@ -32,7 +34,7 @@ public function handle(): void $provider = new ServiceHandlerProvider([$class]); - $result = $provider->handlerForCommand(CreateProfile::class); + $result = [...$provider->handlerForCommand(CreateProfile::class)]; self::assertCount(1, $result); self::assertEquals($class->handle(...), $result[0]->callable()); @@ -49,9 +51,49 @@ public static function handle(): void $provider = new ServiceHandlerProvider([$class]); - $result = $provider->handlerForCommand(CreateProfile::class); + $result = [...$provider->handlerForCommand(CreateProfile::class)]; self::assertCount(1, $result); self::assertEquals($class::handle(...), $result[0]->callable()); } + + public function testFindHandlerByInterface(): void + { + $command = new class () implements SomeCommand { + }; + + $class = new class () { + #[Handle] + public function handle(SomeCommand $command): void + { + } + }; + + $provider = new ServiceHandlerProvider([$class]); + + $result = [...$provider->handlerForCommand($command::class)]; + + self::assertCount(1, $result); + self::assertEquals($class->handle(...), $result[0]->callable()); + } + + public function testFindHandlerByParentClass(): void + { + $command = new class () extends BaseCommand { + }; + + $class = new class () { + #[Handle] + public function handle(BaseCommand $command): void + { + } + }; + + $provider = new ServiceHandlerProvider([$class]); + + $result = [...$provider->handlerForCommand($command::class)]; + + self::assertCount(1, $result); + self::assertEquals($class->handle(...), $result[0]->callable()); + } } diff --git a/tests/Unit/Fixture/BaseCommand.php b/tests/Unit/Fixture/BaseCommand.php new file mode 100644 index 000000000..d7aedbfd5 --- /dev/null +++ b/tests/Unit/Fixture/BaseCommand.php @@ -0,0 +1,9 @@ +id); + } +} diff --git a/tests/Unit/Fixture/SomeCommand.php b/tests/Unit/Fixture/SomeCommand.php new file mode 100644 index 000000000..3019dbaf5 --- /dev/null +++ b/tests/Unit/Fixture/SomeCommand.php @@ -0,0 +1,9 @@ + Date: Fri, 20 Feb 2026 17:13:38 +0100 Subject: [PATCH 15/59] add auto initializable aggregate feature --- docs/pages/aggregate.md | 47 +++++++++++ docs/pages/command_bus.md | 5 ++ docs/pages/repository.md | 9 ++- src/Attribute/AutoInitialize.php | 13 ++++ .../AggregateRoot/AggregateRootMetadata.php | 1 + .../AttributeAggregateRootMetadataFactory.php | 16 ++++ src/Repository/DefaultRepository.php | 26 +++++++ src/Repository/InvalidAggregate.php | 26 +++++++ .../BasicIntegrationTest.php | 48 ++++++++++++ .../Command/AdjustStockForProduct.php | 20 +++++ .../Command/DecreaseStockForProduct.php | 20 +++++ .../Events/StockAdjusted.php | 20 +++++ .../Events/StockCreated.php | 17 ++++ .../Events/StockDecreased.php | 20 +++++ .../BasicImplementation/ProductId.php | 13 ++++ .../Integration/BasicImplementation/Stock.php | 77 +++++++++++++++++++ .../BasicImplementation/StockId.php | 18 +++++ .../Unit/Fixture/AutoInitializableProfile.php | 38 +++++++++ .../Unit/Repository/DefaultRepositoryTest.php | 17 ++++ 19 files changed, 449 insertions(+), 2 deletions(-) create mode 100644 src/Attribute/AutoInitialize.php create mode 100644 src/Repository/InvalidAggregate.php create mode 100644 tests/Integration/BasicImplementation/Command/AdjustStockForProduct.php create mode 100644 tests/Integration/BasicImplementation/Command/DecreaseStockForProduct.php create mode 100644 tests/Integration/BasicImplementation/Events/StockAdjusted.php create mode 100644 tests/Integration/BasicImplementation/Events/StockCreated.php create mode 100644 tests/Integration/BasicImplementation/Events/StockDecreased.php create mode 100644 tests/Integration/BasicImplementation/ProductId.php create mode 100644 tests/Integration/BasicImplementation/Stock.php create mode 100644 tests/Integration/BasicImplementation/StockId.php create mode 100644 tests/Unit/Fixture/AutoInitializableProfile.php diff --git a/docs/pages/aggregate.md b/docs/pages/aggregate.md index 85655319c..015f4de52 100644 --- a/docs/pages/aggregate.md +++ b/docs/pages/aggregate.md @@ -876,6 +876,53 @@ final class Order extends BasicAggregateRoot } } ``` + +## Auto Initialize + +??? example "Experimental" + + This feature is still experimental and may change in the future. + Use it with caution. + +Sometimes you want to be able to access an aggregate even if it has not yet been created in the system. +In this case, the aggregate should be automatically initialized if it cannot be found in the store. +To achieve this, the aggregate must mark the initialization method with the `AutoInitialize` attribute. +The method must be static, receives the aggregate ID as an argument and must return an instance of the aggregate. + +```php +use Patchlevel\EventSourcing\Aggregate\BasicAggregateRoot; +use Patchlevel\EventSourcing\Aggregate\Uuid; +use Patchlevel\EventSourcing\Attribute\Aggregate; +use Patchlevel\EventSourcing\Attribute\Apply; +use Patchlevel\EventSourcing\Attribute\AutoInitialize; +use Patchlevel\EventSourcing\Attribute\Id; + +#[Aggregate('profile')] +final class Profile extends BasicAggregateRoot +{ + #[Id] + private Uuid $id; + + #[AutoInitialize] + public static function initialize(Uuid $id): static + { + $self = new static(); + $self->recordThat(new ProfileCreated($id)); + + return $self; + } + + #[Apply] + public function applyProfileCreated(ProfileCreated $event): void + { + $this->id = $event->id; + } +} +``` +!!! note + + Recording events in the `initialize` method is optional but recommended. + ## Aggregate Root Registry The library needs to know about all aggregates so that the correct aggregate class is used to load from the database. diff --git a/docs/pages/command_bus.md b/docs/pages/command_bus.md index 15010dde6..ea11339d7 100644 --- a/docs/pages/command_bus.md +++ b/docs/pages/command_bus.md @@ -191,6 +191,11 @@ final class Profile extends BasicAggregateRoot // ... apply methods } ``` +!!! tip + + If you want to automatically initialize an aggregate if it cannot be found in the store, + you can use the [Auto Initialize](aggregate.md#auto-initialize) feature. + #### Inject Service You can inject services into aggregate handler methods. diff --git a/docs/pages/repository.md b/docs/pages/repository.md index cd0cd0a42..5285658d7 100644 --- a/docs/pages/repository.md +++ b/docs/pages/repository.md @@ -198,12 +198,17 @@ $profile = $repository->load($id); !!! warning When the method is called, the aggregate is always reloaded and rebuilt from the database. - + !!! note You can only fetch one aggregate at a time and don't do any complex queries either. Projections are used for this purpose. - + +!!! tip + + If you want to automatically initialize an aggregate if it cannot be found in the store, + you can use the [Auto Initialize](aggregate.md#auto-initialize) feature. + ### Has an aggregate You can also check whether an `aggregate` with a certain id exists. diff --git a/src/Attribute/AutoInitialize.php b/src/Attribute/AutoInitialize.php new file mode 100644 index 000000000..fa175edd4 --- /dev/null +++ b/src/Attribute/AutoInitialize.php @@ -0,0 +1,13 @@ + */ public readonly array $childAggregates = [], string|null $streamName = null, + public readonly string|null $autoInitializeMethod = null, ) { $this->streamName = $streamName ?? $this->name . '-{id}'; } diff --git a/src/Metadata/AggregateRoot/AttributeAggregateRootMetadataFactory.php b/src/Metadata/AggregateRoot/AttributeAggregateRootMetadataFactory.php index db328eca7..4ec2a8c11 100644 --- a/src/Metadata/AggregateRoot/AttributeAggregateRootMetadataFactory.php +++ b/src/Metadata/AggregateRoot/AttributeAggregateRootMetadataFactory.php @@ -7,6 +7,7 @@ use Patchlevel\EventSourcing\Aggregate\AggregateRoot; use Patchlevel\EventSourcing\Attribute\Aggregate; use Patchlevel\EventSourcing\Attribute\Apply; +use Patchlevel\EventSourcing\Attribute\AutoInitialize; use Patchlevel\EventSourcing\Attribute\ChildAggregate; use Patchlevel\EventSourcing\Attribute\Id; use Patchlevel\EventSourcing\Attribute\SharedApplyContext; @@ -52,6 +53,7 @@ public function metadata(string $aggregate): AggregateRootMetadata [$suppressEvents, $suppressAll] = $this->findSuppressMissingApply($reflectionClass); $applyMethods = $this->findApplyMethods($reflectionClass, $aggregate, $childAggregates); $snapshot = $this->findSnapshot($reflectionClass); + $autoInitializeMethod = $this->findAutoInitializeMethod($reflectionClass); $metadata = new AggregateRootMetadata( $aggregate, @@ -63,6 +65,7 @@ public function metadata(string $aggregate): AggregateRootMetadata $snapshot, array_map(static fn (array $list) => $list[0], $childAggregates), $this->findStreamName($reflectionClass), + $autoInitializeMethod, ); $this->aggregateMetadata[$aggregate] = $metadata; @@ -176,6 +179,19 @@ private function findStreamName(ReflectionClass $reflector): string|null return $attributes[0]->newInstance()->name; } + private function findAutoInitializeMethod(ReflectionClass $reflector): string|null + { + foreach ($reflector->getMethods() as $method) { + $attributes = $method->getAttributes(AutoInitialize::class); + + if ($attributes !== []) { + return $method->getName(); + } + } + + return null; + } + /** @return list */ private function findChildAggregates(ReflectionClass $reflector): array { diff --git a/src/Repository/DefaultRepository.php b/src/Repository/DefaultRepository.php index 2a0bc24b8..2b99d6f23 100644 --- a/src/Repository/DefaultRepository.php +++ b/src/Repository/DefaultRepository.php @@ -40,6 +40,8 @@ use function array_map; use function assert; use function count; +use function is_a; +use function is_object; use function sprintf; /** @@ -138,6 +140,30 @@ public function load(AggregateRootId $id): AggregateRoot $firstMessage = $stream->current(); if ($firstMessage === null) { + if ($this->metadata->autoInitializeMethod) { + $aggregate = $this->metadata->className::{$this->metadata->autoInitializeMethod}($id); + + if (!is_object($aggregate) || !is_a($aggregate, $this->metadata->className, true)) { + throw new InvalidAggregate( + $this->metadata->autoInitializeMethod, + $this->metadata->className, + $aggregate, + ); + } + + $this->logger->debug( + sprintf( + 'Repository: Auto initialize aggregate "%s" with the id "%s".', + $this->metadata->name, + $id->toString(), + ), + ); + + $this->aggregateIsValid[$aggregate] = true; + + return $aggregate; + } + $this->logger->debug( sprintf( 'Repository: Aggregate "%s" with the id "%s" not found.', diff --git a/src/Repository/InvalidAggregate.php b/src/Repository/InvalidAggregate.php new file mode 100644 index 000000000..358af9f79 --- /dev/null +++ b/src/Repository/InvalidAggregate.php @@ -0,0 +1,26 @@ +connection, + DefaultEventSerializer::createFromPaths([__DIR__ . '/Events']), + DefaultHeadersSerializer::createFromPaths([ + __DIR__ . '/Header', + ]), + ); + + $aggregateRootRegistry = new AggregateRootRegistry(['stock' => Stock::class]); + + $manager = new DefaultRepositoryManager( + $aggregateRootRegistry, + $store, + null, + new DefaultSnapshotStore(['default' => new InMemorySnapshotAdapter()]), + new FooMessageDecorator(), + ); + + $commandBus = SyncCommandBus::createForAggregateHandlers( + $aggregateRootRegistry, + $manager, + ); + + $schemaDirector = new DoctrineSchemaDirector( + $this->connection, + $store, + ); + + $schemaDirector->create(); + + $stockId = StockId::create(); + $productId = ProductId::generate(); + + $commandBus->dispatch(new AdjustStockForProduct($stockId, $productId, 5)); + $commandBus->dispatch(new DecreaseStockForProduct($stockId, $productId, 3)); + + $repository = $manager->get(Stock::class); + $stock = $repository->load($stockId); + + self::assertEquals($stockId, $stock->aggregateRootId()); + self::assertSame(3, $stock->playhead()); + self::assertSame(2, $stock->stockFor($productId)); + } } diff --git a/tests/Integration/BasicImplementation/Command/AdjustStockForProduct.php b/tests/Integration/BasicImplementation/Command/AdjustStockForProduct.php new file mode 100644 index 000000000..e08242aab --- /dev/null +++ b/tests/Integration/BasicImplementation/Command/AdjustStockForProduct.php @@ -0,0 +1,20 @@ + */ + private array $stock; + + #[AutoInitialize] + public static function initialize(StockId $id): static + { + $stock = new self(); + $stock->recordThat(new StockCreated($id)); + + return $stock; + } + + public function id(): StockId + { + return $this->id; + } + + public function stockFor(ProductId $productId): int + { + return $this->stock[$productId->toString()] ?? 0; + } + + #[Handle] + public function decreaseStock(DecreaseStockForProduct $command): void + { + $this->recordThat(new StockDecreased($this->id, $command->productId, $command->quantity)); + } + + #[Handle] + public function adjustStock(AdjustStockForProduct $command): void + { + $this->recordThat(new StockAdjusted($this->id, $command->productId, $command->quantity)); + } + + #[Apply] + protected function applyStockCreated(StockCreated $event): void + { + $this->id = $event->stockId; + $this->stock = []; + } + + #[Apply] + protected function applyStockDecreased(StockDecreased $event): void + { + $this->stock[$event->productId->toString()] = $this->stockFor($event->productId) - $event->quantity; + } + + #[Apply] + protected function applyStockAdjusted(StockAdjusted $event): void + { + $this->stock[$event->productId->toString()] = $event->quantity; + } +} diff --git a/tests/Integration/BasicImplementation/StockId.php b/tests/Integration/BasicImplementation/StockId.php new file mode 100644 index 000000000..92fcc68d4 --- /dev/null +++ b/tests/Integration/BasicImplementation/StockId.php @@ -0,0 +1,18 @@ +id; + } + + #[AutoInitialize] + public static function initialize(ProfileId $id): static + { + $self = new self(); + $self->recordThat(new ProfileCreated($id, Email::fromString('initial@patchlevel.de'))); + + return $self; + } + + #[Apply] + protected function applyProfileCreated(ProfileCreated $event): void + { + $this->id = $event->profileId; + } +} diff --git a/tests/Unit/Repository/DefaultRepositoryTest.php b/tests/Unit/Repository/DefaultRepositoryTest.php index 6560d8367..44a93b9d9 100644 --- a/tests/Unit/Repository/DefaultRepositoryTest.php +++ b/tests/Unit/Repository/DefaultRepositoryTest.php @@ -34,6 +34,7 @@ use Patchlevel\EventSourcing\Store\Store; use Patchlevel\EventSourcing\Store\StreamStore; use Patchlevel\EventSourcing\Store\UniqueConstraintViolation; +use Patchlevel\EventSourcing\Tests\Unit\Fixture\AutoInitializableProfile; use Patchlevel\EventSourcing\Tests\Unit\Fixture\Email; use Patchlevel\EventSourcing\Tests\Unit\Fixture\Profile; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileCreated; @@ -750,4 +751,20 @@ public function testLoadAggregateFromOtherStream(): void self::assertEquals(ProfileId::fromString('1'), $aggregate->id()); self::assertEquals(Email::fromString('hallo@patchlevel.de'), $aggregate->email()); } + + public function testLoadInitializableAggregate(): void + { + $store = $this->createMock(Store::class); + $store + ->expects($this->once()) + ->method('load') + ->willReturn(new ArrayStream([])); + + $repository = new DefaultRepository($store, AutoInitializableProfile::metadata()); + $aggregate = $repository->load(ProfileId::fromString('1')); + + self::assertInstanceOf(AutoInitializableProfile::class, $aggregate); + self::assertSame(1, $aggregate->playhead()); + self::assertEquals(ProfileId::fromString('1'), $aggregate->id()); + } } From 0796d5d2709b3c1dc0faedc9a1d51a849d401103 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 21 Feb 2026 01:12:02 +0000 Subject: [PATCH 16/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.lock b/composer.lock index 68ef55426..8f36ddeb3 100644 --- a/composer.lock +++ b/composer.lock @@ -3345,16 +3345,16 @@ }, { "name": "infection/infection", - "version": "0.32.4", + "version": "0.32.5", "source": { "type": "git", "url": "https://github.com/infection/infection.git", - "reference": "a2b0a3e47b56bd2f27ca13caecae47baa7e5abe8" + "reference": "932fc7aa7a03bdbe387e42f8c8bd17d9d347653e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/infection/infection/zipball/a2b0a3e47b56bd2f27ca13caecae47baa7e5abe8", - "reference": "a2b0a3e47b56bd2f27ca13caecae47baa7e5abe8", + "url": "https://api.github.com/repos/infection/infection/zipball/932fc7aa7a03bdbe387e42f8c8bd17d9d347653e", + "reference": "932fc7aa7a03bdbe387e42f8c8bd17d9d347653e", "shasum": "" }, "require": { @@ -3465,7 +3465,7 @@ ], "support": { "issues": "https://github.com/infection/infection/issues", - "source": "https://github.com/infection/infection/tree/0.32.4" + "source": "https://github.com/infection/infection/tree/0.32.5" }, "funding": [ { @@ -3477,7 +3477,7 @@ "type": "open_collective" } ], - "time": "2026-02-09T13:24:18+00:00" + "time": "2026-02-20T07:59:31+00:00" }, { "name": "infection/mutator", @@ -4396,16 +4396,16 @@ }, { "name": "phpat/phpat", - "version": "0.12.2", + "version": "0.12.3", "source": { "type": "git", "url": "https://github.com/carlosas/phpat.git", - "reference": "fe9caef4f8633a57c1d19643d37b58050b11806c" + "reference": "2412a8959254a076e751498cbba8cf29406e0cf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/carlosas/phpat/zipball/fe9caef4f8633a57c1d19643d37b58050b11806c", - "reference": "fe9caef4f8633a57c1d19643d37b58050b11806c", + "url": "https://api.github.com/repos/carlosas/phpat/zipball/2412a8959254a076e751498cbba8cf29406e0cf4", + "reference": "2412a8959254a076e751498cbba8cf29406e0cf4", "shasum": "" }, "require": { @@ -4447,9 +4447,9 @@ "description": "PHP Architecture Tester", "support": { "issues": "https://github.com/carlosas/phpat/issues", - "source": "https://github.com/carlosas/phpat/tree/0.12.2" + "source": "https://github.com/carlosas/phpat/tree/0.12.3" }, - "time": "2026-01-27T11:41:37+00:00" + "time": "2026-02-20T11:15:22+00:00" }, { "name": "phpbench/container", From 7b0e56c3bdf85bb610376ed38ac651cb20b74fca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:37:08 +0000 Subject: [PATCH 17/59] Update dependency mkdocs-material to v9.7.3 | datasource | package | from | to | | ---------- | --------------- | ----- | ----- | | pypi | mkdocs-material | 9.7.2 | 9.7.3 | Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 68da3c4d0..b801b797f 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,7 +1,7 @@ mkdocs==1.6.1 mike==2.1.3 markdown==3.10.2 -mkdocs-material==9.7.2 +mkdocs-material==9.7.3 # Markdown extensions Pygments==2.19.2 From 3bf7371b911949fc3aab2e57c243b0dbadbd50fa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 23:47:29 +0000 Subject: [PATCH 18/59] Lock file maintenance (#824) Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 24 +++++++++++++----------- tools/composer.lock | 22 +++++++++++----------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/composer.lock b/composer.lock index 8f36ddeb3..7f0d58e10 100644 --- a/composer.lock +++ b/composer.lock @@ -3931,16 +3931,16 @@ }, { "name": "nette/schema", - "version": "v1.3.4", + "version": "v1.3.5", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "086497a2f34b82fede9b5a41cc8e131d087cd8f7" + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/086497a2f34b82fede9b5a41cc8e131d087cd8f7", - "reference": "086497a2f34b82fede9b5a41cc8e131d087cd8f7", + "url": "https://api.github.com/repos/nette/schema/zipball/f0ab1a3cda782dbc5da270d28545236aa80c4002", + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002", "shasum": "" }, "require": { @@ -3948,8 +3948,10 @@ "php": "8.1 - 8.5" }, "require-dev": { + "nette/phpstan-rules": "^1.0", "nette/tester": "^2.6", - "phpstan/phpstan": "^2.0@stable", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1.39@stable", "tracy/tracy": "^2.8" }, "type": "library", @@ -3990,9 +3992,9 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.3.4" + "source": "https://github.com/nette/schema/tree/v1.3.5" }, - "time": "2026-02-08T02:54:00+00:00" + "time": "2026-02-23T03:47:12+00:00" }, { "name": "nette/utils", @@ -4649,11 +4651,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.39", + "version": "2.1.40", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", - "reference": "c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", + "reference": "9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", "shasum": "" }, "require": { @@ -4698,7 +4700,7 @@ "type": "github" } ], - "time": "2026-02-11T14:48:56+00:00" + "time": "2026-02-23T15:04:35+00:00" }, { "name": "phpstan/phpstan-phpunit", diff --git a/tools/composer.lock b/tools/composer.lock index 4d8aed241..8af6a3544 100644 --- a/tools/composer.lock +++ b/tools/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "azjezz/psl", - "version": "4.2.1", + "version": "4.3.0", "source": { "type": "git", "url": "https://github.com/azjezz/psl.git", - "reference": "28c6752857597a1bb6fa8be16678c144b9097ab8" + "reference": "74c95be0214eb7ea39146ed00ac4eb71b45d787b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/azjezz/psl/zipball/28c6752857597a1bb6fa8be16678c144b9097ab8", - "reference": "28c6752857597a1bb6fa8be16678c144b9097ab8", + "url": "https://api.github.com/repos/azjezz/psl/zipball/74c95be0214eb7ea39146ed00ac4eb71b45d787b", + "reference": "74c95be0214eb7ea39146ed00ac4eb71b45d787b", "shasum": "" }, "require": { @@ -30,7 +30,7 @@ "revolt/event-loop": "^1.0.7" }, "require-dev": { - "carthage-software/mago": "^1.3.0", + "carthage-software/mago": "^1.6.0", "infection/infection": "^0.31.2", "php-coveralls/php-coveralls": "^2.7.0", "phpbench/phpbench": "^1.4.0", @@ -68,7 +68,7 @@ "description": "PHP Standard Library", "support": { "issues": "https://github.com/azjezz/psl/issues", - "source": "https://github.com/azjezz/psl/tree/4.2.1" + "source": "https://github.com/azjezz/psl/tree/4.3.0" }, "funding": [ { @@ -80,7 +80,7 @@ "type": "github" } ], - "time": "2026-01-29T12:38:33+00:00" + "time": "2026-02-24T01:58:53+00:00" }, { "name": "beberlei/assert", @@ -1494,11 +1494,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.39", + "version": "2.1.40", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", - "reference": "c6f73a2af4cbcd99c931d0fb8f08548cc0fa8224", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", + "reference": "9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", "shasum": "" }, "require": { @@ -1543,7 +1543,7 @@ "type": "github" } ], - "time": "2026-02-11T14:48:56+00:00" + "time": "2026-02-23T15:04:35+00:00" }, { "name": "psr/container", From 4eec826407b394c3f8c8de2a9c5a297a3f13ec5f Mon Sep 17 00:00:00 2001 From: Clemens Krack Date: Thu, 26 Feb 2026 17:41:38 +0100 Subject: [PATCH 19/59] Enhance testing documentation to support assertions on aggregate state in event sourcing --- docs/pages/testing.md | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/docs/pages/testing.md b/docs/pages/testing.md index d21f5d19a..55182ad7b 100644 --- a/docs/pages/testing.md +++ b/docs/pages/testing.md @@ -26,10 +26,21 @@ final class ProfileTest extends AggregateRootTestCase { $this ->when(static fn () => Profile::createProfile(new CreateProfile(ProfileId::fromString('1'), Email::fromString('hq@patchlevel.de')))) - ->then(new ProfileCreated(ProfileId::fromString('1'), Email::fromString('hq@patchlevel.de'))); + ->then( + new ProfileCreated(ProfileId::fromString('1'), Email::fromString('hq@patchlevel.de')), + static function (Profile $profile): void { + self::assertSame('1', $profile->id()->toString()); + self::assertSame('hq@patchlevel.de', $profile->email()->toString()); + self::assertSame(0, $profile->visited()); + }, + ); } } ``` +In addition to expected events, you can pass `Closure`s to `then`. +The closure receives the aggregate instance after `when`, so you can run PHPUnit assertions on aggregate state. +This support is available since `patchlevel/event-sourcing-phpunit` `1.5`. + You can also prepare the aggregate with events to set it to a specific state and then test whether it behaves as expected. @@ -50,7 +61,14 @@ final class ProfileTest extends AggregateRootTestCase ), ) ->when(static fn (Profile $profile) => $profile->visitProfile(ProfileId::fromString('2'))) - ->then(new ProfileVisited(ProfileId::fromString('2'))); + ->then( + new ProfileVisited(ProfileId::fromString('2')), + static function (Profile $profile): void { + self::assertSame('1', $profile->id()->toString()); + self::assertSame('hq@patchlevel.de', $profile->email()->toString()); + self::assertSame(1, $profile->visited()); + }, + ); } } ``` @@ -70,7 +88,10 @@ final class ProfileTest extends AggregateRootTestCase { $this ->when(new CreateProfile(ProfileId::fromString('1'), Email::fromString('hq@patchlevel.de'))) - ->then(new ProfileCreated(ProfileId::fromString('1'), Email::fromString('hq@patchlevel.de'))); + ->then( + new ProfileCreated(ProfileId::fromString('1'), Email::fromString('hq@patchlevel.de')), + static fn (Profile $profile) => self::assertSame('hq@patchlevel.de', $profile->email()->toString()), + ); } } ``` @@ -99,6 +120,7 @@ final class ProfileTest extends AggregateRootTestCase ) ->then( new ProfileVisited(ProfileId::fromString('2'), 'Extra Parameter / Dependency'), + static fn (Profile $profile) => self::assertSame(1, $profile->visited()), ); } } @@ -176,11 +198,14 @@ final class ProfileTest extends AggregateRootTestCase new ChangeEmail(ProfileId::fromString('1'), Email::fromString('new-hq@patchlevel.de')), $clock, ) - ->then(new EmailChanged( - ProfileId::fromString('1'), - Email::fromString('new-hq@patchlevel.de'), - new DateTimeImmutable('2021-01-01 00:00:10'), - )); + ->then( + new EmailChanged( + ProfileId::fromString('1'), + Email::fromString('new-hq@patchlevel.de'), + new DateTimeImmutable('2021-01-01 00:00:10'), + ), + static fn (Profile $profile) => self::assertSame('new-hq@patchlevel.de', $profile->email()->toString()), + ); } } ``` @@ -220,4 +245,4 @@ final class ProfileTest extends TestCase The `IncrementalRamseyUuidFactory` is only for testing purposes and supports only the version 7 what is used by the library. - \ No newline at end of file + From 0eadbdf372ca37f626dc2eb49525c1aa547eae3c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 21:02:07 +0000 Subject: [PATCH 20/59] Update postgres Docker tag to v18.3 | datasource | package | from | to | | ---------- | -------- | ---- | ---- | | docker | postgres | 18.2 | 18.3 | Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 0ac814fc7..42d6b787f 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -14,7 +14,7 @@ jobs: services: postgres: # Docker Hub image - image: "postgres:18.2" + image: "postgres:18.3" # Provide the password for postgres env: POSTGRES_PASSWORD: postgres From 1fe63582118eda23e3a30879c97600a56ae54d54 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 01:51:22 +0000 Subject: [PATCH 21/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 114 ++++++++++++++++++++++---------------------- tools/composer.lock | 84 ++++++++++++++++---------------- 2 files changed, 99 insertions(+), 99 deletions(-) diff --git a/composer.lock b/composer.lock index 7f0d58e10..fb48f0386 100644 --- a/composer.lock +++ b/composer.lock @@ -68,16 +68,16 @@ }, { "name": "doctrine/dbal", - "version": "4.4.1", + "version": "4.4.2", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "3d544473fb93f5c25b483ea4f4ce99f8c4d9d44c" + "reference": "476f7f0fa6ea4aa5364926db7fabdf6049075722" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/3d544473fb93f5c25b483ea4f4ce99f8c4d9d44c", - "reference": "3d544473fb93f5c25b483ea4f4ce99f8c4d9d44c", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/476f7f0fa6ea4aa5364926db7fabdf6049075722", + "reference": "476f7f0fa6ea4aa5364926db7fabdf6049075722", "shasum": "" }, "require": { @@ -93,9 +93,9 @@ "phpstan/phpstan": "2.1.30", "phpstan/phpstan-phpunit": "2.0.7", "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "11.5.23", - "slevomat/coding-standard": "8.24.0", - "squizlabs/php_codesniffer": "4.0.0", + "phpunit/phpunit": "11.5.50", + "slevomat/coding-standard": "8.27.1", + "squizlabs/php_codesniffer": "4.0.1", "symfony/cache": "^6.3.8|^7.0|^8.0", "symfony/console": "^5.4|^6.3|^7.0|^8.0" }, @@ -154,7 +154,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/4.4.1" + "source": "https://github.com/doctrine/dbal/tree/4.4.2" }, "funding": [ { @@ -170,7 +170,7 @@ "type": "tidelift" } ], - "time": "2025-12-04T10:11:03+00:00" + "time": "2026-02-26T12:12:19+00:00" }, { "name": "doctrine/deprecations", @@ -995,16 +995,16 @@ }, { "name": "symfony/console", - "version": "v8.0.4", + "version": "v8.0.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "ace03c4cf9805080ff40cbeec69fca180c339a3b" + "reference": "488285876e807a4777f074041d8bb508623419fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/ace03c4cf9805080ff40cbeec69fca180c339a3b", - "reference": "ace03c4cf9805080ff40cbeec69fca180c339a3b", + "url": "https://api.github.com/repos/symfony/console/zipball/488285876e807a4777f074041d8bb508623419fa", + "reference": "488285876e807a4777f074041d8bb508623419fa", "shasum": "" }, "require": { @@ -1061,7 +1061,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v8.0.4" + "source": "https://github.com/symfony/console/tree/v8.0.6" }, "funding": [ { @@ -1081,7 +1081,7 @@ "type": "tidelift" } ], - "time": "2026-01-13T13:06:50+00:00" + "time": "2026-02-25T16:59:43+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1313,16 +1313,16 @@ }, { "name": "symfony/finder", - "version": "v8.0.5", + "version": "v8.0.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "8bd576e97c67d45941365bf824e18dc8538e6eb0" + "reference": "441404f09a54de6d1bd6ad219e088cdf4c91f97c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/8bd576e97c67d45941365bf824e18dc8538e6eb0", - "reference": "8bd576e97c67d45941365bf824e18dc8538e6eb0", + "url": "https://api.github.com/repos/symfony/finder/zipball/441404f09a54de6d1bd6ad219e088cdf4c91f97c", + "reference": "441404f09a54de6d1bd6ad219e088cdf4c91f97c", "shasum": "" }, "require": { @@ -1357,7 +1357,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v8.0.5" + "source": "https://github.com/symfony/finder/tree/v8.0.6" }, "funding": [ { @@ -1377,7 +1377,7 @@ "type": "tidelift" } ], - "time": "2026-01-26T15:08:38+00:00" + "time": "2026-01-29T09:41:02+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1869,16 +1869,16 @@ }, { "name": "symfony/string", - "version": "v8.0.4", + "version": "v8.0.6", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "758b372d6882506821ed666032e43020c4f57194" + "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/758b372d6882506821ed666032e43020c4f57194", - "reference": "758b372d6882506821ed666032e43020c4f57194", + "url": "https://api.github.com/repos/symfony/string/zipball/6c9e1108041b5dce21a9a4984b531c4923aa9ec4", + "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4", "shasum": "" }, "require": { @@ -1935,7 +1935,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.4" + "source": "https://github.com/symfony/string/tree/v8.0.6" }, "funding": [ { @@ -1955,20 +1955,20 @@ "type": "tidelift" } ], - "time": "2026-01-12T12:37:40+00:00" + "time": "2026-02-09T10:14:57+00:00" }, { "name": "symfony/type-info", - "version": "v8.0.4", + "version": "v8.0.6", "source": { "type": "git", "url": "https://github.com/symfony/type-info.git", - "reference": "106a2d3bbf0d4576b2f70e6ca866fa420956ed0d" + "reference": "785992c06d07306f963ded3439036f5da9b292fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/106a2d3bbf0d4576b2f70e6ca866fa420956ed0d", - "reference": "106a2d3bbf0d4576b2f70e6ca866fa420956ed0d", + "url": "https://api.github.com/repos/symfony/type-info/zipball/785992c06d07306f963ded3439036f5da9b292fe", + "reference": "785992c06d07306f963ded3439036f5da9b292fe", "shasum": "" }, "require": { @@ -2017,7 +2017,7 @@ "type" ], "support": { - "source": "https://github.com/symfony/type-info/tree/v8.0.4" + "source": "https://github.com/symfony/type-info/tree/v8.0.6" }, "funding": [ { @@ -2037,7 +2037,7 @@ "type": "tidelift" } ], - "time": "2026-01-09T12:15:10+00:00" + "time": "2026-02-20T07:51:53+00:00" }, { "name": "symfony/var-exporter", @@ -3345,16 +3345,16 @@ }, { "name": "infection/infection", - "version": "0.32.5", + "version": "0.32.6", "source": { "type": "git", "url": "https://github.com/infection/infection.git", - "reference": "932fc7aa7a03bdbe387e42f8c8bd17d9d347653e" + "reference": "4ed769947eaf2ecf42203027301bad2bedf037e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/infection/infection/zipball/932fc7aa7a03bdbe387e42f8c8bd17d9d347653e", - "reference": "932fc7aa7a03bdbe387e42f8c8bd17d9d347653e", + "url": "https://api.github.com/repos/infection/infection/zipball/4ed769947eaf2ecf42203027301bad2bedf037e5", + "reference": "4ed769947eaf2ecf42203027301bad2bedf037e5", "shasum": "" }, "require": { @@ -3465,7 +3465,7 @@ ], "support": { "issues": "https://github.com/infection/infection/issues", - "source": "https://github.com/infection/infection/tree/0.32.5" + "source": "https://github.com/infection/infection/tree/0.32.6" }, "funding": [ { @@ -3477,7 +3477,7 @@ "type": "open_collective" } ], - "time": "2026-02-20T07:59:31+00:00" + "time": "2026-02-26T14:34:26+00:00" }, { "name": "infection/mutator", @@ -6814,16 +6814,16 @@ }, { "name": "symfony/filesystem", - "version": "v8.0.1", + "version": "v8.0.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "d937d400b980523dc9ee946bb69972b5e619058d" + "reference": "7bf9162d7a0dff98d079b72948508fa48018a770" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/d937d400b980523dc9ee946bb69972b5e619058d", - "reference": "d937d400b980523dc9ee946bb69972b5e619058d", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/7bf9162d7a0dff98d079b72948508fa48018a770", + "reference": "7bf9162d7a0dff98d079b72948508fa48018a770", "shasum": "" }, "require": { @@ -6860,7 +6860,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v8.0.1" + "source": "https://github.com/symfony/filesystem/tree/v8.0.6" }, "funding": [ { @@ -6880,20 +6880,20 @@ "type": "tidelift" } ], - "time": "2025-12-01T09:13:36+00:00" + "time": "2026-02-25T16:59:43+00:00" }, { "name": "symfony/messenger", - "version": "v8.0.4", + "version": "v8.0.6", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "3483db96bcc33310cd1807d2b962e7e01d9f41c2" + "reference": "4be925bf0155d6435d2cdfa63d5ffd277c44ac10" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/3483db96bcc33310cd1807d2b962e7e01d9f41c2", - "reference": "3483db96bcc33310cd1807d2b962e7e01d9f41c2", + "url": "https://api.github.com/repos/symfony/messenger/zipball/4be925bf0155d6435d2cdfa63d5ffd277c44ac10", + "reference": "4be925bf0155d6435d2cdfa63d5ffd277c44ac10", "shasum": "" }, "require": { @@ -6950,7 +6950,7 @@ "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/messenger/tree/v8.0.4" + "source": "https://github.com/symfony/messenger/tree/v8.0.6" }, "funding": [ { @@ -6970,7 +6970,7 @@ "type": "tidelift" } ], - "time": "2026-01-08T22:36:47+00:00" + "time": "2026-02-25T16:59:43+00:00" }, { "name": "symfony/options-resolver", @@ -7354,16 +7354,16 @@ }, { "name": "symfony/var-dumper", - "version": "v8.0.4", + "version": "v8.0.6", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "326e0406fc315eca57ef5740fa4a280b7a068c82" + "reference": "2e14f7e0bf5ff02c6e63bd31cb8e4855a13d6209" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/326e0406fc315eca57ef5740fa4a280b7a068c82", - "reference": "326e0406fc315eca57ef5740fa4a280b7a068c82", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2e14f7e0bf5ff02c6e63bd31cb8e4855a13d6209", + "reference": "2e14f7e0bf5ff02c6e63bd31cb8e4855a13d6209", "shasum": "" }, "require": { @@ -7417,7 +7417,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v8.0.4" + "source": "https://github.com/symfony/var-dumper/tree/v8.0.6" }, "funding": [ { @@ -7437,7 +7437,7 @@ "type": "tidelift" } ], - "time": "2026-01-01T23:07:29+00:00" + "time": "2026-02-15T10:53:29+00:00" }, { "name": "thecodingmachine/safe", diff --git a/tools/composer.lock b/tools/composer.lock index 8af6a3544..ef3b58335 100644 --- a/tools/composer.lock +++ b/tools/composer.lock @@ -2161,16 +2161,16 @@ }, { "name": "symfony/config", - "version": "v8.0.4", + "version": "v8.0.6", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "8f45af92f08f82902827a8b6f403aaf49d893539" + "reference": "94ea198de42f93dffa920a098cac3961a82e63b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/8f45af92f08f82902827a8b6f403aaf49d893539", - "reference": "8f45af92f08f82902827a8b6f403aaf49d893539", + "url": "https://api.github.com/repos/symfony/config/zipball/94ea198de42f93dffa920a098cac3961a82e63b7", + "reference": "94ea198de42f93dffa920a098cac3961a82e63b7", "shasum": "" }, "require": { @@ -2215,7 +2215,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v8.0.4" + "source": "https://github.com/symfony/config/tree/v8.0.6" }, "funding": [ { @@ -2235,20 +2235,20 @@ "type": "tidelift" } ], - "time": "2026-01-13T13:06:50+00:00" + "time": "2026-02-25T16:59:43+00:00" }, { "name": "symfony/console", - "version": "v7.4.4", + "version": "v7.4.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894" + "reference": "6d643a93b47398599124022eb24d97c153c12f27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/41e38717ac1dd7a46b6bda7d6a82af2d98a78894", - "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894", + "url": "https://api.github.com/repos/symfony/console/zipball/6d643a93b47398599124022eb24d97c153c12f27", + "reference": "6d643a93b47398599124022eb24d97c153c12f27", "shasum": "" }, "require": { @@ -2313,7 +2313,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.4" + "source": "https://github.com/symfony/console/tree/v7.4.6" }, "funding": [ { @@ -2333,20 +2333,20 @@ "type": "tidelift" } ], - "time": "2026-01-13T11:36:38+00:00" + "time": "2026-02-25T17:02:47+00:00" }, { "name": "symfony/dependency-injection", - "version": "v8.0.5", + "version": "v8.0.6", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "40a6c455ade7e3bf25900d6b746d40cfa2573e26" + "reference": "edd98864a7b9eaaa10f389bd414e7d9e816bb59d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/40a6c455ade7e3bf25900d6b746d40cfa2573e26", - "reference": "40a6c455ade7e3bf25900d6b746d40cfa2573e26", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/edd98864a7b9eaaa10f389bd414e7d9e816bb59d", + "reference": "edd98864a7b9eaaa10f389bd414e7d9e816bb59d", "shasum": "" }, "require": { @@ -2394,7 +2394,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v8.0.5" + "source": "https://github.com/symfony/dependency-injection/tree/v8.0.6" }, "funding": [ { @@ -2414,7 +2414,7 @@ "type": "tidelift" } ], - "time": "2026-01-27T16:18:07+00:00" + "time": "2026-02-25T16:59:43+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2646,16 +2646,16 @@ }, { "name": "symfony/filesystem", - "version": "v8.0.1", + "version": "v8.0.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "d937d400b980523dc9ee946bb69972b5e619058d" + "reference": "7bf9162d7a0dff98d079b72948508fa48018a770" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/d937d400b980523dc9ee946bb69972b5e619058d", - "reference": "d937d400b980523dc9ee946bb69972b5e619058d", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/7bf9162d7a0dff98d079b72948508fa48018a770", + "reference": "7bf9162d7a0dff98d079b72948508fa48018a770", "shasum": "" }, "require": { @@ -2692,7 +2692,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v8.0.1" + "source": "https://github.com/symfony/filesystem/tree/v8.0.6" }, "funding": [ { @@ -2712,20 +2712,20 @@ "type": "tidelift" } ], - "time": "2025-12-01T09:13:36+00:00" + "time": "2026-02-25T16:59:43+00:00" }, { "name": "symfony/finder", - "version": "v8.0.5", + "version": "v8.0.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "8bd576e97c67d45941365bf824e18dc8538e6eb0" + "reference": "441404f09a54de6d1bd6ad219e088cdf4c91f97c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/8bd576e97c67d45941365bf824e18dc8538e6eb0", - "reference": "8bd576e97c67d45941365bf824e18dc8538e6eb0", + "url": "https://api.github.com/repos/symfony/finder/zipball/441404f09a54de6d1bd6ad219e088cdf4c91f97c", + "reference": "441404f09a54de6d1bd6ad219e088cdf4c91f97c", "shasum": "" }, "require": { @@ -2760,7 +2760,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v8.0.5" + "source": "https://github.com/symfony/finder/tree/v8.0.6" }, "funding": [ { @@ -2780,7 +2780,7 @@ "type": "tidelift" } ], - "time": "2026-01-26T15:08:38+00:00" + "time": "2026-01-29T09:41:02+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3595,16 +3595,16 @@ }, { "name": "symfony/string", - "version": "v8.0.4", + "version": "v8.0.6", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "758b372d6882506821ed666032e43020c4f57194" + "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/758b372d6882506821ed666032e43020c4f57194", - "reference": "758b372d6882506821ed666032e43020c4f57194", + "url": "https://api.github.com/repos/symfony/string/zipball/6c9e1108041b5dce21a9a4984b531c4923aa9ec4", + "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4", "shasum": "" }, "require": { @@ -3661,7 +3661,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.4" + "source": "https://github.com/symfony/string/tree/v8.0.6" }, "funding": [ { @@ -3681,7 +3681,7 @@ "type": "tidelift" } ], - "time": "2026-01-12T12:37:40+00:00" + "time": "2026-02-09T10:14:57+00:00" }, { "name": "symfony/var-exporter", @@ -3765,16 +3765,16 @@ }, { "name": "symfony/yaml", - "version": "v8.0.1", + "version": "v8.0.6", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "7a1a90ba1df6e821a6b53c4cabdc32a56cabfb14" + "reference": "5f006c50a981e1630bbb70ad409c5d85f9a716e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/7a1a90ba1df6e821a6b53c4cabdc32a56cabfb14", - "reference": "7a1a90ba1df6e821a6b53c4cabdc32a56cabfb14", + "url": "https://api.github.com/repos/symfony/yaml/zipball/5f006c50a981e1630bbb70ad409c5d85f9a716e0", + "reference": "5f006c50a981e1630bbb70ad409c5d85f9a716e0", "shasum": "" }, "require": { @@ -3816,7 +3816,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v8.0.1" + "source": "https://github.com/symfony/yaml/tree/v8.0.6" }, "funding": [ { @@ -3836,7 +3836,7 @@ "type": "tidelift" } ], - "time": "2025-12-04T18:17:06+00:00" + "time": "2026-02-09T10:14:57+00:00" } ], "packages-dev": [], From 7b9c84636679d968e7a8cc95b0d10f091584f870 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 01:41:48 +0000 Subject: [PATCH 22/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index fb48f0386..997514eeb 100644 --- a/composer.lock +++ b/composer.lock @@ -7634,16 +7634,16 @@ }, { "name": "webmozart/assert", - "version": "2.1.5", + "version": "2.1.6", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "79155f94852fa27e2f73b459f6503f5e87e2c188" + "reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/79155f94852fa27e2f73b459f6503f5e87e2c188", - "reference": "79155f94852fa27e2f73b459f6503f5e87e2c188", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/ff31ad6efc62e66e518fbab1cde3453d389bcdc8", + "reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8", "shasum": "" }, "require": { @@ -7690,9 +7690,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/2.1.5" + "source": "https://github.com/webmozarts/assert/tree/2.1.6" }, - "time": "2026-02-18T14:09:36+00:00" + "time": "2026-02-27T10:28:38+00:00" }, { "name": "webmozart/glob", From a22262e6b03ab3dd0a371dcc7791ef9609ab096f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 21:21:57 +0000 Subject: [PATCH 23/59] Update dependency mkdocs-material to v9.7.4 | datasource | package | from | to | | ---------- | --------------- | ----- | ----- | | pypi | mkdocs-material | 9.7.3 | 9.7.4 | Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index b801b797f..7d7ccf5dd 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,7 +1,7 @@ mkdocs==1.6.1 mike==2.1.3 markdown==3.10.2 -mkdocs-material==9.7.3 +mkdocs-material==9.7.4 # Markdown extensions Pygments==2.19.2 From 718a4d5d0fba9233fb7376e5437db7271f834cc9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Mar 2026 01:14:02 +0000 Subject: [PATCH 24/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index 997514eeb..6a951de6d 100644 --- a/composer.lock +++ b/composer.lock @@ -4506,16 +4506,16 @@ }, { "name": "phpbench/phpbench", - "version": "1.4.3", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "b641dde59d969ea42eed70a39f9b51950bc96878" + "reference": "63e5a853b84ef27729b2af7f42365a19434fdc79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/b641dde59d969ea42eed70a39f9b51950bc96878", - "reference": "b641dde59d969ea42eed70a39f9b51950bc96878", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/63e5a853b84ef27729b2af7f42365a19434fdc79", + "reference": "63e5a853b84ef27729b2af7f42365a19434fdc79", "shasum": "" }, "require": { @@ -4526,7 +4526,7 @@ "ext-reflection": "*", "ext-spl": "*", "ext-tokenizer": "*", - "php": "^8.1", + "php": "^8.2", "phpbench/container": "^2.2", "psr/log": "^1.1 || ^2.0 || ^3.0", "seld/jsonlint": "^1.1", @@ -4546,8 +4546,9 @@ "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.0", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^10.4 || ^11.0", + "phpunit/phpunit": "^11.5", "rector/rector": "^1.2", + "sebastian/exporter": "^6.3.2", "symfony/error-handler": "^6.1 || ^7.0 || ^8.0", "symfony/var-dumper": "^6.1 || ^7.0 || ^8.0" }, @@ -4592,7 +4593,7 @@ ], "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.4.3" + "source": "https://github.com/phpbench/phpbench/tree/1.5.0" }, "funding": [ { @@ -4600,7 +4601,7 @@ "type": "github" } ], - "time": "2025-11-06T19:07:31+00:00" + "time": "2026-03-04T20:33:49+00:00" }, { "name": "phpstan/phpdoc-parser", From 34fb86514df0e453ca5590439a0968ad79dbc103 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 01:04:40 +0000 Subject: [PATCH 25/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/composer.lock b/composer.lock index 6a951de6d..1292e909f 100644 --- a/composer.lock +++ b/composer.lock @@ -3609,16 +3609,16 @@ }, { "name": "league/commonmark", - "version": "2.8.0", + "version": "2.8.1", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "4efa10c1e56488e658d10adf7b7b7dcd19940bfb" + "reference": "84b1ca48347efdbe775426f108622a42735a6579" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/4efa10c1e56488e658d10adf7b7b7dcd19940bfb", - "reference": "4efa10c1e56488e658d10adf7b7b7dcd19940bfb", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/84b1ca48347efdbe775426f108622a42735a6579", + "reference": "84b1ca48347efdbe775426f108622a42735a6579", "shasum": "" }, "require": { @@ -3643,9 +3643,9 @@ "phpstan/phpstan": "^1.8.2", "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", "scrutinizer/ocular": "^1.8.1", - "symfony/finder": "^5.3 | ^6.0 | ^7.0", - "symfony/process": "^5.4 | ^6.0 | ^7.0", - "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", + "symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0", "unleashedtech/php-coding-standard": "^3.1.1", "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" }, @@ -3712,7 +3712,7 @@ "type": "tidelift" } ], - "time": "2025-11-26T21:48:24+00:00" + "time": "2026-03-05T21:37:03+00:00" }, { "name": "league/config", @@ -4506,16 +4506,16 @@ }, { "name": "phpbench/phpbench", - "version": "1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "63e5a853b84ef27729b2af7f42365a19434fdc79" + "reference": "9a28fd0833f11171b949843c6fd663eb69b6d14c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/63e5a853b84ef27729b2af7f42365a19434fdc79", - "reference": "63e5a853b84ef27729b2af7f42365a19434fdc79", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/9a28fd0833f11171b949843c6fd663eb69b6d14c", + "reference": "9a28fd0833f11171b949843c6fd663eb69b6d14c", "shasum": "" }, "require": { @@ -4593,7 +4593,7 @@ ], "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.5.0" + "source": "https://github.com/phpbench/phpbench/tree/1.5.1" }, "funding": [ { @@ -4601,7 +4601,7 @@ "type": "github" } ], - "time": "2026-03-04T20:33:49+00:00" + "time": "2026-03-05T08:18:58+00:00" }, { "name": "phpstan/phpdoc-parser", From 7f7e9df24ae9a7ce19ae4d729d7d39db4fc8e2b0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 7 Mar 2026 00:47:42 +0000 Subject: [PATCH 26/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 36 ++++++++++++++++++------------------ tools/composer.lock | 36 ++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/composer.lock b/composer.lock index 1292e909f..3fdaa4f2b 100644 --- a/composer.lock +++ b/composer.lock @@ -995,16 +995,16 @@ }, { "name": "symfony/console", - "version": "v8.0.6", + "version": "v8.0.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "488285876e807a4777f074041d8bb508623419fa" + "reference": "15ed9008a4ebe2d6a78e4937f74e0c13ef2e618a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/488285876e807a4777f074041d8bb508623419fa", - "reference": "488285876e807a4777f074041d8bb508623419fa", + "url": "https://api.github.com/repos/symfony/console/zipball/15ed9008a4ebe2d6a78e4937f74e0c13ef2e618a", + "reference": "15ed9008a4ebe2d6a78e4937f74e0c13ef2e618a", "shasum": "" }, "require": { @@ -1061,7 +1061,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v8.0.6" + "source": "https://github.com/symfony/console/tree/v8.0.7" }, "funding": [ { @@ -1081,7 +1081,7 @@ "type": "tidelift" } ], - "time": "2026-02-25T16:59:43+00:00" + "time": "2026-03-06T14:06:22+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1959,16 +1959,16 @@ }, { "name": "symfony/type-info", - "version": "v8.0.6", + "version": "v8.0.7", "source": { "type": "git", "url": "https://github.com/symfony/type-info.git", - "reference": "785992c06d07306f963ded3439036f5da9b292fe" + "reference": "3c7de103dd6cb68be24e155838a64ef4a70ae195" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/785992c06d07306f963ded3439036f5da9b292fe", - "reference": "785992c06d07306f963ded3439036f5da9b292fe", + "url": "https://api.github.com/repos/symfony/type-info/zipball/3c7de103dd6cb68be24e155838a64ef4a70ae195", + "reference": "3c7de103dd6cb68be24e155838a64ef4a70ae195", "shasum": "" }, "require": { @@ -2017,7 +2017,7 @@ "type" ], "support": { - "source": "https://github.com/symfony/type-info/tree/v8.0.6" + "source": "https://github.com/symfony/type-info/tree/v8.0.7" }, "funding": [ { @@ -2037,7 +2037,7 @@ "type": "tidelift" } ], - "time": "2026-02-20T07:51:53+00:00" + "time": "2026-03-04T13:55:34+00:00" }, { "name": "symfony/var-exporter", @@ -6885,16 +6885,16 @@ }, { "name": "symfony/messenger", - "version": "v8.0.6", + "version": "v8.0.7", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "4be925bf0155d6435d2cdfa63d5ffd277c44ac10" + "reference": "6ba5f08c156cfc95911dbb0da01a9bc390a70fd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/4be925bf0155d6435d2cdfa63d5ffd277c44ac10", - "reference": "4be925bf0155d6435d2cdfa63d5ffd277c44ac10", + "url": "https://api.github.com/repos/symfony/messenger/zipball/6ba5f08c156cfc95911dbb0da01a9bc390a70fd1", + "reference": "6ba5f08c156cfc95911dbb0da01a9bc390a70fd1", "shasum": "" }, "require": { @@ -6951,7 +6951,7 @@ "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/messenger/tree/v8.0.6" + "source": "https://github.com/symfony/messenger/tree/v8.0.7" }, "funding": [ { @@ -6971,7 +6971,7 @@ "type": "tidelift" } ], - "time": "2026-02-25T16:59:43+00:00" + "time": "2026-03-04T13:55:34+00:00" }, { "name": "symfony/options-resolver", diff --git a/tools/composer.lock b/tools/composer.lock index ef3b58335..dce23a34d 100644 --- a/tools/composer.lock +++ b/tools/composer.lock @@ -2161,16 +2161,16 @@ }, { "name": "symfony/config", - "version": "v8.0.6", + "version": "v8.0.7", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "94ea198de42f93dffa920a098cac3961a82e63b7" + "reference": "9a34c52187112503d02903ab35e6e3783f580c29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/94ea198de42f93dffa920a098cac3961a82e63b7", - "reference": "94ea198de42f93dffa920a098cac3961a82e63b7", + "url": "https://api.github.com/repos/symfony/config/zipball/9a34c52187112503d02903ab35e6e3783f580c29", + "reference": "9a34c52187112503d02903ab35e6e3783f580c29", "shasum": "" }, "require": { @@ -2215,7 +2215,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v8.0.6" + "source": "https://github.com/symfony/config/tree/v8.0.7" }, "funding": [ { @@ -2235,20 +2235,20 @@ "type": "tidelift" } ], - "time": "2026-02-25T16:59:43+00:00" + "time": "2026-03-06T13:17:40+00:00" }, { "name": "symfony/console", - "version": "v7.4.6", + "version": "v7.4.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "6d643a93b47398599124022eb24d97c153c12f27" + "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/6d643a93b47398599124022eb24d97c153c12f27", - "reference": "6d643a93b47398599124022eb24d97c153c12f27", + "url": "https://api.github.com/repos/symfony/console/zipball/e1e6770440fb9c9b0cf725f81d1361ad1835329d", + "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d", "shasum": "" }, "require": { @@ -2313,7 +2313,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.6" + "source": "https://github.com/symfony/console/tree/v7.4.7" }, "funding": [ { @@ -2333,20 +2333,20 @@ "type": "tidelift" } ], - "time": "2026-02-25T17:02:47+00:00" + "time": "2026-03-06T14:06:20+00:00" }, { "name": "symfony/dependency-injection", - "version": "v8.0.6", + "version": "v8.0.7", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "edd98864a7b9eaaa10f389bd414e7d9e816bb59d" + "reference": "1faaac6dbfe069a92ab9e5c270fa43fc4e1761da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/edd98864a7b9eaaa10f389bd414e7d9e816bb59d", - "reference": "edd98864a7b9eaaa10f389bd414e7d9e816bb59d", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/1faaac6dbfe069a92ab9e5c270fa43fc4e1761da", + "reference": "1faaac6dbfe069a92ab9e5c270fa43fc4e1761da", "shasum": "" }, "require": { @@ -2394,7 +2394,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v8.0.6" + "source": "https://github.com/symfony/dependency-injection/tree/v8.0.7" }, "funding": [ { @@ -2414,7 +2414,7 @@ "type": "tidelift" } ], - "time": "2026-02-25T16:59:43+00:00" + "time": "2026-03-03T07:49:33+00:00" }, { "name": "symfony/deprecation-contracts", From a5a01b0cc36e466656b7aedcd19cf2c14272ad24 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 8 Mar 2026 06:02:48 +0000 Subject: [PATCH 27/59] Update dependency mike to v2.1.4 | datasource | package | from | to | | ---------- | ------- | ----- | ----- | | pypi | mike | 2.1.3 | 2.1.4 | Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 7d7ccf5dd..c26eacce0 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ mkdocs==1.6.1 -mike==2.1.3 +mike==2.1.4 markdown==3.10.2 mkdocs-material==9.7.4 From a0e4d4ea47466df711536fbc6f4d194c2ae369f9 Mon Sep 17 00:00:00 2001 From: Daniel Badura Date: Mon, 9 Mar 2026 18:16:55 +0100 Subject: [PATCH 28/59] Use `Pdo\Pgsql::getNotify` if PHP version is `>= 8.4` instead of `PDO::pgsqlGetNotify`. --- src/Store/DoctrineDbalStore.php | 16 ++++++-- src/Store/StreamDoctrineDbalStore.php | 16 ++++++-- tests/Unit/Store/DoctrineDbalStoreTest.php | 33 ++++++++++++++++ .../Store/StreamDoctrineDbalStoreTest.php | 38 +++++++++++++++++++ 4 files changed, 95 insertions(+), 8 deletions(-) diff --git a/src/Store/DoctrineDbalStore.php b/src/Store/DoctrineDbalStore.php index 902d6c418..5efcaa07e 100644 --- a/src/Store/DoctrineDbalStore.php +++ b/src/Store/DoctrineDbalStore.php @@ -34,6 +34,7 @@ use Patchlevel\EventSourcing\Store\Criteria\ToIndexCriterion; use Patchlevel\EventSourcing\Store\Header\IndexHeader; use PDO; +use Pdo\Pgsql; use function array_fill; use function array_filter; @@ -49,6 +50,8 @@ use function is_string; use function sprintf; +use const PHP_VERSION_ID; + final class DoctrineDbalStore implements Store, SubscriptionStore, DoctrineSchemaConfigurator { /** @@ -386,10 +389,15 @@ public function wait(int $timeoutMilliseconds): void $this->connection->executeStatement(sprintf('LISTEN "%s"', $this->config['table_name'])); - /** @var PDO $nativeConnection */ - $nativeConnection = $this->connection->getNativeConnection(); - - $nativeConnection->pgsqlGetNotify(PDO::FETCH_ASSOC, $timeoutMilliseconds); + if (PHP_VERSION_ID >= 80400) { + /** @var Pgsql $nativeConnection */ + $nativeConnection = $this->connection->getNativeConnection(); + $nativeConnection->getNotify(PDO::FETCH_ASSOC, $timeoutMilliseconds); + } else { + /** @var PDO $nativeConnection */ + $nativeConnection = $this->connection->getNativeConnection(); + $nativeConnection->pgsqlGetNotify(PDO::FETCH_ASSOC, $timeoutMilliseconds); + } } public function setupSubscription(): void diff --git a/src/Store/StreamDoctrineDbalStore.php b/src/Store/StreamDoctrineDbalStore.php index 83a24a247..204dcd1f8 100644 --- a/src/Store/StreamDoctrineDbalStore.php +++ b/src/Store/StreamDoctrineDbalStore.php @@ -39,6 +39,7 @@ use Patchlevel\EventSourcing\Store\Header\RecordedOnHeader; use Patchlevel\EventSourcing\Store\Header\StreamNameHeader; use PDO; +use Pdo\Pgsql; use Psr\Clock\ClockInterface; use Ramsey\Uuid\Uuid; @@ -58,6 +59,8 @@ use function str_contains; use function str_replace; +use const PHP_VERSION_ID; + final class StreamDoctrineDbalStore implements StreamStore, SubscriptionStore, DoctrineSchemaConfigurator { /** @@ -465,10 +468,15 @@ public function wait(int $timeoutMilliseconds): void $this->connection->executeStatement(sprintf('LISTEN "%s"', $this->config['table_name'])); - /** @var PDO $nativeConnection */ - $nativeConnection = $this->connection->getNativeConnection(); - - $nativeConnection->pgsqlGetNotify(PDO::FETCH_ASSOC, $timeoutMilliseconds); + if (PHP_VERSION_ID >= 80400) { + /** @var Pgsql $nativeConnection */ + $nativeConnection = $this->connection->getNativeConnection(); + $nativeConnection->getNotify(PDO::FETCH_ASSOC, $timeoutMilliseconds); + } else { + /** @var PDO $nativeConnection */ + $nativeConnection = $this->connection->getNativeConnection(); + $nativeConnection->pgsqlGetNotify(PDO::FETCH_ASSOC, $timeoutMilliseconds); + } } public function setupSubscription(): void diff --git a/tests/Unit/Store/DoctrineDbalStoreTest.php b/tests/Unit/Store/DoctrineDbalStoreTest.php index 0d968ff5d..aa887a29c 100644 --- a/tests/Unit/Store/DoctrineDbalStoreTest.php +++ b/tests/Unit/Store/DoctrineDbalStoreTest.php @@ -41,6 +41,7 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileEmailChanged; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; use PDO; +use Pdo\Pgsql; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\RequiresPhp; use PHPUnit\Framework\TestCase; @@ -1343,7 +1344,39 @@ public function testSetupSubscriptionNotPostgres(): void $doctrineDbalStore->setupSubscription(); } + #[RequiresPhp('>= 8.4')] public function testWait(): void + { + $nativeConnection = $this->createMock(Pgsql::class); + $nativeConnection + ->expects($this->once()) + ->method('getNotify') + ->with(PDO::FETCH_ASSOC, 100) + ->willReturn([]); + + $connection = $this->createMock(Connection::class); + $connection->expects($this->once())->method('executeStatement')->with('LISTEN "eventstore"') + ->willReturn(1); + $connection->expects($this->once())->method('getNativeConnection') + ->willReturn($nativeConnection); + + $abstractPlatform = $this->createMock(PostgreSQLPlatform::class); + $connection->expects($this->once())->method('getDatabasePlatform')->willReturn($abstractPlatform); + + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); + + $doctrineDbalStore = new DoctrineDbalStore( + $connection, + $eventSerializer, + $headersSerializer, + ); + + $doctrineDbalStore->wait(100); + } + + #[RequiresPhp('< 8.4')] + public function testWaitDeprecatedFunction(): void { $nativeConnection = $this->getMockBuilder(PDO::class) ->disableOriginalConstructor() diff --git a/tests/Unit/Store/StreamDoctrineDbalStoreTest.php b/tests/Unit/Store/StreamDoctrineDbalStoreTest.php index c78414c2c..35d50426c 100644 --- a/tests/Unit/Store/StreamDoctrineDbalStoreTest.php +++ b/tests/Unit/Store/StreamDoctrineDbalStoreTest.php @@ -44,7 +44,9 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileEmailChanged; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileId; use PDO; +use Pdo\Pgsql; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\RequiresPhp; use PHPUnit\Framework\TestCase; use Psr\Clock\ClockInterface; use RuntimeException; @@ -1367,7 +1369,43 @@ public function testSetupSubscriptionNotPostgres(): void $doctrineDbalStore->setupSubscription(); } + #[RequiresPhp('>= 8.4')] public function testWait(): void + { + $nativeConnection = $this->createMock(Pgsql::class); + $nativeConnection + ->expects($this->once()) + ->method('getNotify') + ->with(PDO::FETCH_ASSOC, 100) + ->willReturn([]); + + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->once()) + ->method('executeStatement') + ->with('LISTEN "event_store"') + ->willReturn(1); + $connection + ->expects($this->once()) + ->method('getNativeConnection') + ->willReturn($nativeConnection); + + $abstractPlatform = $this->createMock(PostgreSQLPlatform::class); + $connection->method('getDatabasePlatform')->willReturn($abstractPlatform); + + $eventSerializer = $this->createMock(EventSerializer::class); + $headersSerializer = $this->createMock(HeadersSerializer::class); + + $doctrineDbalStore = new StreamDoctrineDbalStore( + $connection, + $eventSerializer, + $headersSerializer, + ); + $doctrineDbalStore->wait(100); + } + + #[RequiresPhp('< 8.4')] + public function testWaitDeprecatedFunction(): void { $nativeConnection = $this->getMockBuilder(PDO::class) ->disableOriginalConstructor() From 333635c21da08aa84ddbe19d3d1198760d2f2925 Mon Sep 17 00:00:00 2001 From: Daniel Badura Date: Mon, 9 Mar 2026 18:29:03 +0100 Subject: [PATCH 29/59] Add `pdo_pgsql` to the list of extensions for unit tests --- .github/workflows/unit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 0776f96c2..2daa95966 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -46,7 +46,7 @@ jobs: coverage: "pcov" php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1 - extensions: pdo_sqlite + extensions: pdo_sqlite, pdo_pgsql - uses: ramsey/composer-install@3.1.1 with: From 6e9f6295a85178a960e8f2e0d46cfda393333e5b Mon Sep 17 00:00:00 2001 From: Daniel Badura Date: Mon, 9 Mar 2026 18:41:13 +0100 Subject: [PATCH 30/59] Adjust also other workflows extensions. --- .github/workflows/coding-standard.yml | 1 - .github/workflows/deptrac.yml | 1 - .github/workflows/mutation-tests-diff.yml | 2 +- .github/workflows/mutation-tests.yml | 2 +- .github/workflows/phpstan.yml | 1 - 5 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/coding-standard.yml b/.github/workflows/coding-standard.yml index bdb62784f..bfb0ea133 100644 --- a/.github/workflows/coding-standard.yml +++ b/.github/workflows/coding-standard.yml @@ -34,7 +34,6 @@ jobs: coverage: none php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1, opcache.enable_cli=1 - extensions: pdo_sqlite - uses: ramsey/composer-install@3.1.1 with: diff --git a/.github/workflows/deptrac.yml b/.github/workflows/deptrac.yml index ea0419496..339aee32a 100644 --- a/.github/workflows/deptrac.yml +++ b/.github/workflows/deptrac.yml @@ -34,7 +34,6 @@ jobs: coverage: "pcov" php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1 - extensions: pdo_sqlite - uses: ramsey/composer-install@3.1.1 with: diff --git a/.github/workflows/mutation-tests-diff.yml b/.github/workflows/mutation-tests-diff.yml index d01b57fee..db05d5316 100644 --- a/.github/workflows/mutation-tests-diff.yml +++ b/.github/workflows/mutation-tests-diff.yml @@ -32,7 +32,7 @@ jobs: coverage: "pcov" php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1 - extensions: pdo_sqlite + extensions: pdo_sqlite, pdo_pgsql - uses: ramsey/composer-install@3.1.1 with: diff --git a/.github/workflows/mutation-tests.yml b/.github/workflows/mutation-tests.yml index 379d868f7..f63c2fe32 100644 --- a/.github/workflows/mutation-tests.yml +++ b/.github/workflows/mutation-tests.yml @@ -34,7 +34,7 @@ jobs: coverage: "pcov" php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1 - extensions: pdo_sqlite + extensions: pdo_sqlite, pdo_pgsql - uses: ramsey/composer-install@3.1.1 with: diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index a1fb6c325..b5b5a6afe 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -34,7 +34,6 @@ jobs: coverage: none php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1, opcache.enable_cli=1 - extensions: pdo_sqlite - uses: ramsey/composer-install@3.1.1 with: From 113334fc30ccc9ef079c289591c87d083671e27e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 02:42:21 +0000 Subject: [PATCH 31/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 3fdaa4f2b..5999bafcb 100644 --- a/composer.lock +++ b/composer.lock @@ -416,16 +416,16 @@ }, { "name": "patchlevel/hydrator", - "version": "1.16.0", + "version": "1.17.0", "source": { "type": "git", "url": "https://github.com/patchlevel/hydrator.git", - "reference": "27b29f806f366194752cbaaa241e426bed2d942f" + "reference": "9c10c8781000abee812448da3f61ba12a005646f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/patchlevel/hydrator/zipball/27b29f806f366194752cbaaa241e426bed2d942f", - "reference": "27b29f806f366194752cbaaa241e426bed2d942f", + "url": "https://api.github.com/repos/patchlevel/hydrator/zipball/9c10c8781000abee812448da3f61ba12a005646f", + "reference": "9c10c8781000abee812448da3f61ba12a005646f", "shasum": "" }, "require": { @@ -474,9 +474,9 @@ ], "support": { "issues": "https://github.com/patchlevel/hydrator/issues", - "source": "https://github.com/patchlevel/hydrator/tree/1.16.0" + "source": "https://github.com/patchlevel/hydrator/tree/1.17.0" }, - "time": "2026-02-17T11:56:35+00:00" + "time": "2026-03-09T15:44:23+00:00" }, { "name": "patchlevel/worker", From 420b639bf08ac35ad533835e81153e0165627b72 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 05:28:44 +0000 Subject: [PATCH 32/59] Update ramsey/composer-install action to v3.2.0 | datasource | package | from | to | | ----------- | ----------------------- | ----- | ----- | | github-tags | ramsey/composer-install | 3.1.1 | 3.2.0 | Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/backward-compatibility-check.yml | 4 ++-- .github/workflows/benchmark.yml | 4 ++-- .github/workflows/coding-standard.yml | 2 +- .github/workflows/deptrac.yml | 4 ++-- .github/workflows/docs-check.yml | 2 +- .github/workflows/integration.yml | 8 ++++---- .github/workflows/mutation-tests-diff.yml | 2 +- .github/workflows/mutation-tests.yml | 2 +- .github/workflows/phpstan.yml | 2 +- .github/workflows/unit.yml | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/backward-compatibility-check.yml b/.github/workflows/backward-compatibility-check.yml index eb0a370a3..d2fb6ec86 100644 --- a/.github/workflows/backward-compatibility-check.yml +++ b/.github/workflows/backward-compatibility-check.yml @@ -34,11 +34,11 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite, bcmath, intl, sodium - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} working-directory: 'tools' diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 42d6b787f..b2f9aa32d 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -50,7 +50,7 @@ jobs: with: ref: ${{ github.base_ref }} - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} @@ -62,7 +62,7 @@ jobs: with: clean: false - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/coding-standard.yml b/.github/workflows/coding-standard.yml index bfb0ea133..5f8987ac8 100644 --- a/.github/workflows/coding-standard.yml +++ b/.github/workflows/coding-standard.yml @@ -35,7 +35,7 @@ jobs: php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1, opcache.enable_cli=1 - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/deptrac.yml b/.github/workflows/deptrac.yml index 339aee32a..237b8aa1e 100644 --- a/.github/workflows/deptrac.yml +++ b/.github/workflows/deptrac.yml @@ -35,11 +35,11 @@ jobs: php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1 - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} working-directory: 'tools' diff --git a/.github/workflows/docs-check.yml b/.github/workflows/docs-check.yml index e6b5ca295..d7405ec8c 100644 --- a/.github/workflows/docs-check.yml +++ b/.github/workflows/docs-check.yml @@ -34,7 +34,7 @@ jobs: php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1, opcache.enable_cli=1 - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 97379be06..4becd743c 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -58,7 +58,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_pgsql - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} @@ -113,7 +113,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_mysql - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} @@ -167,7 +167,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_mysql - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} @@ -201,7 +201,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} diff --git a/.github/workflows/mutation-tests-diff.yml b/.github/workflows/mutation-tests-diff.yml index db05d5316..14cc1fba6 100644 --- a/.github/workflows/mutation-tests-diff.yml +++ b/.github/workflows/mutation-tests-diff.yml @@ -34,7 +34,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite, pdo_pgsql - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/mutation-tests.yml b/.github/workflows/mutation-tests.yml index f63c2fe32..8d4416639 100644 --- a/.github/workflows/mutation-tests.yml +++ b/.github/workflows/mutation-tests.yml @@ -36,7 +36,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite, pdo_pgsql - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index b5b5a6afe..d9c680056 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -35,7 +35,7 @@ jobs: php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1, opcache.enable_cli=1 - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 2daa95966..091705543 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -48,7 +48,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite, pdo_pgsql - - uses: ramsey/composer-install@3.1.1 + - uses: ramsey/composer-install@3.2.0 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} From 77ab503a20989eb9447e19c2d21fd44b1ec5272d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 18:04:36 +0000 Subject: [PATCH 33/59] Update dependency mkdocs-material to v9.7.5 | datasource | package | from | to | | ---------- | --------------- | ----- | ----- | | pypi | mkdocs-material | 9.7.4 | 9.7.5 | Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index c26eacce0..97170ff4d 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,7 +1,7 @@ mkdocs==1.6.1 mike==2.1.4 markdown==3.10.2 -mkdocs-material==9.7.4 +mkdocs-material==9.7.5 # Markdown extensions Pygments==2.19.2 From 95c0cd8e4668e6181426a6226a1a5b659d220c3e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 14 Mar 2026 00:42:36 +0000 Subject: [PATCH 34/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 5999bafcb..d1a372c71 100644 --- a/composer.lock +++ b/composer.lock @@ -416,16 +416,16 @@ }, { "name": "patchlevel/hydrator", - "version": "1.17.0", + "version": "1.18.0", "source": { "type": "git", "url": "https://github.com/patchlevel/hydrator.git", - "reference": "9c10c8781000abee812448da3f61ba12a005646f" + "reference": "fb802134da1eb6294a4358b1f55dbe3621104d80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/patchlevel/hydrator/zipball/9c10c8781000abee812448da3f61ba12a005646f", - "reference": "9c10c8781000abee812448da3f61ba12a005646f", + "url": "https://api.github.com/repos/patchlevel/hydrator/zipball/fb802134da1eb6294a4358b1f55dbe3621104d80", + "reference": "fb802134da1eb6294a4358b1f55dbe3621104d80", "shasum": "" }, "require": { @@ -474,9 +474,9 @@ ], "support": { "issues": "https://github.com/patchlevel/hydrator/issues", - "source": "https://github.com/patchlevel/hydrator/tree/1.17.0" + "source": "https://github.com/patchlevel/hydrator/tree/1.18.0" }, - "time": "2026-03-09T15:44:23+00:00" + "time": "2026-03-13T17:09:15+00:00" }, { "name": "patchlevel/worker", From 1aed1683989a1b6c45c57a19c02fdc8798d4b528 Mon Sep 17 00:00:00 2001 From: Daniel Badura Date: Sat, 14 Mar 2026 13:58:35 +0100 Subject: [PATCH 35/59] Add new keywords to composer.json --- composer.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 89b479ea5..cfb4e3237 100644 --- a/composer.json +++ b/composer.json @@ -4,8 +4,20 @@ "license": "MIT", "description": "A lightweight but also all-inclusive event sourcing library with a focus on developer experience", "keywords": [ + "events", + "aggregates", + "messages", "event-sourcing", - "ddd" + "domain driven design", + "ddd", + "cqrs", + "projection", + "processor", + "event driven", + "message driven", + "dcb", + "dynamic consistency boundary", + "patchlevel" ], "homepage": "https://event-sourcing.patchlevel.io", "authors": [ From b9a92667117743df5254c336fc73c85c795838e0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 15 Mar 2026 17:12:35 +0000 Subject: [PATCH 36/59] Update shivammathur/setup-php action to v2.37.0 | datasource | package | from | to | | ----------- | ---------------------- | ------ | ------ | | github-tags | shivammathur/setup-php | 2.36.0 | 2.37.0 | Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/backward-compatibility-check.yml | 2 +- .github/workflows/benchmark.yml | 2 +- .github/workflows/coding-standard.yml | 2 +- .github/workflows/deptrac.yml | 2 +- .github/workflows/docs-check.yml | 2 +- .github/workflows/integration.yml | 8 ++++---- .github/workflows/mutation-tests-diff.yml | 2 +- .github/workflows/mutation-tests.yml | 2 +- .github/workflows/phpstan.yml | 2 +- .github/workflows/unit.yml | 2 +- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/backward-compatibility-check.yml b/.github/workflows/backward-compatibility-check.yml index d2fb6ec86..e13e3ea5f 100644 --- a/.github/workflows/backward-compatibility-check.yml +++ b/.github/workflows/backward-compatibility-check.yml @@ -27,7 +27,7 @@ jobs: fetch-depth: 0 - name: "Install PHP" - uses: "shivammathur/setup-php@2.36.0" + uses: "shivammathur/setup-php@2.37.0" with: coverage: "pcov" php-version: "${{ matrix.php-version }}" diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index b2f9aa32d..9039801f1 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -38,7 +38,7 @@ jobs: steps: - name: "Install PHP" - uses: "shivammathur/setup-php@2.36.0" + uses: "shivammathur/setup-php@2.37.0" with: coverage: none php-version: "${{ matrix.php-version }}" diff --git a/.github/workflows/coding-standard.yml b/.github/workflows/coding-standard.yml index 5f8987ac8..204a16aad 100644 --- a/.github/workflows/coding-standard.yml +++ b/.github/workflows/coding-standard.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@v6 - name: "Install PHP" - uses: "shivammathur/setup-php@2.36.0" + uses: "shivammathur/setup-php@2.37.0" with: coverage: none php-version: "${{ matrix.php-version }}" diff --git a/.github/workflows/deptrac.yml b/.github/workflows/deptrac.yml index 237b8aa1e..70f9920a5 100644 --- a/.github/workflows/deptrac.yml +++ b/.github/workflows/deptrac.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@v6 - name: "Install PHP" - uses: "shivammathur/setup-php@2.36.0" + uses: "shivammathur/setup-php@2.37.0" with: coverage: "pcov" php-version: "${{ matrix.php-version }}" diff --git a/.github/workflows/docs-check.yml b/.github/workflows/docs-check.yml index d7405ec8c..16cd7bb80 100644 --- a/.github/workflows/docs-check.yml +++ b/.github/workflows/docs-check.yml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@v6 - name: "Install PHP" - uses: "shivammathur/setup-php@2.36.0" + uses: "shivammathur/setup-php@2.37.0" with: coverage: none php-version: "${{ matrix.php-version }}" diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 4becd743c..e11037431 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -51,7 +51,7 @@ jobs: uses: actions/checkout@v6 - name: "Install PHP" - uses: "shivammathur/setup-php@2.36.0" + uses: "shivammathur/setup-php@2.37.0" with: coverage: "pcov" php-version: "${{ matrix.php-version }}" @@ -106,7 +106,7 @@ jobs: uses: actions/checkout@v6 - name: "Install PHP" - uses: "shivammathur/setup-php@2.36.0" + uses: "shivammathur/setup-php@2.37.0" with: coverage: "pcov" php-version: "${{ matrix.php-version }}" @@ -160,7 +160,7 @@ jobs: uses: actions/checkout@v6 - name: "Install PHP" - uses: "shivammathur/setup-php@2.36.0" + uses: "shivammathur/setup-php@2.37.0" with: coverage: "pcov" php-version: "${{ matrix.php-version }}" @@ -194,7 +194,7 @@ jobs: uses: actions/checkout@v6 - name: "Install PHP" - uses: "shivammathur/setup-php@2.36.0" + uses: "shivammathur/setup-php@2.37.0" with: coverage: "pcov" php-version: "${{ matrix.php-version }}" diff --git a/.github/workflows/mutation-tests-diff.yml b/.github/workflows/mutation-tests-diff.yml index 14cc1fba6..ac638916f 100644 --- a/.github/workflows/mutation-tests-diff.yml +++ b/.github/workflows/mutation-tests-diff.yml @@ -27,7 +27,7 @@ jobs: fetch-depth: 0 - name: "Install PHP" - uses: "shivammathur/setup-php@2.36.0" + uses: "shivammathur/setup-php@2.37.0" with: coverage: "pcov" php-version: "${{ matrix.php-version }}" diff --git a/.github/workflows/mutation-tests.yml b/.github/workflows/mutation-tests.yml index 8d4416639..732643f12 100644 --- a/.github/workflows/mutation-tests.yml +++ b/.github/workflows/mutation-tests.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@v6 - name: "Install PHP" - uses: "shivammathur/setup-php@2.36.0" + uses: "shivammathur/setup-php@2.37.0" with: coverage: "pcov" php-version: "${{ matrix.php-version }}" diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index d9c680056..0975addbc 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@v6 - name: "Install PHP" - uses: "shivammathur/setup-php@2.36.0" + uses: "shivammathur/setup-php@2.37.0" with: coverage: none php-version: "${{ matrix.php-version }}" diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 091705543..e15d4e09c 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -41,7 +41,7 @@ jobs: uses: actions/checkout@v6 - name: "Install PHP" - uses: "shivammathur/setup-php@2.36.0" + uses: "shivammathur/setup-php@2.37.0" with: coverage: "pcov" php-version: "${{ matrix.php-version }}" From 01873123222e31ab9e1dd4f3188835fd8c163360 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 01:28:13 +0000 Subject: [PATCH 37/59] Update ramsey/composer-install action to v3.2.1 | datasource | package | from | to | | ----------- | ----------------------- | ----- | ----- | | github-tags | ramsey/composer-install | 3.2.0 | 3.2.1 | Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/backward-compatibility-check.yml | 4 ++-- .github/workflows/benchmark.yml | 4 ++-- .github/workflows/coding-standard.yml | 2 +- .github/workflows/deptrac.yml | 4 ++-- .github/workflows/docs-check.yml | 2 +- .github/workflows/integration.yml | 8 ++++---- .github/workflows/mutation-tests-diff.yml | 2 +- .github/workflows/mutation-tests.yml | 2 +- .github/workflows/phpstan.yml | 2 +- .github/workflows/unit.yml | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/backward-compatibility-check.yml b/.github/workflows/backward-compatibility-check.yml index e13e3ea5f..3afd29e23 100644 --- a/.github/workflows/backward-compatibility-check.yml +++ b/.github/workflows/backward-compatibility-check.yml @@ -34,11 +34,11 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite, bcmath, intl, sodium - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} working-directory: 'tools' diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 9039801f1..80066d883 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -50,7 +50,7 @@ jobs: with: ref: ${{ github.base_ref }} - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} @@ -62,7 +62,7 @@ jobs: with: clean: false - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/coding-standard.yml b/.github/workflows/coding-standard.yml index 204a16aad..aabbb6f8c 100644 --- a/.github/workflows/coding-standard.yml +++ b/.github/workflows/coding-standard.yml @@ -35,7 +35,7 @@ jobs: php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1, opcache.enable_cli=1 - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/deptrac.yml b/.github/workflows/deptrac.yml index 70f9920a5..1baf6d3d2 100644 --- a/.github/workflows/deptrac.yml +++ b/.github/workflows/deptrac.yml @@ -35,11 +35,11 @@ jobs: php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1 - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} working-directory: 'tools' diff --git a/.github/workflows/docs-check.yml b/.github/workflows/docs-check.yml index 16cd7bb80..dd12be414 100644 --- a/.github/workflows/docs-check.yml +++ b/.github/workflows/docs-check.yml @@ -34,7 +34,7 @@ jobs: php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1, opcache.enable_cli=1 - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index e11037431..709a2d54f 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -58,7 +58,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_pgsql - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} @@ -113,7 +113,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_mysql - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} @@ -167,7 +167,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_mysql - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} @@ -201,7 +201,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} diff --git a/.github/workflows/mutation-tests-diff.yml b/.github/workflows/mutation-tests-diff.yml index ac638916f..bcc6a336c 100644 --- a/.github/workflows/mutation-tests-diff.yml +++ b/.github/workflows/mutation-tests-diff.yml @@ -34,7 +34,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite, pdo_pgsql - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/mutation-tests.yml b/.github/workflows/mutation-tests.yml index 732643f12..b364a92eb 100644 --- a/.github/workflows/mutation-tests.yml +++ b/.github/workflows/mutation-tests.yml @@ -36,7 +36,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite, pdo_pgsql - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 0975addbc..c7c9e7828 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -35,7 +35,7 @@ jobs: php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1, opcache.enable_cli=1 - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index e15d4e09c..13ab91e6d 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -48,7 +48,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite, pdo_pgsql - - uses: ramsey/composer-install@3.2.0 + - uses: ramsey/composer-install@3.2.1 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} From bdbef571faee7c7a789a814dc7f0ae8ead7c9dc4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 06:02:53 +0000 Subject: [PATCH 38/59] Update ramsey/composer-install action to v4 | datasource | package | from | to | | ----------- | ----------------------- | ----- | ----- | | github-tags | ramsey/composer-install | 3.2.1 | 4.0.0 | Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/backward-compatibility-check.yml | 4 ++-- .github/workflows/benchmark.yml | 4 ++-- .github/workflows/coding-standard.yml | 2 +- .github/workflows/deptrac.yml | 4 ++-- .github/workflows/docs-check.yml | 2 +- .github/workflows/integration.yml | 8 ++++---- .github/workflows/mutation-tests-diff.yml | 2 +- .github/workflows/mutation-tests.yml | 2 +- .github/workflows/phpstan.yml | 2 +- .github/workflows/unit.yml | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/backward-compatibility-check.yml b/.github/workflows/backward-compatibility-check.yml index 3afd29e23..04c46e8f0 100644 --- a/.github/workflows/backward-compatibility-check.yml +++ b/.github/workflows/backward-compatibility-check.yml @@ -34,11 +34,11 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite, bcmath, intl, sodium - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} working-directory: 'tools' diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 80066d883..fbf52c424 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -50,7 +50,7 @@ jobs: with: ref: ${{ github.base_ref }} - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} @@ -62,7 +62,7 @@ jobs: with: clean: false - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/coding-standard.yml b/.github/workflows/coding-standard.yml index aabbb6f8c..f64b7670a 100644 --- a/.github/workflows/coding-standard.yml +++ b/.github/workflows/coding-standard.yml @@ -35,7 +35,7 @@ jobs: php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1, opcache.enable_cli=1 - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/deptrac.yml b/.github/workflows/deptrac.yml index 1baf6d3d2..d7b74fe02 100644 --- a/.github/workflows/deptrac.yml +++ b/.github/workflows/deptrac.yml @@ -35,11 +35,11 @@ jobs: php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1 - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} working-directory: 'tools' diff --git a/.github/workflows/docs-check.yml b/.github/workflows/docs-check.yml index dd12be414..429fd9190 100644 --- a/.github/workflows/docs-check.yml +++ b/.github/workflows/docs-check.yml @@ -34,7 +34,7 @@ jobs: php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1, opcache.enable_cli=1 - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 709a2d54f..5abfba81a 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -58,7 +58,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_pgsql - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} @@ -113,7 +113,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_mysql - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} @@ -167,7 +167,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_mysql - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} @@ -201,7 +201,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} diff --git a/.github/workflows/mutation-tests-diff.yml b/.github/workflows/mutation-tests-diff.yml index bcc6a336c..c615f1202 100644 --- a/.github/workflows/mutation-tests-diff.yml +++ b/.github/workflows/mutation-tests-diff.yml @@ -34,7 +34,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite, pdo_pgsql - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/mutation-tests.yml b/.github/workflows/mutation-tests.yml index b364a92eb..f152731e4 100644 --- a/.github/workflows/mutation-tests.yml +++ b/.github/workflows/mutation-tests.yml @@ -36,7 +36,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite, pdo_pgsql - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index c7c9e7828..a0887de83 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -35,7 +35,7 @@ jobs: php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1, opcache.enable_cli=1 - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 13ab91e6d..8e6a0811a 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -48,7 +48,7 @@ jobs: ini-values: memory_limit=-1 extensions: pdo_sqlite, pdo_pgsql - - uses: ramsey/composer-install@3.2.1 + - uses: ramsey/composer-install@4.0.0 with: dependency-versions: ${{ matrix.dependencies }} composer-options: ${{ matrix.composer-options }} From 8f898d0ae9fa311b31b9448db96d99530e50e079 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 01:43:27 +0000 Subject: [PATCH 39/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 8 ++++---- tools/composer.lock | 17 +++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/composer.lock b/composer.lock index d1a372c71..b6517010b 100644 --- a/composer.lock +++ b/composer.lock @@ -4652,11 +4652,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.40", + "version": "2.1.41", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", - "reference": "9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a2eae8f20856b3afe74bf1f9726ce8c11438e300", + "reference": "a2eae8f20856b3afe74bf1f9726ce8c11438e300", "shasum": "" }, "require": { @@ -4701,7 +4701,7 @@ "type": "github" } ], - "time": "2026-02-23T15:04:35+00:00" + "time": "2026-03-16T18:24:10+00:00" }, { "name": "phpstan/phpstan-phpunit", diff --git a/tools/composer.lock b/tools/composer.lock index dce23a34d..05db6717f 100644 --- a/tools/composer.lock +++ b/tools/composer.lock @@ -11,12 +11,12 @@ "version": "4.3.0", "source": { "type": "git", - "url": "https://github.com/azjezz/psl.git", + "url": "https://github.com/php-standard-library/php-standard-library.git", "reference": "74c95be0214eb7ea39146ed00ac4eb71b45d787b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/azjezz/psl/zipball/74c95be0214eb7ea39146ed00ac4eb71b45d787b", + "url": "https://api.github.com/repos/php-standard-library/php-standard-library/zipball/74c95be0214eb7ea39146ed00ac4eb71b45d787b", "reference": "74c95be0214eb7ea39146ed00ac4eb71b45d787b", "shasum": "" }, @@ -67,8 +67,8 @@ ], "description": "PHP Standard Library", "support": { - "issues": "https://github.com/azjezz/psl/issues", - "source": "https://github.com/azjezz/psl/tree/4.3.0" + "issues": "https://github.com/php-standard-library/php-standard-library/issues", + "source": "https://github.com/php-standard-library/php-standard-library/tree/4.3.0" }, "funding": [ { @@ -80,6 +80,7 @@ "type": "github" } ], + "abandoned": "php-standard-library/php-standard-library", "time": "2026-02-24T01:58:53+00:00" }, { @@ -1494,11 +1495,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.40", + "version": "2.1.41", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", - "reference": "9b2c7aeb83a75d8680ea5e7c9b7fca88052b766b", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a2eae8f20856b3afe74bf1f9726ce8c11438e300", + "reference": "a2eae8f20856b3afe74bf1f9726ce8c11438e300", "shasum": "" }, "require": { @@ -1543,7 +1544,7 @@ "type": "github" } ], - "time": "2026-02-23T15:04:35+00:00" + "time": "2026-03-16T18:24:10+00:00" }, { "name": "psr/container", From 9bd202e837ab233d1e7cc0a50b62e2708bff0914 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Mar 2026 00:45:22 +0000 Subject: [PATCH 40/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 20 ++++++++++---------- tools/composer.lock | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/composer.lock b/composer.lock index b6517010b..99eb4d73c 100644 --- a/composer.lock +++ b/composer.lock @@ -4398,16 +4398,16 @@ }, { "name": "phpat/phpat", - "version": "0.12.3", + "version": "0.12.4", "source": { "type": "git", "url": "https://github.com/carlosas/phpat.git", - "reference": "2412a8959254a076e751498cbba8cf29406e0cf4" + "reference": "5319264270c335f548451209bb0f32b55aa59924" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/carlosas/phpat/zipball/2412a8959254a076e751498cbba8cf29406e0cf4", - "reference": "2412a8959254a076e751498cbba8cf29406e0cf4", + "url": "https://api.github.com/repos/carlosas/phpat/zipball/5319264270c335f548451209bb0f32b55aa59924", + "reference": "5319264270c335f548451209bb0f32b55aa59924", "shasum": "" }, "require": { @@ -4449,9 +4449,9 @@ "description": "PHP Architecture Tester", "support": { "issues": "https://github.com/carlosas/phpat/issues", - "source": "https://github.com/carlosas/phpat/tree/0.12.3" + "source": "https://github.com/carlosas/phpat/tree/0.12.4" }, - "time": "2026-02-20T11:15:22+00:00" + "time": "2026-03-17T16:47:43+00:00" }, { "name": "phpbench/container", @@ -4652,11 +4652,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.41", + "version": "2.1.42", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a2eae8f20856b3afe74bf1f9726ce8c11438e300", - "reference": "a2eae8f20856b3afe74bf1f9726ce8c11438e300", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1279e1ce86ba768f0780c9d889852b4e02ff40d0", + "reference": "1279e1ce86ba768f0780c9d889852b4e02ff40d0", "shasum": "" }, "require": { @@ -4701,7 +4701,7 @@ "type": "github" } ], - "time": "2026-03-16T18:24:10+00:00" + "time": "2026-03-17T14:58:32+00:00" }, { "name": "phpstan/phpstan-phpunit", diff --git a/tools/composer.lock b/tools/composer.lock index 05db6717f..28aee4fdc 100644 --- a/tools/composer.lock +++ b/tools/composer.lock @@ -1495,11 +1495,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.41", + "version": "2.1.42", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a2eae8f20856b3afe74bf1f9726ce8c11438e300", - "reference": "a2eae8f20856b3afe74bf1f9726ce8c11438e300", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1279e1ce86ba768f0780c9d889852b4e02ff40d0", + "reference": "1279e1ce86ba768f0780c9d889852b4e02ff40d0", "shasum": "" }, "require": { @@ -1544,7 +1544,7 @@ "type": "github" } ], - "time": "2026-03-16T18:24:10+00:00" + "time": "2026-03-17T14:58:32+00:00" }, { "name": "psr/container", From c01e0991041bd4fd2238eb7f1a4784425e57fc4b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:40:16 +0000 Subject: [PATCH 41/59] Update dependency mkdocs-material to v9.7.6 | datasource | package | from | to | | ---------- | --------------- | ----- | ----- | | pypi | mkdocs-material | 9.7.5 | 9.7.6 | Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 97170ff4d..78a702fd9 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,7 +1,7 @@ mkdocs==1.6.1 mike==2.1.4 markdown==3.10.2 -mkdocs-material==9.7.5 +mkdocs-material==9.7.6 # Markdown extensions Pygments==2.19.2 From aeda607f3cb5fef0b3fd655568b99fcf5b229d53 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 01:09:17 +0000 Subject: [PATCH 42/59] Update dependency league/commonmark to v2.8.2 [SECURITY] | datasource | package | from | to | | ---------- | ----------------- | ----- | ----- | | packagist | league/commonmark | 2.8.1 | 2.8.2 | Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.lock b/composer.lock index 99eb4d73c..c20430d66 100644 --- a/composer.lock +++ b/composer.lock @@ -3609,16 +3609,16 @@ }, { "name": "league/commonmark", - "version": "2.8.1", + "version": "2.8.2", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "84b1ca48347efdbe775426f108622a42735a6579" + "reference": "59fb075d2101740c337c7216e3f32b36c204218b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/84b1ca48347efdbe775426f108622a42735a6579", - "reference": "84b1ca48347efdbe775426f108622a42735a6579", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/59fb075d2101740c337c7216e3f32b36c204218b", + "reference": "59fb075d2101740c337c7216e3f32b36c204218b", "shasum": "" }, "require": { @@ -3712,7 +3712,7 @@ "type": "tidelift" } ], - "time": "2026-03-05T21:37:03+00:00" + "time": "2026-03-19T13:16:38+00:00" }, { "name": "league/config", From 941a5ba9d6841a0599785b481fbd6c55630a5021 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 10:07:32 +0000 Subject: [PATCH 43/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index c20430d66..ff1426b21 100644 --- a/composer.lock +++ b/composer.lock @@ -68,16 +68,16 @@ }, { "name": "doctrine/dbal", - "version": "4.4.2", + "version": "4.4.3", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "476f7f0fa6ea4aa5364926db7fabdf6049075722" + "reference": "61e730f1658814821a85f2402c945f3883407dec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/476f7f0fa6ea4aa5364926db7fabdf6049075722", - "reference": "476f7f0fa6ea4aa5364926db7fabdf6049075722", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/61e730f1658814821a85f2402c945f3883407dec", + "reference": "61e730f1658814821a85f2402c945f3883407dec", "shasum": "" }, "require": { @@ -154,7 +154,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/4.4.2" + "source": "https://github.com/doctrine/dbal/tree/4.4.3" }, "funding": [ { @@ -170,7 +170,7 @@ "type": "tidelift" } ], - "time": "2026-02-26T12:12:19+00:00" + "time": "2026-03-20T08:52:12+00:00" }, { "name": "doctrine/deprecations", From d4b0fea3e14ad17f217e8050d3eb3e3d5ea9117c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 21 Mar 2026 00:46:12 +0000 Subject: [PATCH 44/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- tools/composer.lock | 1852 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 1765 insertions(+), 87 deletions(-) diff --git a/tools/composer.lock b/tools/composer.lock index 28aee4fdc..8bd6f933f 100644 --- a/tools/composer.lock +++ b/tools/composer.lock @@ -6,83 +6,6 @@ ], "content-hash": "bc2a574929879bec056439db3b8d8736", "packages": [ - { - "name": "azjezz/psl", - "version": "4.3.0", - "source": { - "type": "git", - "url": "https://github.com/php-standard-library/php-standard-library.git", - "reference": "74c95be0214eb7ea39146ed00ac4eb71b45d787b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-standard-library/php-standard-library/zipball/74c95be0214eb7ea39146ed00ac4eb71b45d787b", - "reference": "74c95be0214eb7ea39146ed00ac4eb71b45d787b", - "shasum": "" - }, - "require": { - "ext-bcmath": "*", - "ext-intl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "ext-sodium": "*", - "php": "~8.3.0 || ~8.4.0 || ~8.5.0", - "revolt/event-loop": "^1.0.7" - }, - "require-dev": { - "carthage-software/mago": "^1.6.0", - "infection/infection": "^0.31.2", - "php-coveralls/php-coveralls": "^2.7.0", - "phpbench/phpbench": "^1.4.0", - "phpunit/phpunit": "^9.6.22" - }, - "suggest": { - "php-standard-library/phpstan-extension": "PHPStan integration", - "php-standard-library/psalm-plugin": "Psalm integration" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/hhvm/hsl", - "name": "hhvm/hsl" - } - }, - "autoload": { - "files": [ - "src/bootstrap.php" - ], - "psr-4": { - "Psl\\": "src/Psl" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "azjezz", - "email": "azjezz@protonmail.com" - } - ], - "description": "PHP Standard Library", - "support": { - "issues": "https://github.com/php-standard-library/php-standard-library/issues", - "source": "https://github.com/php-standard-library/php-standard-library/tree/4.3.0" - }, - "funding": [ - { - "url": "https://github.com/azjezz", - "type": "github" - }, - { - "url": "https://github.com/veewee", - "type": "github" - } - ], - "abandoned": "php-standard-library/php-standard-library", - "time": "2026-02-24T01:58:53+00:00" - }, { "name": "beberlei/assert", "version": "v3.3.3", @@ -1282,6 +1205,1747 @@ ], "time": "2025-11-27T11:43:11+00:00" }, + { + "name": "php-standard-library/async", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/async.git", + "reference": "abd7c05295ff1bdbe498973294b7dc0f0b5bd7bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/async/zipball/abd7c05295ff1bdbe498973294b7dc0f0b5bd7bf", + "reference": "abd7c05295ff1bdbe498973294b7dc0f0b5bd7bf", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/date-time": "^6.0", + "php-standard-library/default": "^6.0", + "php-standard-library/foundation": "^6.0", + "php-standard-library/promise": "^6.0", + "revolt/event-loop": "^1.0.8" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/dict": "^6.0", + "php-standard-library/result": "^6.0", + "php-standard-library/str": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Async\\": "src/Psl/Async/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Fiber-based structured concurrency using cooperative multitasking", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "Fibers", + "async", + "concurrency", + "cooperative-multitasking" + ], + "support": { + "source": "https://github.com/php-standard-library/async/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/channel", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/channel.git", + "reference": "96654b43182091c1b935f52ce65d16429d7311a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/channel/zipball/96654b43182091c1b935f52ce65d16429d7311a9", + "reference": "96654b43182091c1b935f52ce65d16429d7311a9", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/async": "^6.0", + "php-standard-library/foundation": "^6.0", + "revolt/event-loop": "^1.0.8" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/date-time": "^6.0", + "php-standard-library/file": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Channel\\": "src/Psl/Channel/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Message-passing channels for async communication, inspired by Go and Rust", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "async", + "channel", + "concurrency", + "message-passing" + ], + "support": { + "source": "https://github.com/php-standard-library/channel/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/collection", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/collection.git", + "reference": "810decf3218650445b41cf03d398f32cbf2c2d85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/collection/zipball/810decf3218650445b41cf03d398f32cbf2c2d85", + "reference": "810decf3218650445b41cf03d398f32cbf2c2d85", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/default": "^6.0", + "php-standard-library/foundation": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/str": "^6.0", + "php-standard-library/vec": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psl\\Collection\\": "src/Psl/Collection/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Generic, object-oriented Vector, Map, and Set collections with immutable and mutable variants", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "collection", + "generics", + "map", + "set", + "vector" + ], + "support": { + "source": "https://github.com/php-standard-library/collection/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/comparison", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/comparison.git", + "reference": "1bfb6d50a4544d29bec9908e7d64f2c07b6e813c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/comparison/zipball/1bfb6d50a4544d29bec9908e7d64f2c07b6e813c", + "reference": "1bfb6d50a4544d29bec9908e7d64f2c07b6e813c", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/default": "^6.0", + "php-standard-library/foundation": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Comparison\\": "src/Psl/Comparison/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Interfaces and functions for type-safe, consistent value comparison", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "comparable", + "comparison", + "ordering" + ], + "support": { + "source": "https://github.com/php-standard-library/comparison/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/date-time", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/date-time.git", + "reference": "7d97bac5afe71a98c54ca54df0b60fa5c6b270b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/date-time/zipball/7d97bac5afe71a98c54ca54df0b60fa5c6b270b8", + "reference": "7d97bac5afe71a98c54ca54df0b60fa5c6b270b8", + "shasum": "" + }, + "require": { + "ext-intl": "*", + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/comparison": "^6.0", + "php-standard-library/default": "^6.0", + "php-standard-library/foundation": "^6.0", + "php-standard-library/interoperability": "^6.0", + "php-standard-library/locale": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/json": "^6.0", + "php-standard-library/math": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\DateTime\\": "src/Psl/DateTime/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Immutable, timezone-aware date and time types with Duration, Period, and Interval", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "datetime", + "duration", + "immutable", + "timezone" + ], + "support": { + "source": "https://github.com/php-standard-library/date-time/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/default", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/default.git", + "reference": "9f5fff4b952963c2e1d922c4669994c0d2790cc3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/default/zipball/9f5fff4b952963c2e1d922c4669994c0d2790cc3", + "reference": "9f5fff4b952963c2e1d922c4669994c0d2790cc3", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psl\\Default\\": "src/Psl/Default/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "DefaultInterface for classes to provide standardized default instances", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "default", + "interface" + ], + "support": { + "source": "https://github.com/php-standard-library/default/tree/6.1.1" + }, + "time": "2026-03-19T03:52:36+00:00" + }, + { + "name": "php-standard-library/dict", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/dict.git", + "reference": "d67628e5c963dc8456277cbb5f34e6821c4dcd4a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/dict/zipball/d67628e5c963dc8456277cbb5f34e6821c4dcd4a", + "reference": "d67628e5c963dc8456277cbb5f34e6821c4dcd4a", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/foundation": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/collection": "^6.0", + "php-standard-library/iter": "^6.0", + "php-standard-library/str": "^6.0", + "php-standard-library/vec": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Dict\\": "src/Psl/Dict/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Functions for creating and transforming associative arrays with preserved keys", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "Associative", + "Dict", + "array", + "map" + ], + "support": { + "source": "https://github.com/php-standard-library/dict/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/env", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/env.git", + "reference": "2ffd1744f048a304edf0d07b2ecaabf984abded9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/env/zipball/2ffd1744f048a304edf0d07b2ecaabf984abded9", + "reference": "2ffd1744f048a304edf0d07b2ecaabf984abded9", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/foundation": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/filesystem": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Env\\": "src/Psl/Env/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Functions for inspecting and modifying environment variables, working directory, and paths", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "env", + "environment" + ], + "support": { + "source": "https://github.com/php-standard-library/env/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/file", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/file.git", + "reference": "362bb70d25ce8fa9a4f18dc84ae228944ae8396c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/file/zipball/362bb70d25ce8fa9a4f18dc84ae228944ae8396c", + "reference": "362bb70d25ce8fa9a4f18dc84ae228944ae8396c", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/async": "^6.0", + "php-standard-library/foundation": "^6.0", + "php-standard-library/io": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/env": "^6.0", + "php-standard-library/filesystem": "^6.0", + "php-standard-library/os": "^6.0", + "php-standard-library/str": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\File\\": "src/Psl/File/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Typed file handles for reading and writing with write modes and advisory locking", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "file", + "handle", + "io" + ], + "support": { + "source": "https://github.com/php-standard-library/file/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/filesystem", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/filesystem.git", + "reference": "00b2dc3e5a97d8e5167c6b2d7ea1571c17938dd7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/filesystem/zipball/00b2dc3e5a97d8e5167c6b2d7ea1571c17938dd7", + "reference": "00b2dc3e5a97d8e5167c6b2d7ea1571c17938dd7", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/foundation": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/env": "^6.0", + "php-standard-library/file": "^6.0", + "php-standard-library/os": "^6.0", + "php-standard-library/str": "^6.0", + "php-standard-library/type": "^6.0", + "php-standard-library/vec": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Filesystem\\": "src/Psl/Filesystem/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Type-safe functions for file system operations with proper exception handling", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "file-system", + "filesystem" + ], + "support": { + "source": "https://github.com/php-standard-library/filesystem/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/foundation", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/foundation.git", + "reference": "404e032a4718d19ada14af614458b38c9021242c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/foundation/zipball/404e032a4718d19ada14af614458b38c9021242c", + "reference": "404e032a4718d19ada14af614458b38c9021242c", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\": "src/Psl/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Exceptions, Ref, and invariant functions", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "exceptions", + "foundation", + "invariant" + ], + "support": { + "source": "https://github.com/php-standard-library/foundation/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/interoperability", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/interoperability.git", + "reference": "e04c5056f3ec9ffc88c972f9d257b9704a11b88e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/interoperability/zipball/e04c5056f3ec9ffc88c972f9d257b9704a11b88e", + "reference": "e04c5056f3ec9ffc88c972f9d257b9704a11b88e", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psl\\Interoperability\\": "src/Psl/Interoperability/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Interfaces for converting between PSL types and PHP stdlib/intl equivalents", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "conversion", + "interoperability" + ], + "support": { + "source": "https://github.com/php-standard-library/interoperability/tree/6.1.1" + }, + "time": "2026-03-19T03:52:36+00:00" + }, + { + "name": "php-standard-library/io", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/io.git", + "reference": "26d16c851c4f2bcafb49b7a583b1a72fcd05d0dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/io/zipball/26d16c851c4f2bcafb49b7a583b1a72fcd05d0dc", + "reference": "26d16c851c4f2bcafb49b7a583b1a72fcd05d0dc", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/async": "^6.0", + "php-standard-library/channel": "^6.0", + "php-standard-library/foundation": "^6.0", + "php-standard-library/result": "^6.0", + "revolt/event-loop": "^1.0.8" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/date-time": "^6.0", + "php-standard-library/os": "^6.0", + "php-standard-library/str": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\IO\\": "src/Psl/IO/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Handle-based I/O abstractions - composable, testable, and async-ready", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "async", + "handle", + "io", + "stream" + ], + "support": { + "source": "https://github.com/php-standard-library/io/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/iter", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/iter.git", + "reference": "32e89ceaf035a1f2ac1c370ec28bfee476a8b298" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/iter/zipball/32e89ceaf035a1f2ac1c370ec28bfee476a8b298", + "reference": "32e89ceaf035a1f2ac1c370ec28bfee476a8b298", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/foundation": "^6.0", + "php-standard-library/option": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/collection": "^6.0", + "php-standard-library/dict": "^6.0", + "php-standard-library/math": "^6.0", + "php-standard-library/vec": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Iter\\": "src/Psl/Iter/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Utility functions for inspecting and reducing iterables - arrays, generators, and iterators", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "generator", + "iter", + "iterable", + "iterator" + ], + "support": { + "source": "https://github.com/php-standard-library/iter/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/json", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/json.git", + "reference": "ad3ff6f9e22ed27b0eccc58bbc7d83242841f775" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/json/zipball/ad3ff6f9e22ed27b0eccc58bbc7d83242841f775", + "reference": "ad3ff6f9e22ed27b0eccc58bbc7d83242841f775", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/foundation": "^6.0", + "php-standard-library/type": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/collection": "^6.0", + "php-standard-library/math": "^6.0", + "php-standard-library/str": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Json\\": "src/Psl/Json/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "JSON encoding and decoding with typed exceptions and sensible defaults", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "decoding", + "encoding", + "json" + ], + "support": { + "source": "https://github.com/php-standard-library/json/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/locale", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/locale.git", + "reference": "f07d093011f026696ae331e096268e6a21c4bc05" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/locale/zipball/f07d093011f026696ae331e096268e6a21c4bc05", + "reference": "f07d093011f026696ae331e096268e6a21c4bc05", + "shasum": "" + }, + "require": { + "ext-intl": "*", + "php": "~8.4.0 || ~8.5.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/str": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psl\\Locale\\": "src/Psl/Locale/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Backed enum with 700+ locale identifiers for type-safe internationalization", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "i18n", + "internationalization", + "locale" + ], + "support": { + "source": "https://github.com/php-standard-library/locale/tree/6.1.1" + }, + "time": "2026-03-19T16:33:51+00:00" + }, + { + "name": "php-standard-library/option", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/option.git", + "reference": "7b75ae4886380deaa5b1b991953fac208d047061" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/option/zipball/7b75ae4886380deaa5b1b991953fac208d047061", + "reference": "7b75ae4886380deaa5b1b991953fac208d047061", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/comparison": "^6.0", + "php-standard-library/foundation": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Option\\": "src/Psl/Option/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Option type (Some/None) replacing nullable types with explicit presence semantics", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "monad", + "none", + "option", + "some" + ], + "support": { + "source": "https://github.com/php-standard-library/option/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/process", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/process.git", + "reference": "4273b57d9b3b9a19306251eeddcfd6930cabb351" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/process/zipball/4273b57d9b3b9a19306251eeddcfd6930cabb351", + "reference": "4273b57d9b3b9a19306251eeddcfd6930cabb351", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/async": "^6.0", + "php-standard-library/date-time": "^6.0", + "php-standard-library/foundation": "^6.0", + "php-standard-library/io": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/env": "^6.0", + "php-standard-library/filesystem": "^6.0", + "php-standard-library/os": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psl\\Process\\": "src/Psl/Process/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Typed, non-blocking API for spawning and managing child processes", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "async", + "process", + "subprocess" + ], + "support": { + "source": "https://github.com/php-standard-library/process/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/promise", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/promise.git", + "reference": "02d8479aaa35a28b72aad0ba6b58d389d87c131c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/promise/zipball/02d8479aaa35a28b72aad0ba6b58d389d87c131c", + "reference": "02d8479aaa35a28b72aad0ba6b58d389d87c131c", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psl\\Promise\\": "src/Psl/Promise/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Promise interface for deferred computations - resolved or rejected", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "async", + "deferred", + "promise" + ], + "support": { + "source": "https://github.com/php-standard-library/promise/tree/6.1.1" + }, + "time": "2026-03-19T03:52:36+00:00" + }, + { + "name": "php-standard-library/range", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/range.git", + "reference": "6085700229b7b10e78afd0f490323a19c37511e3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/range/zipball/6085700229b7b10e78afd0f490323a19c37511e3", + "reference": "6085700229b7b10e78afd0f490323a19c37511e3", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/foundation": "^6.0", + "php-standard-library/iter": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/math": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Range\\": "src/Psl/Range/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Range types for integer sequences with iteration support", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "iterator", + "range" + ], + "support": { + "source": "https://github.com/php-standard-library/range/tree/6.1.1" + }, + "time": "2026-03-19T03:52:36+00:00" + }, + { + "name": "php-standard-library/regex", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/regex.git", + "reference": "ba19a34048c9978daf216b886ea77f7aa7b07937" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/regex/zipball/ba19a34048c9978daf216b886ea77f7aa7b07937", + "reference": "ba19a34048c9978daf216b886ea77f7aa7b07937", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/foundation": "^6.0", + "php-standard-library/type": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Regex\\": "src/Psl/Regex/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Type-safe regular expressions with typed capture groups and proper error handling", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "preg", + "regex", + "regular-expression" + ], + "support": { + "source": "https://github.com/php-standard-library/regex/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/result", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/result.git", + "reference": "60598868e6d451553c97ee82c65f519b5f043490" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/result/zipball/60598868e6d451553c97ee82c65f519b5f043490", + "reference": "60598868e6d451553c97ee82c65f519b5f043490", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/foundation": "^6.0", + "php-standard-library/promise": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/fun": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Result\\": "src/Psl/Result/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Result type capturing success or failure as a value for controlled error handling", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "error-handling", + "monad", + "result" + ], + "support": { + "source": "https://github.com/php-standard-library/result/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/shell", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/shell.git", + "reference": "21e8003fa8ba414417c19069eaef2940837c8f2b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/shell/zipball/21e8003fa8ba414417c19069eaef2940837c8f2b", + "reference": "21e8003fa8ba414417c19069eaef2940837c8f2b", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/async": "^6.0", + "php-standard-library/default": "^6.0", + "php-standard-library/foundation": "^6.0", + "php-standard-library/process": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/date-time": "^6.0", + "php-standard-library/env": "^6.0", + "php-standard-library/os": "^6.0", + "php-standard-library/secure-random": "^6.0", + "php-standard-library/str": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Shell\\": "src/Psl/Shell/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Shell command execution with argument escaping and error output management", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "command", + "execute", + "shell" + ], + "support": { + "source": "https://github.com/php-standard-library/shell/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/str", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/str.git", + "reference": "fb806004b9eaf36f66a5d45b097d70afbcdca873" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/str/zipball/fb806004b9eaf36f66a5d45b097d70afbcdca873", + "reference": "fb806004b9eaf36f66a5d45b097d70afbcdca873", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/default": "^6.0", + "php-standard-library/foundation": "^6.0", + "php-standard-library/range": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Str\\": "src/Psl/Str/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Unicode-aware string functions replacing PHP mb_* and standard string functions", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "mbstring", + "string", + "unicode", + "utf8" + ], + "support": { + "source": "https://github.com/php-standard-library/str/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/type", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/type.git", + "reference": "642c3d038b5a627d33a2152115a7d57e6eff2d5a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/type/zipball/642c3d038b5a627d33a2152115a7d57e6eff2d5a", + "reference": "642c3d038b5a627d33a2152115a7d57e6eff2d5a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/collection": "^6.0", + "php-standard-library/foundation": "^6.0", + "php-standard-library/iter": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/dict": "^6.0", + "php-standard-library/math": "^6.0", + "php-standard-library/result": "^6.0", + "php-standard-library/str": "^6.0", + "php-standard-library/vec": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Type\\": "src/Psl/Type/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Runtime type validation implementing Parse, Don't Validate - coerce and assert unstructured input into well-typed data", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "coercion", + "type", + "type-safety", + "validation" + ], + "support": { + "source": "https://github.com/php-standard-library/type/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, + { + "name": "php-standard-library/vec", + "version": "6.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-standard-library/vec.git", + "reference": "e41db9b7140bb75bf5996495eed1b7b36b1057bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-standard-library/vec/zipball/e41db9b7140bb75bf5996495eed1b7b36b1057bb", + "reference": "e41db9b7140bb75bf5996495eed1b7b36b1057bb", + "shasum": "" + }, + "require": { + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/foundation": "^6.0" + }, + "conflict": { + "azjezz/psl": "*" + }, + "require-dev": { + "php-standard-library/collection": "^6.0", + "php-standard-library/fun": "^6.0", + "php-standard-library/iter": "^6.0", + "php-standard-library/str": "^6.0", + "phpunit/phpunit": "^13.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-next": "6.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/Psl/bootstrap.php" + ], + "psr-4": { + "Psl\\Vec\\": "src/Psl/Vec/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Seifeddine Gmati", + "email": "azjezz@carthage.software" + }, + { + "name": "Contributors", + "homepage": "https://github.com/php-standard-library/php-standard-library/graphs/contributors" + } + ], + "description": "Functions for creating and transforming sequential, 0-indexed arrays (lists)", + "homepage": "https://php-standard-library.dev", + "keywords": [ + "array", + "list", + "vec", + "vector" + ], + "support": { + "source": "https://github.com/php-standard-library/vec/tree/6.1.1" + }, + "time": "2026-03-20T08:02:33+00:00" + }, { "name": "phpdocumentor/graphviz", "version": "2.1.0", @@ -1846,20 +3510,19 @@ }, { "name": "roave/backward-compatibility-check", - "version": "8.19.0", + "version": "8.20.0", "source": { "type": "git", "url": "https://github.com/Roave/BackwardCompatibilityCheck.git", - "reference": "810eb88eeff37ef300653bcfb77ca51a314777a2" + "reference": "ba4f4b2c4d388d2bf974d1fefa28557e628d8da4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/BackwardCompatibilityCheck/zipball/810eb88eeff37ef300653bcfb77ca51a314777a2", - "reference": "810eb88eeff37ef300653bcfb77ca51a314777a2", + "url": "https://api.github.com/repos/Roave/BackwardCompatibilityCheck/zipball/ba4f4b2c4d388d2bf974d1fefa28557e628d8da4", + "reference": "ba4f4b2c4d388d2bf974d1fefa28557e628d8da4", "shasum": "" }, "require": { - "azjezz/psl": "^4.2.1", "composer/composer": "^2.9.5", "ext-dom": "*", "ext-json": "*", @@ -1868,7 +3531,20 @@ "nikic/php-parser": "^5.7.0", "nikolaposa/version": "^4.2.1", "ocramius/package-versions": "^2.11.0", - "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "php": "~8.4.0 || ~8.5.0", + "php-standard-library/async": "^6.0", + "php-standard-library/dict": "^6.0", + "php-standard-library/env": "^6.0", + "php-standard-library/file": "^6.0", + "php-standard-library/filesystem": "^6.0", + "php-standard-library/foundation": "^6.0", + "php-standard-library/iter": "^6.0", + "php-standard-library/json": "^6.0", + "php-standard-library/regex": "^6.0", + "php-standard-library/shell": "^6.0", + "php-standard-library/str": "^6.0", + "php-standard-library/type": "^6.0", + "php-standard-library/vec": "^6.0", "roave/better-reflection": "^6.69.0", "symfony/console": "^7.4.4" }, @@ -1879,9 +3555,11 @@ }, "require-dev": { "doctrine/coding-standard": "^14.0.0", - "justinrainbow/json-schema": "^6.6.4", + "justinrainbow/json-schema": "^6.7.2", + "php-standard-library/hash": "^6.0", "php-standard-library/psalm-plugin": "^2.3.0", - "phpunit/phpunit": "^12.5.11", + "php-standard-library/secure-random": "^6.0", + "phpunit/phpunit": "^12.5.12", "psalm/plugin-phpunit": "^0.19.5", "roave/infection-static-analysis-plugin": "^1.43.0", "roave/security-advisories": "dev-master", @@ -1914,9 +3592,9 @@ "description": "Tool to compare two revisions of a public API to check for BC breaks", "support": { "issues": "https://github.com/Roave/BackwardCompatibilityCheck/issues", - "source": "https://github.com/Roave/BackwardCompatibilityCheck/tree/8.19.0" + "source": "https://github.com/Roave/BackwardCompatibilityCheck/tree/8.20.0" }, - "time": "2026-02-13T19:14:25+00:00" + "time": "2026-03-20T13:02:25+00:00" }, { "name": "roave/better-reflection", From 9d5f1a42d3097dcff1aff51bae9ab18c54091089 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 22 Mar 2026 01:59:05 +0000 Subject: [PATCH 45/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index ff1426b21..1b6124001 100644 --- a/composer.lock +++ b/composer.lock @@ -4506,16 +4506,16 @@ }, { "name": "phpbench/phpbench", - "version": "1.5.1", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "9a28fd0833f11171b949843c6fd663eb69b6d14c" + "reference": "fdeb35d0d277dd6b9e795df6cbbec9218f3b143e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/9a28fd0833f11171b949843c6fd663eb69b6d14c", - "reference": "9a28fd0833f11171b949843c6fd663eb69b6d14c", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/fdeb35d0d277dd6b9e795df6cbbec9218f3b143e", + "reference": "fdeb35d0d277dd6b9e795df6cbbec9218f3b143e", "shasum": "" }, "require": { @@ -4593,7 +4593,7 @@ ], "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.5.1" + "source": "https://github.com/phpbench/phpbench/tree/1.6.0" }, "funding": [ { @@ -4601,7 +4601,7 @@ "type": "github" } ], - "time": "2026-03-05T08:18:58+00:00" + "time": "2026-03-21T18:08:17+00:00" }, { "name": "phpstan/phpdoc-parser", From f99b366a827fedce92375081efebe0cc0b20691a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 01:56:22 +0000 Subject: [PATCH 46/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 1b6124001..b10492c29 100644 --- a/composer.lock +++ b/composer.lock @@ -4506,16 +4506,16 @@ }, { "name": "phpbench/phpbench", - "version": "1.6.0", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "fdeb35d0d277dd6b9e795df6cbbec9218f3b143e" + "reference": "661c8c6abbc7734986cf7bc6062c237fbb450461" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/fdeb35d0d277dd6b9e795df6cbbec9218f3b143e", - "reference": "fdeb35d0d277dd6b9e795df6cbbec9218f3b143e", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/661c8c6abbc7734986cf7bc6062c237fbb450461", + "reference": "661c8c6abbc7734986cf7bc6062c237fbb450461", "shasum": "" }, "require": { @@ -4593,7 +4593,7 @@ ], "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.6.0" + "source": "https://github.com/phpbench/phpbench/tree/1.6.1" }, "funding": [ { @@ -4601,7 +4601,7 @@ "type": "github" } ], - "time": "2026-03-21T18:08:17+00:00" + "time": "2026-03-22T10:27:20+00:00" }, { "name": "phpstan/phpdoc-parser", From c25a95e4fb6b198440b4807d24287a04c18839e7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 01:22:33 +0000 Subject: [PATCH 47/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 8 ++++---- tools/composer.lock | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index b10492c29..bf7c8c480 100644 --- a/composer.lock +++ b/composer.lock @@ -4652,11 +4652,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.42", + "version": "2.1.43", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1279e1ce86ba768f0780c9d889852b4e02ff40d0", - "reference": "1279e1ce86ba768f0780c9d889852b4e02ff40d0", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d01bebe3edfd4d49b9666ee5b8271ddca561042f", + "reference": "d01bebe3edfd4d49b9666ee5b8271ddca561042f", "shasum": "" }, "require": { @@ -4701,7 +4701,7 @@ "type": "github" } ], - "time": "2026-03-17T14:58:32+00:00" + "time": "2026-03-24T20:40:50+00:00" }, { "name": "phpstan/phpstan-phpunit", diff --git a/tools/composer.lock b/tools/composer.lock index 8bd6f933f..b1d9689aa 100644 --- a/tools/composer.lock +++ b/tools/composer.lock @@ -3159,11 +3159,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.42", + "version": "2.1.43", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/1279e1ce86ba768f0780c9d889852b4e02ff40d0", - "reference": "1279e1ce86ba768f0780c9d889852b4e02ff40d0", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d01bebe3edfd4d49b9666ee5b8271ddca561042f", + "reference": "d01bebe3edfd4d49b9666ee5b8271ddca561042f", "shasum": "" }, "require": { @@ -3208,7 +3208,7 @@ "type": "github" } ], - "time": "2026-03-17T14:58:32+00:00" + "time": "2026-03-24T20:40:50+00:00" }, { "name": "psr/container", From af96a7377c9a7b601a9a8dd52cb5e07a95431485 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 01:20:13 +0000 Subject: [PATCH 48/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 8 ++++---- tools/composer.lock | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index bf7c8c480..3a959313b 100644 --- a/composer.lock +++ b/composer.lock @@ -4652,11 +4652,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.43", + "version": "2.1.44", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d01bebe3edfd4d49b9666ee5b8271ddca561042f", - "reference": "d01bebe3edfd4d49b9666ee5b8271ddca561042f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/4a88c083c668b2c364a425c9b3171b2d9ea5d218", + "reference": "4a88c083c668b2c364a425c9b3171b2d9ea5d218", "shasum": "" }, "require": { @@ -4701,7 +4701,7 @@ "type": "github" } ], - "time": "2026-03-24T20:40:50+00:00" + "time": "2026-03-25T17:34:21+00:00" }, { "name": "phpstan/phpstan-phpunit", diff --git a/tools/composer.lock b/tools/composer.lock index b1d9689aa..ab7ca303d 100644 --- a/tools/composer.lock +++ b/tools/composer.lock @@ -3159,11 +3159,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.43", + "version": "2.1.44", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d01bebe3edfd4d49b9666ee5b8271ddca561042f", - "reference": "d01bebe3edfd4d49b9666ee5b8271ddca561042f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/4a88c083c668b2c364a425c9b3171b2d9ea5d218", + "reference": "4a88c083c668b2c364a425c9b3171b2d9ea5d218", "shasum": "" }, "require": { @@ -3208,7 +3208,7 @@ "type": "github" } ], - "time": "2026-03-24T20:40:50+00:00" + "time": "2026-03-25T17:34:21+00:00" }, { "name": "psr/container", From 35c06a6a4bb09e490cf217386a287b90fa8b061b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 29 Mar 2026 17:25:19 +0000 Subject: [PATCH 49/59] Update Docs dependencies | datasource | package | from | to | | ---------- | ------------------ | ------ | ------- | | pypi | pygments | 2.19.2 | 2.20.0 | | pypi | pymdown-extensions | 10.21 | 10.21.2 | Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 78a702fd9..50d076b0a 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -4,8 +4,8 @@ markdown==3.10.2 mkdocs-material==9.7.6 # Markdown extensions -Pygments==2.19.2 -pymdown-extensions==10.21 +Pygments==2.20.0 +pymdown-extensions==10.21.2 # MkDocs plugins mkdocs-material-extensions==1.3.1 From 5186f6bdf8de5518e1c968790323cb9c4d8541c4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 01:30:23 +0000 Subject: [PATCH 50/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 3a959313b..d482dee2c 100644 --- a/composer.lock +++ b/composer.lock @@ -416,16 +416,16 @@ }, { "name": "patchlevel/hydrator", - "version": "1.18.0", + "version": "1.19.0", "source": { "type": "git", "url": "https://github.com/patchlevel/hydrator.git", - "reference": "fb802134da1eb6294a4358b1f55dbe3621104d80" + "reference": "5b79db6c2089cd3ddb5f08666179d85bda3160d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/patchlevel/hydrator/zipball/fb802134da1eb6294a4358b1f55dbe3621104d80", - "reference": "fb802134da1eb6294a4358b1f55dbe3621104d80", + "url": "https://api.github.com/repos/patchlevel/hydrator/zipball/5b79db6c2089cd3ddb5f08666179d85bda3160d7", + "reference": "5b79db6c2089cd3ddb5f08666179d85bda3160d7", "shasum": "" }, "require": { @@ -474,9 +474,9 @@ ], "support": { "issues": "https://github.com/patchlevel/hydrator/issues", - "source": "https://github.com/patchlevel/hydrator/tree/1.18.0" + "source": "https://github.com/patchlevel/hydrator/tree/1.19.0" }, - "time": "2026-03-13T17:09:15+00:00" + "time": "2026-03-29T12:04:47+00:00" }, { "name": "patchlevel/worker", From 3b992058437286bf3b3bcb7fbe30d796aab50d07 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 01:45:18 +0000 Subject: [PATCH 51/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 8 ++++---- tools/composer.lock | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/composer.lock b/composer.lock index d482dee2c..1d01e939a 100644 --- a/composer.lock +++ b/composer.lock @@ -4652,11 +4652,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.44", + "version": "2.1.45", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/4a88c083c668b2c364a425c9b3171b2d9ea5d218", - "reference": "4a88c083c668b2c364a425c9b3171b2d9ea5d218", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f8cdfd9421b7edb7686a2d150a234870464eac70", + "reference": "f8cdfd9421b7edb7686a2d150a234870464eac70", "shasum": "" }, "require": { @@ -4701,7 +4701,7 @@ "type": "github" } ], - "time": "2026-03-25T17:34:21+00:00" + "time": "2026-03-30T13:22:02+00:00" }, { "name": "phpstan/phpstan-phpunit", diff --git a/tools/composer.lock b/tools/composer.lock index ab7ca303d..0e4bc3ae9 100644 --- a/tools/composer.lock +++ b/tools/composer.lock @@ -75,16 +75,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.5.10", + "version": "1.5.11", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "961a5e4056dd2e4a2eedcac7576075947c28bf63" + "reference": "68ff39175e8e94a4bb1d259407ce51a6a60f09e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/961a5e4056dd2e4a2eedcac7576075947c28bf63", - "reference": "961a5e4056dd2e4a2eedcac7576075947c28bf63", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/68ff39175e8e94a4bb1d259407ce51a6a60f09e6", + "reference": "68ff39175e8e94a4bb1d259407ce51a6a60f09e6", "shasum": "" }, "require": { @@ -131,7 +131,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.5.10" + "source": "https://github.com/composer/ca-bundle/tree/1.5.11" }, "funding": [ { @@ -143,20 +143,20 @@ "type": "github" } ], - "time": "2025-12-08T15:06:51+00:00" + "time": "2026-03-30T09:16:10+00:00" }, { "name": "composer/class-map-generator", - "version": "1.7.1", + "version": "1.7.2", "source": { "type": "git", "url": "https://github.com/composer/class-map-generator.git", - "reference": "8f5fa3cc214230e71f54924bd0197a3bcc705eb1" + "reference": "6a9c2f0970022ab00dc58c07d0685dd712f2231b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/class-map-generator/zipball/8f5fa3cc214230e71f54924bd0197a3bcc705eb1", - "reference": "8f5fa3cc214230e71f54924bd0197a3bcc705eb1", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/6a9c2f0970022ab00dc58c07d0685dd712f2231b", + "reference": "6a9c2f0970022ab00dc58c07d0685dd712f2231b", "shasum": "" }, "require": { @@ -200,7 +200,7 @@ ], "support": { "issues": "https://github.com/composer/class-map-generator/issues", - "source": "https://github.com/composer/class-map-generator/tree/1.7.1" + "source": "https://github.com/composer/class-map-generator/tree/1.7.2" }, "funding": [ { @@ -212,7 +212,7 @@ "type": "github" } ], - "time": "2025-12-29T13:15:25+00:00" + "time": "2026-03-30T15:36:56+00:00" }, { "name": "composer/composer", @@ -3159,11 +3159,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.44", + "version": "2.1.45", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/4a88c083c668b2c364a425c9b3171b2d9ea5d218", - "reference": "4a88c083c668b2c364a425c9b3171b2d9ea5d218", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f8cdfd9421b7edb7686a2d150a234870464eac70", + "reference": "f8cdfd9421b7edb7686a2d150a234870464eac70", "shasum": "" }, "require": { @@ -3208,7 +3208,7 @@ "type": "github" } ], - "time": "2026-03-25T17:34:21+00:00" + "time": "2026-03-30T13:22:02+00:00" }, { "name": "psr/container", From 48ae396ff3a8602aa35c534f5353f2c6baa698e8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 01:09:15 +0000 Subject: [PATCH 52/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 144 ++++++++++++++++++++++---------------------- tools/composer.lock | 108 ++++++++++++++++----------------- 2 files changed, 126 insertions(+), 126 deletions(-) diff --git a/composer.lock b/composer.lock index 1d01e939a..5641691bc 100644 --- a/composer.lock +++ b/composer.lock @@ -995,16 +995,16 @@ }, { "name": "symfony/console", - "version": "v8.0.7", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "15ed9008a4ebe2d6a78e4937f74e0c13ef2e618a" + "reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/15ed9008a4ebe2d6a78e4937f74e0c13ef2e618a", - "reference": "15ed9008a4ebe2d6a78e4937f74e0c13ef2e618a", + "url": "https://api.github.com/repos/symfony/console/zipball/5b66d385dc58f69652e56f78a4184615e3f2b7f7", + "reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7", "shasum": "" }, "require": { @@ -1061,7 +1061,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v8.0.7" + "source": "https://github.com/symfony/console/tree/v8.0.8" }, "funding": [ { @@ -1081,7 +1081,7 @@ "type": "tidelift" } ], - "time": "2026-03-06T14:06:22+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1152,16 +1152,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v8.0.4", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "99301401da182b6cfaa4700dbe9987bb75474b47" + "reference": "f662acc6ab22a3d6d716dcb44c381c6002940df6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/99301401da182b6cfaa4700dbe9987bb75474b47", - "reference": "99301401da182b6cfaa4700dbe9987bb75474b47", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f662acc6ab22a3d6d716dcb44c381c6002940df6", + "reference": "f662acc6ab22a3d6d716dcb44c381c6002940df6", "shasum": "" }, "require": { @@ -1213,7 +1213,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.4" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.8" }, "funding": [ { @@ -1233,7 +1233,7 @@ "type": "tidelift" } ], - "time": "2026-01-05T11:45:55+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -1313,16 +1313,16 @@ }, { "name": "symfony/finder", - "version": "v8.0.6", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "441404f09a54de6d1bd6ad219e088cdf4c91f97c" + "reference": "8da41214757b87d97f181e3d14a4179286151007" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/441404f09a54de6d1bd6ad219e088cdf4c91f97c", - "reference": "441404f09a54de6d1bd6ad219e088cdf4c91f97c", + "url": "https://api.github.com/repos/symfony/finder/zipball/8da41214757b87d97f181e3d14a4179286151007", + "reference": "8da41214757b87d97f181e3d14a4179286151007", "shasum": "" }, "require": { @@ -1357,7 +1357,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v8.0.6" + "source": "https://github.com/symfony/finder/tree/v8.0.8" }, "funding": [ { @@ -1377,7 +1377,7 @@ "type": "tidelift" } ], - "time": "2026-01-29T09:41:02+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1803,16 +1803,16 @@ }, { "name": "symfony/stopwatch", - "version": "v8.0.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "67df1914c6ccd2d7b52f70d40cf2aea02159d942" + "reference": "85954ed72d5440ea4dc9a10b7e49e01df766ffa3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/67df1914c6ccd2d7b52f70d40cf2aea02159d942", - "reference": "67df1914c6ccd2d7b52f70d40cf2aea02159d942", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/85954ed72d5440ea4dc9a10b7e49e01df766ffa3", + "reference": "85954ed72d5440ea4dc9a10b7e49e01df766ffa3", "shasum": "" }, "require": { @@ -1845,7 +1845,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v8.0.0" + "source": "https://github.com/symfony/stopwatch/tree/v8.0.8" }, "funding": [ { @@ -1865,20 +1865,20 @@ "type": "tidelift" } ], - "time": "2025-08-04T07:36:47+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/string", - "version": "v8.0.6", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4" + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/6c9e1108041b5dce21a9a4984b531c4923aa9ec4", - "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4", + "url": "https://api.github.com/repos/symfony/string/zipball/ae9488f874d7603f9d2dfbf120203882b645d963", + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963", "shasum": "" }, "require": { @@ -1935,7 +1935,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.6" + "source": "https://github.com/symfony/string/tree/v8.0.8" }, "funding": [ { @@ -1955,20 +1955,20 @@ "type": "tidelift" } ], - "time": "2026-02-09T10:14:57+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/type-info", - "version": "v8.0.7", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/type-info.git", - "reference": "3c7de103dd6cb68be24e155838a64ef4a70ae195" + "reference": "622d81551770029d44d16be68969712eb47892f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/3c7de103dd6cb68be24e155838a64ef4a70ae195", - "reference": "3c7de103dd6cb68be24e155838a64ef4a70ae195", + "url": "https://api.github.com/repos/symfony/type-info/zipball/622d81551770029d44d16be68969712eb47892f1", + "reference": "622d81551770029d44d16be68969712eb47892f1", "shasum": "" }, "require": { @@ -2017,7 +2017,7 @@ "type" ], "support": { - "source": "https://github.com/symfony/type-info/tree/v8.0.7" + "source": "https://github.com/symfony/type-info/tree/v8.0.8" }, "funding": [ { @@ -2037,20 +2037,20 @@ "type": "tidelift" } ], - "time": "2026-03-04T13:55:34+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/var-exporter", - "version": "v8.0.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04" + "reference": "15776bb07a91b089037da89f8832fa41d5fa6ec6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", - "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/15776bb07a91b089037da89f8832fa41d5fa6ec6", + "reference": "15776bb07a91b089037da89f8832fa41d5fa6ec6", "shasum": "" }, "require": { @@ -2097,7 +2097,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v8.0.0" + "source": "https://github.com/symfony/var-exporter/tree/v8.0.8" }, "funding": [ { @@ -2117,7 +2117,7 @@ "type": "tidelift" } ], - "time": "2025-11-05T18:53:00+00:00" + "time": "2026-03-30T15:14:47+00:00" } ], "packages-dev": [ @@ -6738,16 +6738,16 @@ }, { "name": "symfony/clock", - "version": "v8.0.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "832119f9b8dbc6c8e6f65f30c5969eca1e88764f" + "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/832119f9b8dbc6c8e6f65f30c5969eca1e88764f", - "reference": "832119f9b8dbc6c8e6f65f30c5969eca1e88764f", + "url": "https://api.github.com/repos/symfony/clock/zipball/b55a638b189a6faa875e0ccdb00908fb87af95b3", + "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3", "shasum": "" }, "require": { @@ -6791,7 +6791,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v8.0.0" + "source": "https://github.com/symfony/clock/tree/v8.0.8" }, "funding": [ { @@ -6811,7 +6811,7 @@ "type": "tidelift" } ], - "time": "2025-11-12T15:46:48+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/filesystem", @@ -6885,16 +6885,16 @@ }, { "name": "symfony/messenger", - "version": "v8.0.7", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "6ba5f08c156cfc95911dbb0da01a9bc390a70fd1" + "reference": "8d0e6b2d5e5dc9d484c6e45117395ae98f0a497a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/6ba5f08c156cfc95911dbb0da01a9bc390a70fd1", - "reference": "6ba5f08c156cfc95911dbb0da01a9bc390a70fd1", + "url": "https://api.github.com/repos/symfony/messenger/zipball/8d0e6b2d5e5dc9d484c6e45117395ae98f0a497a", + "reference": "8d0e6b2d5e5dc9d484c6e45117395ae98f0a497a", "shasum": "" }, "require": { @@ -6951,7 +6951,7 @@ "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/messenger/tree/v8.0.7" + "source": "https://github.com/symfony/messenger/tree/v8.0.8" }, "funding": [ { @@ -6971,20 +6971,20 @@ "type": "tidelift" } ], - "time": "2026-03-04T13:55:34+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/options-resolver", - "version": "v8.0.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7" + "reference": "b48bce0a70b914f6953dafbd10474df232ed4de8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/d2b592535ffa6600c265a3893a7f7fd2bad82dd7", - "reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b48bce0a70b914f6953dafbd10474df232ed4de8", + "reference": "b48bce0a70b914f6953dafbd10474df232ed4de8", "shasum": "" }, "require": { @@ -7022,7 +7022,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v8.0.0" + "source": "https://github.com/symfony/options-resolver/tree/v8.0.8" }, "funding": [ { @@ -7042,7 +7042,7 @@ "type": "tidelift" } ], - "time": "2025-11-12T15:55:31+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/polyfill-php80", @@ -7290,16 +7290,16 @@ }, { "name": "symfony/process", - "version": "v8.0.5", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674" + "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674", - "reference": "b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674", + "url": "https://api.github.com/repos/symfony/process/zipball/cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", + "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", "shasum": "" }, "require": { @@ -7331,7 +7331,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v8.0.5" + "source": "https://github.com/symfony/process/tree/v8.0.8" }, "funding": [ { @@ -7351,20 +7351,20 @@ "type": "tidelift" } ], - "time": "2026-01-26T15:08:38+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/var-dumper", - "version": "v8.0.6", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "2e14f7e0bf5ff02c6e63bd31cb8e4855a13d6209" + "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2e14f7e0bf5ff02c6e63bd31cb8e4855a13d6209", - "reference": "2e14f7e0bf5ff02c6e63bd31cb8e4855a13d6209", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfb7badd53bf4177f6e9416cfbbccc13c0e773a1", + "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1", "shasum": "" }, "require": { @@ -7418,7 +7418,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v8.0.6" + "source": "https://github.com/symfony/var-dumper/tree/v8.0.8" }, "funding": [ { @@ -7438,7 +7438,7 @@ "type": "tidelift" } ], - "time": "2026-02-15T10:53:29+00:00" + "time": "2026-03-31T07:15:36+00:00" }, { "name": "thecodingmachine/safe", diff --git a/tools/composer.lock b/tools/composer.lock index 0e4bc3ae9..efc52883d 100644 --- a/tools/composer.lock +++ b/tools/composer.lock @@ -3840,16 +3840,16 @@ }, { "name": "symfony/config", - "version": "v8.0.7", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "9a34c52187112503d02903ab35e6e3783f580c29" + "reference": "c7369cc1da250fcbfe0c5a9d109e419661549c39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/9a34c52187112503d02903ab35e6e3783f580c29", - "reference": "9a34c52187112503d02903ab35e6e3783f580c29", + "url": "https://api.github.com/repos/symfony/config/zipball/c7369cc1da250fcbfe0c5a9d109e419661549c39", + "reference": "c7369cc1da250fcbfe0c5a9d109e419661549c39", "shasum": "" }, "require": { @@ -3894,7 +3894,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v8.0.7" + "source": "https://github.com/symfony/config/tree/v8.0.8" }, "funding": [ { @@ -3914,20 +3914,20 @@ "type": "tidelift" } ], - "time": "2026-03-06T13:17:40+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/console", - "version": "v7.4.7", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d" + "reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/e1e6770440fb9c9b0cf725f81d1361ad1835329d", - "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d", + "url": "https://api.github.com/repos/symfony/console/zipball/1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707", + "reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707", "shasum": "" }, "require": { @@ -3992,7 +3992,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.7" + "source": "https://github.com/symfony/console/tree/v7.4.8" }, "funding": [ { @@ -4012,20 +4012,20 @@ "type": "tidelift" } ], - "time": "2026-03-06T14:06:20+00:00" + "time": "2026-03-30T13:54:39+00:00" }, { "name": "symfony/dependency-injection", - "version": "v8.0.7", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "1faaac6dbfe069a92ab9e5c270fa43fc4e1761da" + "reference": "3ce58b0fa844dc647ca1d66ea34748af985728c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/1faaac6dbfe069a92ab9e5c270fa43fc4e1761da", - "reference": "1faaac6dbfe069a92ab9e5c270fa43fc4e1761da", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/3ce58b0fa844dc647ca1d66ea34748af985728c5", + "reference": "3ce58b0fa844dc647ca1d66ea34748af985728c5", "shasum": "" }, "require": { @@ -4073,7 +4073,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v8.0.7" + "source": "https://github.com/symfony/dependency-injection/tree/v8.0.8" }, "funding": [ { @@ -4093,7 +4093,7 @@ "type": "tidelift" } ], - "time": "2026-03-03T07:49:33+00:00" + "time": "2026-03-31T07:15:36+00:00" }, { "name": "symfony/deprecation-contracts", @@ -4164,16 +4164,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v8.0.4", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "99301401da182b6cfaa4700dbe9987bb75474b47" + "reference": "f662acc6ab22a3d6d716dcb44c381c6002940df6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/99301401da182b6cfaa4700dbe9987bb75474b47", - "reference": "99301401da182b6cfaa4700dbe9987bb75474b47", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f662acc6ab22a3d6d716dcb44c381c6002940df6", + "reference": "f662acc6ab22a3d6d716dcb44c381c6002940df6", "shasum": "" }, "require": { @@ -4225,7 +4225,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.4" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.8" }, "funding": [ { @@ -4245,7 +4245,7 @@ "type": "tidelift" } ], - "time": "2026-01-05T11:45:55+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -4395,16 +4395,16 @@ }, { "name": "symfony/finder", - "version": "v8.0.6", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "441404f09a54de6d1bd6ad219e088cdf4c91f97c" + "reference": "8da41214757b87d97f181e3d14a4179286151007" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/441404f09a54de6d1bd6ad219e088cdf4c91f97c", - "reference": "441404f09a54de6d1bd6ad219e088cdf4c91f97c", + "url": "https://api.github.com/repos/symfony/finder/zipball/8da41214757b87d97f181e3d14a4179286151007", + "reference": "8da41214757b87d97f181e3d14a4179286151007", "shasum": "" }, "require": { @@ -4439,7 +4439,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v8.0.6" + "source": "https://github.com/symfony/finder/tree/v8.0.8" }, "funding": [ { @@ -4459,7 +4459,7 @@ "type": "tidelift" } ], - "time": "2026-01-29T09:41:02+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5122,16 +5122,16 @@ }, { "name": "symfony/process", - "version": "v8.0.5", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674" + "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674", - "reference": "b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674", + "url": "https://api.github.com/repos/symfony/process/zipball/cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", + "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", "shasum": "" }, "require": { @@ -5163,7 +5163,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v8.0.5" + "source": "https://github.com/symfony/process/tree/v8.0.8" }, "funding": [ { @@ -5183,7 +5183,7 @@ "type": "tidelift" } ], - "time": "2026-01-26T15:08:38+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/service-contracts", @@ -5274,16 +5274,16 @@ }, { "name": "symfony/string", - "version": "v8.0.6", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4" + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/6c9e1108041b5dce21a9a4984b531c4923aa9ec4", - "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4", + "url": "https://api.github.com/repos/symfony/string/zipball/ae9488f874d7603f9d2dfbf120203882b645d963", + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963", "shasum": "" }, "require": { @@ -5340,7 +5340,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.6" + "source": "https://github.com/symfony/string/tree/v8.0.8" }, "funding": [ { @@ -5360,20 +5360,20 @@ "type": "tidelift" } ], - "time": "2026-02-09T10:14:57+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/var-exporter", - "version": "v8.0.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04" + "reference": "15776bb07a91b089037da89f8832fa41d5fa6ec6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", - "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/15776bb07a91b089037da89f8832fa41d5fa6ec6", + "reference": "15776bb07a91b089037da89f8832fa41d5fa6ec6", "shasum": "" }, "require": { @@ -5420,7 +5420,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v8.0.0" + "source": "https://github.com/symfony/var-exporter/tree/v8.0.8" }, "funding": [ { @@ -5440,20 +5440,20 @@ "type": "tidelift" } ], - "time": "2025-11-05T18:53:00+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/yaml", - "version": "v8.0.6", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "5f006c50a981e1630bbb70ad409c5d85f9a716e0" + "reference": "54174ab48c0c0f9e21512b304be17f8150ccf8f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/5f006c50a981e1630bbb70ad409c5d85f9a716e0", - "reference": "5f006c50a981e1630bbb70ad409c5d85f9a716e0", + "url": "https://api.github.com/repos/symfony/yaml/zipball/54174ab48c0c0f9e21512b304be17f8150ccf8f1", + "reference": "54174ab48c0c0f9e21512b304be17f8150ccf8f1", "shasum": "" }, "require": { @@ -5495,7 +5495,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v8.0.6" + "source": "https://github.com/symfony/yaml/tree/v8.0.8" }, "funding": [ { @@ -5515,7 +5515,7 @@ "type": "tidelift" } ], - "time": "2026-02-09T10:14:57+00:00" + "time": "2026-03-30T15:14:47+00:00" } ], "packages-dev": [], From 23e7e045f8a6d082361c4f7bcc731f3ed65a0ef7 Mon Sep 17 00:00:00 2001 From: David Badura Date: Wed, 1 Apr 2026 15:13:09 +0200 Subject: [PATCH 53/59] dbal cleanup handler: check if a table or index exists before deleting it --- .../Cleanup/Dbal/DbalCleanupTaskHandler.php | 20 +++++- .../Dbal/DbalCleanupTaskHandlerTest.php | 66 +++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/Subscription/Cleanup/Dbal/DbalCleanupTaskHandler.php b/src/Subscription/Cleanup/Dbal/DbalCleanupTaskHandler.php index 110268b56..6fe084a9a 100644 --- a/src/Subscription/Cleanup/Dbal/DbalCleanupTaskHandler.php +++ b/src/Subscription/Cleanup/Dbal/DbalCleanupTaskHandler.php @@ -9,6 +9,8 @@ use Patchlevel\EventSourcing\Subscription\Cleanup\CleanupTaskHandler; use Patchlevel\EventSourcing\Subscription\Cleanup\CleanupTaskNotSupported; +use function strtolower; + final class DbalCleanupTaskHandler implements CleanupTaskHandler { public function __construct( @@ -19,13 +21,27 @@ public function __construct( public function __invoke(object $task): void { if ($task instanceof DropTableTask) { - $this->connection($task->connectionName)->createSchemaManager()->dropTable($task->table); + $schemaManager = $this->connection($task->connectionName)->createSchemaManager(); + if ($schemaManager->tablesExist([$task->table])) { + $schemaManager->dropTable($task->table); + } return; } if ($task instanceof DropIndexTask) { - $this->connection($task->connectionName)->createSchemaManager()->dropIndex($task->index, $task->table); + $schemaManager = $this->connection($task->connectionName)->createSchemaManager(); + + if (!$schemaManager->tablesExist([$task->table])) { + return; + } + + foreach ($schemaManager->introspectTableIndexesByUnquotedName($task->table) as $index) { + if (strtolower($index->getObjectName()->toString()) === strtolower($task->index)) { + $schemaManager->dropIndex($task->index, $task->table); + break; + } + } return; } diff --git a/tests/Unit/Subscription/Cleanup/Dbal/DbalCleanupTaskHandlerTest.php b/tests/Unit/Subscription/Cleanup/Dbal/DbalCleanupTaskHandlerTest.php index 04a6252dd..373cb25fa 100644 --- a/tests/Unit/Subscription/Cleanup/Dbal/DbalCleanupTaskHandlerTest.php +++ b/tests/Unit/Subscription/Cleanup/Dbal/DbalCleanupTaskHandlerTest.php @@ -6,6 +6,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Schema\AbstractSchemaManager; +use Doctrine\DBAL\Schema\Index; use Doctrine\Persistence\ConnectionRegistry; use Patchlevel\EventSourcing\Subscription\Cleanup\CleanupTaskNotSupported; use Patchlevel\EventSourcing\Subscription\Cleanup\Dbal\ConnectionNameNotSupported; @@ -53,6 +54,7 @@ public function testHandleNoSupportedTask(): void public function testHandleDropTable(): void { $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager->expects($this->once())->method('tablesExist')->with(['test'])->willReturn(true); $schemaManager->expects($this->once())->method('dropTable')->with('test'); $connection = $this->createMock(Connection::class); @@ -66,9 +68,32 @@ public function testHandleDropTable(): void $handler(new DropTableTask('test')); } + public function testHandleDropTableIfNotExists(): void + { + $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager->expects($this->once())->method('tablesExist')->with(['test'])->willReturn(false); + $schemaManager->expects($this->never())->method('dropTable'); + + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->once()) + ->method('createSchemaManager') + ->willReturn($schemaManager); + + $handler = new DbalCleanupTaskHandler($connection); + + $handler(new DropTableTask('test')); + } + public function testHandleDropIndex(): void { $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager->expects($this->once())->method('tablesExist')->with(['bar'])->willReturn(true); + $schemaManager + ->expects($this->once()) + ->method('introspectTableIndexesByUnquotedName') + ->with('bar') + ->willReturn([new Index('foo', ['id'])]); $schemaManager->expects($this->once())->method('dropIndex')->with('foo', 'bar'); $connection = $this->createMock(Connection::class); @@ -82,9 +107,50 @@ public function testHandleDropIndex(): void $handler(new DropIndexTask('foo', 'bar')); } + public function testHandleDropIndexIfTableNotExists(): void + { + $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager->expects($this->once())->method('tablesExist')->with(['bar'])->willReturn(false); + $schemaManager->expects($this->never())->method('introspectTableIndexesByUnquotedName'); + $schemaManager->expects($this->never())->method('dropIndex'); + + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->once()) + ->method('createSchemaManager') + ->willReturn($schemaManager); + + $handler = new DbalCleanupTaskHandler($connection); + + $handler(new DropIndexTask('foo', 'bar')); + } + + public function testHandleDropIndexIfIndexNotExists(): void + { + $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager->expects($this->once())->method('tablesExist')->with(['bar'])->willReturn(true); + $schemaManager + ->expects($this->once()) + ->method('introspectTableIndexesByUnquotedName') + ->with('bar') + ->willReturn([new Index('baz', ['id'])]); + $schemaManager->expects($this->never())->method('dropIndex'); + + $connection = $this->createMock(Connection::class); + $connection + ->expects($this->once()) + ->method('createSchemaManager') + ->willReturn($schemaManager); + + $handler = new DbalCleanupTaskHandler($connection); + + $handler(new DropIndexTask('foo', 'bar')); + } + public function testHandleWithConnectionRegistry(): void { $schemaManager = $this->createMock(AbstractSchemaManager::class); + $schemaManager->expects($this->once())->method('tablesExist')->with(['test'])->willReturn(true); $schemaManager->expects($this->once())->method('dropTable')->with('test'); $connection = $this->createMock(Connection::class); From d718214de14f992b53e5ae1dc6b5f21b371ce84e Mon Sep 17 00:00:00 2001 From: David Badura Date: Wed, 1 Apr 2026 15:18:54 +0200 Subject: [PATCH 54/59] bump dbal dependencies --- composer.json | 2 +- composer.lock | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.json b/composer.json index cfb4e3237..2aeb68832 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ ], "require": { "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", - "doctrine/dbal": "^4.0.0", + "doctrine/dbal": "^4.4.0", "doctrine/migrations": "^3.3.2", "patchlevel/hydrator": "^1.8.0", "patchlevel/worker": "^1.4.0", diff --git a/composer.lock b/composer.lock index 5641691bc..ebf28ee5e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "eaaa35929b4073dbe5946b8486ca1a64", + "content-hash": "040555186133771a1ea827cd0aade8d4", "packages": [ { "name": "brick/math", @@ -4652,11 +4652,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.45", + "version": "2.1.46", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f8cdfd9421b7edb7686a2d150a234870464eac70", - "reference": "f8cdfd9421b7edb7686a2d150a234870464eac70", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a193923fc2d6325ef4e741cf3af8c3e8f54dbf25", + "reference": "a193923fc2d6325ef4e741cf3af8c3e8f54dbf25", "shasum": "" }, "require": { @@ -4701,7 +4701,7 @@ "type": "github" } ], - "time": "2026-03-30T13:22:02+00:00" + "time": "2026-04-01T09:25:14+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -6815,16 +6815,16 @@ }, { "name": "symfony/filesystem", - "version": "v8.0.6", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "7bf9162d7a0dff98d079b72948508fa48018a770" + "reference": "66b769ae743ce2d13e435528fbef4af03d623e5a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/7bf9162d7a0dff98d079b72948508fa48018a770", - "reference": "7bf9162d7a0dff98d079b72948508fa48018a770", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/66b769ae743ce2d13e435528fbef4af03d623e5a", + "reference": "66b769ae743ce2d13e435528fbef4af03d623e5a", "shasum": "" }, "require": { @@ -6861,7 +6861,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v8.0.6" + "source": "https://github.com/symfony/filesystem/tree/v8.0.8" }, "funding": [ { @@ -6881,7 +6881,7 @@ "type": "tidelift" } ], - "time": "2026-02-25T16:59:43+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/messenger", From 198d57271cac1164aed30055d02aa9df0dbfed8d Mon Sep 17 00:00:00 2001 From: David Badura Date: Wed, 1 Apr 2026 15:20:30 +0200 Subject: [PATCH 55/59] add phpstan error in baseline --- phpstan-baseline.neon | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index f7e40e355..515515788 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -126,6 +126,12 @@ parameters: count: 1 path: src/Store/StreamDoctrineDbalStoreStream.php + - + message: '#^Parameter \#1 \$tableName of method Doctrine\\DBAL\\Schema\\AbstractSchemaManager\\:\:introspectTableIndexesByUnquotedName\(\) expects non\-empty\-string, string given\.$#' + identifier: argument.type + count: 1 + path: src/Subscription/Cleanup/Dbal/DbalCleanupTaskHandler.php + - message: '#^Generator expects key type int, int\<1, max\>\|null given\.$#' identifier: generator.keyType @@ -336,18 +342,6 @@ parameters: count: 1 path: tests/Unit/Aggregate/AggregateRootTest.php - - - message: '#^Cannot access offset 0 on iterable\\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 - path: tests/Unit/CommandBus/AggregateHandlerProviderTest.php - - - - message: '#^Cannot call method callable\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: tests/Unit/CommandBus/AggregateHandlerProviderTest.php - - message: '#^Parameter \#2 \$aggregateClass of class Patchlevel\\EventSourcing\\CommandBus\\Handler\\CreateAggregateHandler constructor expects class\-string\, string given\.$#' identifier: argument.type @@ -366,18 +360,6 @@ parameters: count: 5 path: tests/Unit/CommandBus/InstantRetryCommandBusTest.php - - - message: '#^Cannot access offset 0 on iterable\\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 - path: tests/Unit/CommandBus/ServiceHandlerProviderTest.php - - - - message: '#^Cannot call method callable\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: tests/Unit/CommandBus/ServiceHandlerProviderTest.php - - message: '#^Parameter \#1 \$data of static method Patchlevel\\EventSourcing\\Tests\\Unit\\Fixture\\Message\:\:fromArray\(\) expects array\{id\: string, text\: string, createdAt\: string\}, array\ given\.$#' identifier: argument.type From 8c75ca8e6dff5441d2f32abc5d1f7632e9a08ca2 Mon Sep 17 00:00:00 2001 From: David Badura Date: Wed, 1 Apr 2026 15:29:29 +0200 Subject: [PATCH 56/59] improve task types --- phpstan-baseline.neon | 12 ++++++------ src/Subscription/Cleanup/Dbal/DropIndexTask.php | 5 +++++ src/Subscription/Cleanup/Dbal/DropTableTask.php | 4 ++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 515515788..b84c95a16 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -126,12 +126,6 @@ parameters: count: 1 path: src/Store/StreamDoctrineDbalStoreStream.php - - - message: '#^Parameter \#1 \$tableName of method Doctrine\\DBAL\\Schema\\AbstractSchemaManager\\:\:introspectTableIndexesByUnquotedName\(\) expects non\-empty\-string, string given\.$#' - identifier: argument.type - count: 1 - path: src/Subscription/Cleanup/Dbal/DbalCleanupTaskHandler.php - - message: '#^Generator expects key type int, int\<1, max\>\|null given\.$#' identifier: generator.keyType @@ -318,6 +312,12 @@ parameters: count: 1 path: tests/Integration/Store/StreamDoctrineDbalStoreTest.php + - + message: '#^Parameter \#1 \$table of class Patchlevel\\EventSourcing\\Subscription\\Cleanup\\Dbal\\DropTableTask constructor expects non\-empty\-string, string given\.$#' + identifier: argument.type + count: 1 + path: tests/Integration/Subscription/Subscriber/ProfileProjectionWithCleanup.php + - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertArrayHasKey\(\) with 0 and array\{Patchlevel\\EventSourcing\\Subscription\\Subscription\} will always evaluate to true\.$#' identifier: staticMethod.alreadyNarrowedType diff --git a/src/Subscription/Cleanup/Dbal/DropIndexTask.php b/src/Subscription/Cleanup/Dbal/DropIndexTask.php index 6dcad5066..5e657d266 100644 --- a/src/Subscription/Cleanup/Dbal/DropIndexTask.php +++ b/src/Subscription/Cleanup/Dbal/DropIndexTask.php @@ -6,6 +6,11 @@ final class DropIndexTask { + /** + * @param non-empty-string $index + * @param non-empty-string $table + * @param non-empty-string|null $connectionName + */ public function __construct( public readonly string $index, public readonly string $table, diff --git a/src/Subscription/Cleanup/Dbal/DropTableTask.php b/src/Subscription/Cleanup/Dbal/DropTableTask.php index 73a363da5..62d5231c2 100644 --- a/src/Subscription/Cleanup/Dbal/DropTableTask.php +++ b/src/Subscription/Cleanup/Dbal/DropTableTask.php @@ -6,6 +6,10 @@ final class DropTableTask { + /** + * @param non-empty-string $table + * @param non-empty-string|null $connectionName + */ public function __construct( public readonly string $table, public readonly string|null $connectionName = null, From 29676803176169e2d3acac813345b635c7c6d872 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 01:48:49 +0000 Subject: [PATCH 57/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- tools/composer.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tools/composer.lock b/tools/composer.lock index efc52883d..aae982be8 100644 --- a/tools/composer.lock +++ b/tools/composer.lock @@ -3159,11 +3159,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.45", + "version": "2.1.46", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f8cdfd9421b7edb7686a2d150a234870464eac70", - "reference": "f8cdfd9421b7edb7686a2d150a234870464eac70", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a193923fc2d6325ef4e741cf3af8c3e8f54dbf25", + "reference": "a193923fc2d6325ef4e741cf3af8c3e8f54dbf25", "shasum": "" }, "require": { @@ -3208,7 +3208,7 @@ "type": "github" } ], - "time": "2026-03-30T13:22:02+00:00" + "time": "2026-04-01T09:25:14+00:00" }, { "name": "psr/container", @@ -4325,16 +4325,16 @@ }, { "name": "symfony/filesystem", - "version": "v8.0.6", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "7bf9162d7a0dff98d079b72948508fa48018a770" + "reference": "66b769ae743ce2d13e435528fbef4af03d623e5a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/7bf9162d7a0dff98d079b72948508fa48018a770", - "reference": "7bf9162d7a0dff98d079b72948508fa48018a770", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/66b769ae743ce2d13e435528fbef4af03d623e5a", + "reference": "66b769ae743ce2d13e435528fbef4af03d623e5a", "shasum": "" }, "require": { @@ -4371,7 +4371,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v8.0.6" + "source": "https://github.com/symfony/filesystem/tree/v8.0.8" }, "funding": [ { @@ -4391,7 +4391,7 @@ "type": "tidelift" } ], - "time": "2026-02-25T16:59:43+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/finder", From ab29c2534462ea587ae43a181f14f52c6da00051 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:33:19 +0000 Subject: [PATCH 58/59] Lock file maintenance Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- composer.lock | 24 ++++++++++++------------ tools/composer.lock | 12 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/composer.lock b/composer.lock index ebf28ee5e..9f80463f2 100644 --- a/composer.lock +++ b/composer.lock @@ -2926,16 +2926,16 @@ }, { "name": "doctrine/orm", - "version": "3.6.2", + "version": "3.6.3", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "4262eb495b4d2a53b45de1ac58881e0091f2970f" + "reference": "e88cd591f0786089dee22b972c28aa2076df51c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/4262eb495b4d2a53b45de1ac58881e0091f2970f", - "reference": "4262eb495b4d2a53b45de1ac58881e0091f2970f", + "url": "https://api.github.com/repos/doctrine/orm/zipball/e88cd591f0786089dee22b972c28aa2076df51c0", + "reference": "e88cd591f0786089dee22b972c28aa2076df51c0", "shasum": "" }, "require": { @@ -3008,9 +3008,9 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/3.6.2" + "source": "https://github.com/doctrine/orm/tree/3.6.3" }, - "time": "2026-01-30T21:41:41+00:00" + "time": "2026-04-02T06:53:27+00:00" }, { "name": "doctrine/persistence", @@ -3534,16 +3534,16 @@ }, { "name": "justinrainbow/json-schema", - "version": "v6.7.2", + "version": "6.8.0", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "6fea66c7204683af437864e7c4e7abf383d14bc0" + "reference": "89ac92bcfe5d0a8a4433c7b89d394553ae7250cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/6fea66c7204683af437864e7c4e7abf383d14bc0", - "reference": "6fea66c7204683af437864e7c4e7abf383d14bc0", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/89ac92bcfe5d0a8a4433c7b89d394553ae7250cc", + "reference": "89ac92bcfe5d0a8a4433c7b89d394553ae7250cc", "shasum": "" }, "require": { @@ -3603,9 +3603,9 @@ ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/v6.7.2" + "source": "https://github.com/jsonrainbow/json-schema/tree/6.8.0" }, - "time": "2026-02-15T15:06:22+00:00" + "time": "2026-04-02T12:43:11+00:00" }, { "name": "league/commonmark", diff --git a/tools/composer.lock b/tools/composer.lock index aae982be8..987d078b0 100644 --- a/tools/composer.lock +++ b/tools/composer.lock @@ -877,16 +877,16 @@ }, { "name": "justinrainbow/json-schema", - "version": "v6.7.2", + "version": "6.8.0", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "6fea66c7204683af437864e7c4e7abf383d14bc0" + "reference": "89ac92bcfe5d0a8a4433c7b89d394553ae7250cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/6fea66c7204683af437864e7c4e7abf383d14bc0", - "reference": "6fea66c7204683af437864e7c4e7abf383d14bc0", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/89ac92bcfe5d0a8a4433c7b89d394553ae7250cc", + "reference": "89ac92bcfe5d0a8a4433c7b89d394553ae7250cc", "shasum": "" }, "require": { @@ -946,9 +946,9 @@ ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/v6.7.2" + "source": "https://github.com/jsonrainbow/json-schema/tree/6.8.0" }, - "time": "2026-02-15T15:06:22+00:00" + "time": "2026-04-02T12:43:11+00:00" }, { "name": "marc-mabe/php-enum", From a4aad60c6a7428441329f0671c7744efc51f94ac Mon Sep 17 00:00:00 2001 From: David Badura Date: Fri, 3 Apr 2026 18:46:16 +0200 Subject: [PATCH 59/59] add doctrine cipher key store for hydrator extension --- phpstan-baseline.neon | 42 ++++- .../ExtensionDoctrineCipherKeyStore.php | 127 ++++++++++++++ .../PersonalData/Events/NameChanged.php | 6 +- .../PersonalData/Events/ProfileCreated.php | 6 +- .../PersonalData/PersonalDataTest.php | 164 ++++++++++++++++++ 5 files changed, 340 insertions(+), 5 deletions(-) create mode 100644 src/Cryptography/ExtensionDoctrineCipherKeyStore.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index b84c95a16..8f4bd55e6 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -18,6 +18,42 @@ parameters: count: 1 path: src/Cryptography/DoctrineCipherKeyStore.php + - + message: '#^Offset ''crypto_iv'' does not exist on array\{id\: non\-empty\-string, subject_id\: non\-empty\-string, crypto_key\: non\-empty\-string, crypto_method\: non\-empty\-string, created_at\: non\-empty\-string\}\.$#' + identifier: offsetAccess.notFound + count: 1 + path: src/Cryptography/ExtensionDoctrineCipherKeyStore.php + + - + message: '#^Parameter \#1 \$string of function base64_decode expects string, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Cryptography/ExtensionDoctrineCipherKeyStore.php + + - + message: '#^Parameter \#2 \$subjectId of class Patchlevel\\Hydrator\\Extension\\Cryptography\\Cipher\\CipherKey constructor expects non\-empty\-string, string given\.$#' + identifier: argument.type + count: 1 + path: src/Cryptography/ExtensionDoctrineCipherKeyStore.php + + - + message: '#^Parameter \#3 \$key of class Patchlevel\\Hydrator\\Extension\\Cryptography\\Cipher\\CipherKey constructor expects non\-empty\-string, string given\.$#' + identifier: argument.type + count: 1 + path: src/Cryptography/ExtensionDoctrineCipherKeyStore.php + + - + message: '#^Parameter \#4 \$method of class Patchlevel\\Hydrator\\Extension\\Cryptography\\Cipher\\CipherKey constructor expects non\-empty\-string, string given\.$#' + identifier: argument.type + count: 1 + path: src/Cryptography/ExtensionDoctrineCipherKeyStore.php + + - + message: '#^Parameter \#5 \$createdAt of class Patchlevel\\Hydrator\\Extension\\Cryptography\\Cipher\\CipherKey constructor expects DateTimeImmutable, mixed given\.$#' + identifier: argument.type + count: 2 + path: src/Cryptography/ExtensionDoctrineCipherKeyStore.php + - message: '#^Call to function method_exists\(\) with ReflectionFunction and ''isAnonymous'' will always evaluate to true\.$#' identifier: function.alreadyNarrowedType @@ -285,19 +321,19 @@ parameters: - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertArrayHasKey\(\) with 0 and array\{array\\} will always evaluate to true\.$#' identifier: staticMethod.alreadyNarrowedType - count: 1 + count: 4 path: tests/Integration/PersonalData/PersonalDataTest.php - message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Patchlevel\\\\EventSourcing\\\\Tests\\\\Integration\\\\PersonalData\\\\Profile'' and Patchlevel\\EventSourcing\\Tests\\Integration\\PersonalData\\Profile will always evaluate to true\.$#' identifier: staticMethod.alreadyNarrowedType - count: 6 + count: 8 path: tests/Integration/PersonalData/PersonalDataTest.php - message: '#^Parameter \#2 \$haystack of static method PHPUnit\\Framework\\Assert\:\:assertStringNotContainsString\(\) expects string, mixed given\.$#' identifier: argument.type - count: 1 + count: 3 path: tests/Integration/PersonalData/PersonalDataTest.php - diff --git a/src/Cryptography/ExtensionDoctrineCipherKeyStore.php b/src/Cryptography/ExtensionDoctrineCipherKeyStore.php new file mode 100644 index 000000000..8bb321319 --- /dev/null +++ b/src/Cryptography/ExtensionDoctrineCipherKeyStore.php @@ -0,0 +1,127 @@ +dateTimeType = Type::getType(Types::DATETIMETZ_IMMUTABLE); + } + + public function get(string $id): CipherKey + { + /** @var Row|false $result */ + $result = $this->connection->fetchAssociative( + "SELECT * FROM {$this->tableName} WHERE id = :id", + ['id' => $id], + ); + + if ($result === false) { + throw CipherKeyNotExists::forKeyId($id); + } + + return new CipherKey( + $result['id'], + $result['subject_id'], + base64_decode($result['crypto_key']), + $result['crypto_method'], + $this->dateTimeType->convertToPHPValue($result['created_at'], $this->connection->getDatabasePlatform()), + ); + } + + public function currentKeyFor(string $subjectId): CipherKey + { + /** @var Row|false $result */ + $result = $this->connection->fetchAssociative( + "SELECT * FROM {$this->tableName} WHERE subject_id = :subject_id", + ['subject_id' => $subjectId], + ); + + if ($result === false) { + throw CipherKeyNotExists::forSubjectId($subjectId); + } + + return new CipherKey( + $result['id'], + base64_decode($result['crypto_key']), + $result['crypto_method'], + base64_decode($result['crypto_iv']), + $this->dateTimeType->convertToPHPValue($result['created_at'], $this->connection->getDatabasePlatform()), + ); + } + + public function store(CipherKey $key): void + { + $this->connection->insert($this->tableName, [ + 'id' => $key->id, + 'subject_id' => $key->subjectId, + 'crypto_key' => base64_encode($key->key), + 'crypto_method' => $key->method, + 'created_at' => $this->dateTimeType->convertToDatabaseValue($key->createdAt, $this->connection->getDatabasePlatform()), + ]); + } + + public function remove(string $id): void + { + $this->connection->delete($this->tableName, ['id' => $id]); + } + + public function removeWithSubjectId(string $subjectId): void + { + $this->connection->delete($this->tableName, ['subject_id' => $subjectId]); + } + + public function configureSchema(Schema $schema, Connection $connection): void + { + if (!DoctrineHelper::sameDatabase($this->connection, $connection)) { + return; + } + + $table = $schema->createTable($this->tableName); + $table->addColumn('id', 'string') + ->setNotnull(true) + ->setLength(255); + $table->addColumn('subject_id', 'string') + ->setNotnull(true) + ->setLength(255); + $table->addColumn('crypto_key', 'string') + ->setNotnull(true) + ->setLength(255); + $table->addColumn('crypto_method', 'string') + ->setNotnull(true) + ->setLength(255); + $table->addColumn('created_at', 'datetimetz_immutable') + ->setNotnull(true); + $table->setPrimaryKey(['id']); + $table->addIndex(['subject_id']); + } +} diff --git a/tests/Integration/PersonalData/Events/NameChanged.php b/tests/Integration/PersonalData/Events/NameChanged.php index 22c18482d..bc6c1edd0 100644 --- a/tests/Integration/PersonalData/Events/NameChanged.php +++ b/tests/Integration/PersonalData/Events/NameChanged.php @@ -6,15 +6,19 @@ use Patchlevel\EventSourcing\Attribute\Event; use Patchlevel\EventSourcing\Tests\Integration\PersonalData\ProfileId; -use Patchlevel\Hydrator\Attribute\DataSubjectId; +use Patchlevel\Hydrator\Attribute\DataSubjectId as LegacyDataSubjectId; use Patchlevel\Hydrator\Attribute\PersonalData; +use Patchlevel\Hydrator\Extension\Cryptography\Attribute\DataSubjectId; +use Patchlevel\Hydrator\Extension\Cryptography\Attribute\SensitiveData; #[Event('profile.name_changed')] final class NameChanged { public function __construct( #[DataSubjectId] + #[LegacyDataSubjectId] public readonly ProfileId $aggregateId, + #[SensitiveData(fallback: 'unknown')] #[PersonalData(fallback: 'unknown')] public readonly string $name, ) { diff --git a/tests/Integration/PersonalData/Events/ProfileCreated.php b/tests/Integration/PersonalData/Events/ProfileCreated.php index 82b1dd0b8..bb9e775f3 100644 --- a/tests/Integration/PersonalData/Events/ProfileCreated.php +++ b/tests/Integration/PersonalData/Events/ProfileCreated.php @@ -6,15 +6,19 @@ use Patchlevel\EventSourcing\Attribute\Event; use Patchlevel\EventSourcing\Tests\Integration\PersonalData\ProfileId; -use Patchlevel\Hydrator\Attribute\DataSubjectId; +use Patchlevel\Hydrator\Attribute\DataSubjectId as LegacyDataSubjectId; use Patchlevel\Hydrator\Attribute\PersonalData; +use Patchlevel\Hydrator\Extension\Cryptography\Attribute\DataSubjectId; +use Patchlevel\Hydrator\Extension\Cryptography\Attribute\SensitiveData; #[Event('profile.created')] final class ProfileCreated { public function __construct( #[DataSubjectId] + #[LegacyDataSubjectId] public ProfileId $profileId, + #[SensitiveData(fallback: 'unknown')] #[PersonalData(fallback: 'unknown')] public string $name, ) { diff --git a/tests/Integration/PersonalData/PersonalDataTest.php b/tests/Integration/PersonalData/PersonalDataTest.php index eb2b4a7fb..a4782506e 100644 --- a/tests/Integration/PersonalData/PersonalDataTest.php +++ b/tests/Integration/PersonalData/PersonalDataTest.php @@ -6,7 +6,9 @@ use Doctrine\DBAL\Connection; use Patchlevel\EventSourcing\Cryptography\DoctrineCipherKeyStore; +use Patchlevel\EventSourcing\Cryptography\ExtensionDoctrineCipherKeyStore; use Patchlevel\EventSourcing\Metadata\AggregateRoot\AggregateRootRegistry; +use Patchlevel\EventSourcing\Metadata\Event\AttributeEventRegistryFactory; use Patchlevel\EventSourcing\Repository\DefaultRepositoryManager; use Patchlevel\EventSourcing\Schema\ChainDoctrineSchemaConfigurator; use Patchlevel\EventSourcing\Schema\DoctrineSchemaDirector; @@ -19,7 +21,11 @@ use Patchlevel\EventSourcing\Subscription\Subscriber\MetadataSubscriberAccessorRepository; use Patchlevel\EventSourcing\Tests\DbalManager; use Patchlevel\EventSourcing\Tests\Integration\PersonalData\Processor\DeletePersonalDataProcessor; +use Patchlevel\Hydrator\CoreExtension; use Patchlevel\Hydrator\Cryptography\PersonalDataPayloadCryptographer; +use Patchlevel\Hydrator\Extension\Cryptography\BaseCryptographer; +use Patchlevel\Hydrator\Extension\Cryptography\CryptographyExtension; +use Patchlevel\Hydrator\StackHydratorBuilder; use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\TestCase; @@ -232,4 +238,162 @@ public function testRemoveKeyWithEventAndSnapshot(): void self::assertSame(2, $profile->playhead()); self::assertSame('unknown', $profile->name()); } + + public function testWithStackHydrator(): void + { + $cipherKeyStore = new ExtensionDoctrineCipherKeyStore($this->connection); + $extension = new CryptographyExtension( + BaseCryptographer::createWithOpenssl($cipherKeyStore), + ); + + $eventSerializer = new DefaultEventSerializer( + (new AttributeEventRegistryFactory())->create([__DIR__ . '/Events']), + (new StackHydratorBuilder()) + ->useExtension(new CoreExtension()) + ->useExtension($extension) + ->build(), + ); + + $store = new DoctrineDbalStore( + $this->connection, + $eventSerializer, + ); + + $manager = new DefaultRepositoryManager( + new AggregateRootRegistry(['profile' => Profile::class]), + $store, + ); + + $repository = $manager->get(Profile::class); + + $schemaDirector = new DoctrineSchemaDirector( + $this->connection, + new ChainDoctrineSchemaConfigurator([ + $store, + $cipherKeyStore, + ]), + ); + + $schemaDirector->create(); + + $profileId = ProfileId::generate(); + $profile = Profile::create($profileId, 'John'); + + $repository->save($profile); + + $profile = $repository->load($profileId); + + self::assertInstanceOf(Profile::class, $profile); + self::assertEquals($profileId, $profile->aggregateRootId()); + self::assertSame(1, $profile->playhead()); + self::assertSame('John', $profile->name()); + + $result = $this->connection->fetchAllAssociative('SELECT * FROM eventstore'); + + self::assertCount(1, $result); + self::assertArrayHasKey(0, $result); + + $row = $result[0]; + + self::assertStringNotContainsString('John', $row['payload']); + } + + public function testWithStackHydratorWithLegacyFallback(): void + { + $extensionCipherKeyStore = new ExtensionDoctrineCipherKeyStore($this->connection); + $legacyCipherKeyStore = new DoctrineCipherKeyStore($this->connection); + + $cryptographer = PersonalDataPayloadCryptographer::createWithOpenssl($legacyCipherKeyStore); + + $store = new DoctrineDbalStore( + $this->connection, + DefaultEventSerializer::createFromPaths([__DIR__ . '/Events'], cryptographer: $cryptographer), + ); + + $manager = new DefaultRepositoryManager( + new AggregateRootRegistry(['profile' => Profile::class]), + $store, + ); + + $repository = $manager->get(Profile::class); + + $schemaDirector = new DoctrineSchemaDirector( + $this->connection, + new ChainDoctrineSchemaConfigurator([ + $store, + $legacyCipherKeyStore, + $extensionCipherKeyStore, + ]), + ); + + $schemaDirector->create(); + + $profileId = ProfileId::generate(); + $profile = Profile::create($profileId, 'John'); + + $repository->save($profile); + + // switch to new hydrator + + $extension = new CryptographyExtension( + BaseCryptographer::createWithOpenssl($extensionCipherKeyStore), + PersonalDataPayloadCryptographer::createWithOpenssl($legacyCipherKeyStore), + ); + + $eventSerializer = new DefaultEventSerializer( + (new AttributeEventRegistryFactory())->create([__DIR__ . '/Events']), + (new StackHydratorBuilder()) + ->useExtension(new CoreExtension()) + ->useExtension($extension) + ->build(), + ); + + $store = new DoctrineDbalStore( + $this->connection, + $eventSerializer, + ); + + $manager = new DefaultRepositoryManager( + new AggregateRootRegistry(['profile' => Profile::class]), + $store, + ); + + $repository = $manager->get(Profile::class); + $profile = $repository->load($profileId); + + self::assertInstanceOf(Profile::class, $profile); + self::assertEquals($profileId, $profile->aggregateRootId()); + self::assertSame(1, $profile->playhead()); + self::assertSame('John', $profile->name()); + + $result = $this->connection->fetchAllAssociative('SELECT * FROM eventstore'); + + self::assertCount(1, $result); + self::assertArrayHasKey(0, $result); + + $row = $result[0]; + + self::assertStringNotContainsString('John', $row['payload']); + + $result = $this->connection->fetchAllAssociative('SELECT * FROM crypto_keys'); + + self::assertCount(1, $result); + self::assertArrayHasKey(0, $result); + + $row = $result[0]; + + self::assertEquals($profileId->toString(), $row['subject_id']); + + $result = $this->connection->fetchAllAssociative('SELECT * FROM cryptography_keys'); + + self::assertCount(0, $result); + + $profile->changeName('John 2'); + $repository->save($profile); + + $result = $this->connection->fetchAllAssociative('SELECT * FROM cryptography_keys'); + + self::assertCount(1, $result); + self::assertEquals($profileId->toString(), $row['subject_id']); + } }