diff --git a/.travis.yml b/.travis.yml index 40f6484b..9eff19d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,7 @@ before_script: script: - if [[ $TEST_COVERAGE == 'true' ]]; then php -dzend_extension=xdebug.so ./vendor/bin/phpunit --exclude-group=ignore --coverage-text --coverage-clover ./build/logs/clover.xml; else ./vendor/bin/phpunit --exclude-group=ignore; fi +- ./vendor/bin/psalm - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/php-cs-fixer fix -v --diff --dry-run; fi after_success: diff --git a/composer.json b/composer.json index d281698a..eaeb8ed3 100644 --- a/composer.json +++ b/composer.json @@ -19,16 +19,16 @@ "require": { "php": "^7.4", "ext-json": "*", - "ramsey/uuid": "^3.8" + "ramsey/uuid": "^3.9.3" }, "require-dev": { - "amphp/amp": "^2.1.2", - "doctrine/instantiator": "^1.1", - "php-coveralls/php-coveralls": "^2.1", - "phpspec/prophecy": "^1.7.2", - "phpunit/phpunit": "^8.2.2", - "prooph/php-cs-fixer-config": "^0.3", - "sebastian/object-enumerator": "^3.0.3" + "amphp/amp": "^2.4.4", + "doctrine/instantiator": "^1.3", + "php-coveralls/php-coveralls": "^2.2", + "phpspec/prophecy": "^1.10.3", + "phpunit/phpunit": "^9.1", + "prooph/php-cs-fixer-config": "^0.3.1", + "vimeo/psalm": "^3.11.2" }, "autoload": { "psr-4": { diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 00000000..d0e0cce1 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/src/AllEventsSlice.php b/src/AllEventsSlice.php index 06948921..4c3c7dbd 100644 --- a/src/AllEventsSlice.php +++ b/src/AllEventsSlice.php @@ -18,17 +18,14 @@ class AllEventsSlice private ReadDirection $readDirection; private Position $fromPosition; private Position $nextPosition; - /** @var ResolvedEvent[] */ + /** @var list */ private array $events; private bool $isEndOfStream; /** * @internal * - * @param ReadDirection $readDirection - * @param Position $fromPosition - * @param Position $nextPosition - * @param ResolvedEvent[] $events + * @param list $events */ public function __construct( ReadDirection $readDirection, @@ -58,7 +55,7 @@ public function nextPosition(): Position return $this->nextPosition; } - /** @return ResolvedEvent[] */ + /** @return list */ public function events(): array { return $this->events; diff --git a/src/Async/CatchUpSubscriptionDropped.php b/src/Async/CatchUpSubscriptionDropped.php deleted file mode 100644 index 1bd42765..00000000 --- a/src/Async/CatchUpSubscriptionDropped.php +++ /dev/null @@ -1,26 +0,0 @@ - - * (c) 2015-2020 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Prooph\EventStore\Async; - -use Prooph\EventStore\SubscriptionDropReason; -use Throwable; - -interface CatchUpSubscriptionDropped -{ - public function __invoke( - EventStoreCatchUpSubscription $subscription, - SubscriptionDropReason $reason, - ?Throwable $exception = null - ): void; -} diff --git a/src/Async/EventAppearedOnCatchupSubscription.php b/src/Async/EventAppearedOnCatchupSubscription.php deleted file mode 100644 index a5753034..00000000 --- a/src/Async/EventAppearedOnCatchupSubscription.php +++ /dev/null @@ -1,25 +0,0 @@ - - * (c) 2015-2020 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Prooph\EventStore\Async; - -use Amp\Promise; -use Prooph\EventStore\ResolvedEvent; - -interface EventAppearedOnCatchupSubscription -{ - public function __invoke( - EventStoreCatchUpSubscription $subscription, - ResolvedEvent $resolvedEvent - ): Promise; -} diff --git a/src/Async/EventAppearedOnPersistentSubscription.php b/src/Async/EventAppearedOnPersistentSubscription.php deleted file mode 100644 index 309d3250..00000000 --- a/src/Async/EventAppearedOnPersistentSubscription.php +++ /dev/null @@ -1,26 +0,0 @@ - - * (c) 2015-2020 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Prooph\EventStore\Async; - -use Amp\Promise; -use Prooph\EventStore\ResolvedEvent; - -interface EventAppearedOnPersistentSubscription -{ - public function __invoke( - EventStorePersistentSubscription $subscription, - ResolvedEvent $resolvedEvent, - ?int $retryCount = null - ): Promise; -} diff --git a/src/Async/EventAppearedOnSubscription.php b/src/Async/EventAppearedOnSubscription.php deleted file mode 100644 index 3ba98db4..00000000 --- a/src/Async/EventAppearedOnSubscription.php +++ /dev/null @@ -1,26 +0,0 @@ - - * (c) 2015-2020 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Prooph\EventStore\Async; - -use Amp\Promise; -use Prooph\EventStore\EventStoreSubscription; -use Prooph\EventStore\ResolvedEvent; - -interface EventAppearedOnSubscription -{ - public function __invoke( - EventStoreSubscription $subscription, - ResolvedEvent $resolvedEvent - ): Promise; -} diff --git a/src/Async/EventStoreCatchUpSubscription.php b/src/Async/EventStoreCatchUpSubscription.php index 9909365f..ecd58b6d 100644 --- a/src/Async/EventStoreCatchUpSubscription.php +++ b/src/Async/EventStoreCatchUpSubscription.php @@ -14,7 +14,6 @@ namespace Prooph\EventStore\Async; use Amp\Promise; -use Throwable; interface EventStoreCatchUpSubscription { diff --git a/src/Async/EventStoreConnection.php b/src/Async/EventStoreConnection.php index b96688df..dfd1a6f3 100644 --- a/src/Async/EventStoreConnection.php +++ b/src/Async/EventStoreConnection.php @@ -29,13 +29,15 @@ use Prooph\EventStore\PersistentSubscriptionSettings; use Prooph\EventStore\Position; use Prooph\EventStore\RawStreamMetadataResult; +use Prooph\EventStore\ResolvedEvent; use Prooph\EventStore\StreamEventsSlice; use Prooph\EventStore\StreamMetadata; use Prooph\EventStore\StreamMetadataResult; -use Prooph\EventStore\SubscriptionDropped; +use Prooph\EventStore\SubscriptionDropReason; use Prooph\EventStore\SystemSettings; use Prooph\EventStore\UserCredentials; use Prooph\EventStore\WriteResult; +use Throwable; interface EventStoreConnection { @@ -54,10 +56,7 @@ public function deleteStreamAsync( ): Promise; /** - * @param string $stream - * @param int $expectedVersion - * @param EventData[] $events - * @param UserCredentials|null $userCredentials + * @param list $events * @return Promise */ public function appendToStreamAsync( @@ -68,10 +67,7 @@ public function appendToStreamAsync( ): Promise; /** - * @param string $stream - * @param int $expectedVersion - * @param EventData[] $events - * @param UserCredentials|null $userCredentials + * @param list $events * @return Promise */ public function conditionalAppendToStreamAsync( @@ -184,74 +180,106 @@ public function deletePersistentSubscriptionAsync( ): Promise; /** + * @param Closure(EventStoreSubscription, ResolvedEvent): Promise $eventAppeared + * @param null|Closure(EventStoreSubscription, SubscriptionDropReason, ?Throwable): void $subscriptionDropped + * @param null|UserCredentials $userCredentials * @return Promise */ public function subscribeToStreamAsync( string $stream, bool $resolveLinkTos, - EventAppearedOnSubscription $eventAppeared, - ?SubscriptionDropped $subscriptionDropped = null, + Closure $eventAppeared, + ?Closure $subscriptionDropped = null, ?UserCredentials $userCredentials = null ): Promise; /** + * @param Closure(EventStoreCatchUpSubscription, ResolvedEvent): Promise $eventAppeared + * @param null|Closure(EventStoreCatchUpSubscription): void $liveProcessingStarted + * @param null|Closure(EventStoreCatchUpSubscription, SubscriptionDropReason, ?Throwable): void $subscriptionDropped * @return Promise */ public function subscribeToStreamFromAsync( string $stream, ?int $lastCheckpoint, ?CatchUpSubscriptionSettings $settings, - EventAppearedOnCatchupSubscription $eventAppeared, - ?LiveProcessingStartedOnCatchUpSubscription $liveProcessingStarted = null, - ?CatchUpSubscriptionDropped $subscriptionDropped = null, + Closure $eventAppeared, + ?Closure $liveProcessingStarted = null, + ?Closure $subscriptionDropped = null, ?UserCredentials $userCredentials = null ): Promise; /** + * @param Closure(EventStoreSubscription, ResolvedEvent): Promise $eventAppeared + * @param null|Closure(EventStoreSubscription, SubscriptionDropReason, ?Throwable): void $subscriptionDropped * @return Promise */ public function subscribeToAllAsync( bool $resolveLinkTos, - EventAppearedOnSubscription $eventAppeared, - ?SubscriptionDropped $subscriptionDropped = null, + Closure $eventAppeared, + ?Closure $subscriptionDropped = null, ?UserCredentials $userCredentials = null ): Promise; /** + * @param Closure(EventStoreCatchUpSubscription, ResolvedEvent): Promise $eventAppeared + * @param null|Closure(EventStoreCatchUpSubscription): void $liveProcessingStarted + * @param null|Closure(EventStoreCatchUpSubscription, SubscriptionDropReason, ?Throwable): void $subscriptionDropped * @return Promise */ public function subscribeToAllFromAsync( ?Position $lastCheckpoint, ?CatchUpSubscriptionSettings $settings, - EventAppearedOnCatchupSubscription $eventAppeared, - ?LiveProcessingStartedOnCatchUpSubscription $liveProcessingStarted = null, - ?CatchUpSubscriptionDropped $subscriptionDropped = null, + Closure $eventAppeared, + ?Closure $liveProcessingStarted = null, + ?Closure $subscriptionDropped = null, ?UserCredentials $userCredentials = null ): Promise; /** + * @param Closure(EventStorePersistentSubscription, ResolvedEvent, ?int): Promise $eventAppeared + * @param null|Closure(EventStorePersistentSubscription, SubscriptionDropReason, ?Throwable): void $subscriptionDropped + * * @return Promise */ public function connectToPersistentSubscriptionAsync( string $stream, string $groupName, - EventAppearedOnPersistentSubscription $eventAppeared, - ?PersistentSubscriptionDropped $subscriptionDropped = null, + Closure $eventAppeared, + ?Closure $subscriptionDropped = null, int $bufferSize = 10, bool $autoAck = true, ?UserCredentials $userCredentials = null ): Promise; + /** + * @param Closure(ClientConnectionEventArgs): void $handler + */ public function onConnected(Closure $handler): ListenerHandler; + /** + * @param Closure(ClientConnectionEventArgs): void $handler + */ public function onDisconnected(Closure $handler): ListenerHandler; + /** + * @param Closure(ClientReconnectingEventArgs): void $handler + */ public function onReconnecting(Closure $handler): ListenerHandler; + /** + * @param Closure(ClientClosedEventArgs): void $handler + */ public function onClosed(Closure $handler): ListenerHandler; + /** + * @param Closure(ClientErrorEventArgs): void $handler + */ public function onErrorOccurred(Closure $handler): ListenerHandler; + /** + * @param Closure(ClientAuthenticationFailedEventArgs): void $handler + */ public function onAuthenticationFailed(Closure $handler): ListenerHandler; public function detach(ListenerHandler $handler): void; diff --git a/src/Async/EventStorePersistentSubscription.php b/src/Async/EventStorePersistentSubscription.php index 241b80b7..f05b3291 100644 --- a/src/Async/EventStorePersistentSubscription.php +++ b/src/Async/EventStorePersistentSubscription.php @@ -25,10 +25,6 @@ interface EventStorePersistentSubscription /** * Acknowledge that a message have completed processing (this will tell the server it has been processed) * Note: There is no need to ack a message if you have Auto Ack enabled - * - * @param ResolvedEvent $event - * - * @return void */ public function acknowledge(ResolvedEvent $event): void; @@ -36,19 +32,13 @@ public function acknowledge(ResolvedEvent $event): void; * Acknowledge that a message have completed processing (this will tell the server it has been processed) * Note: There is no need to ack a message if you have Auto Ack enabled * - * @param ResolvedEvent[] $events - * - * @return void + * @param list $events */ public function acknowledgeMultiple(array $events): void; /** * Acknowledge that a message have completed processing (this will tell the server it has been processed) * Note: There is no need to ack a message if you have Auto Ack enabled - * - * @param EventId $eventId - * - * @return void */ public function acknowledgeEventId(EventId $eventId): void; @@ -56,9 +46,7 @@ public function acknowledgeEventId(EventId $eventId): void; * Acknowledge that a message have completed processing (this will tell the server it has been processed) * Note: There is no need to ack a message if you have Auto Ack enabled * - * @param EventId[] $eventIds - * - * @return void + * @param list $eventIds */ public function acknowledgeMultipleEventIds(array $eventIds): void; @@ -74,9 +62,7 @@ public function fail( /** * Mark n messages that have failed processing. The server will take action based upon the action parameter * - * @param ResolvedEvent[] $events - * @param PersistentSubscriptionNakEventAction $action - * @param string $reason + * @param list $events */ public function failMultiple( array $events, @@ -96,9 +82,7 @@ public function failEventId( /** * Mark n messages that have failed processing. The server will take action based upon the action parameter * - * @param EventId[] $eventIds - * @param PersistentSubscriptionNakEventAction $action - * @param string $reason + * @param list $eventIds */ public function failMultipleEventIds( array $eventIds, diff --git a/src/Async/EventStoreTransaction.php b/src/Async/EventStoreTransaction.php index 617c8ccf..794c9d03 100644 --- a/src/Async/EventStoreTransaction.php +++ b/src/Async/EventStoreTransaction.php @@ -59,7 +59,7 @@ public function commitAsync(): Promise } /** - * @param EventData[] $events + * @param list $events * * @return Promise */ diff --git a/src/Async/Internal/EventStoreTransactionConnection.php b/src/Async/Internal/EventStoreTransactionConnection.php index 491f46dd..42103ed7 100644 --- a/src/Async/Internal/EventStoreTransactionConnection.php +++ b/src/Async/Internal/EventStoreTransactionConnection.php @@ -21,6 +21,7 @@ /** @internal */ interface EventStoreTransactionConnection { + /** @return Promise */ public function transactionalWriteAsync( EventStoreTransaction $transaction, array $events, diff --git a/src/Async/LiveProcessingStartedOnCatchUpSubscription.php b/src/Async/LiveProcessingStartedOnCatchUpSubscription.php deleted file mode 100644 index c2a1087a..00000000 --- a/src/Async/LiveProcessingStartedOnCatchUpSubscription.php +++ /dev/null @@ -1,19 +0,0 @@ - - * (c) 2015-2020 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Prooph\EventStore\Async; - -interface LiveProcessingStartedOnCatchUpSubscription -{ - public function __invoke(EventStoreCatchUpSubscription $subscription): void; -} diff --git a/src/Async/PersistentSubscriptionDropped.php b/src/Async/PersistentSubscriptionDropped.php deleted file mode 100644 index bc88d62a..00000000 --- a/src/Async/PersistentSubscriptionDropped.php +++ /dev/null @@ -1,26 +0,0 @@ - - * (c) 2015-2020 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Prooph\EventStore\Async; - -use Prooph\EventStore\SubscriptionDropReason; -use Throwable; - -interface PersistentSubscriptionDropped -{ - public function __invoke( - EventStorePersistentSubscription $subscription, - SubscriptionDropReason $reason, - ?Throwable $exception = null - ): void; -} diff --git a/src/Async/PersistentSubscriptions/PersistentSubscriptionsManager.php b/src/Async/PersistentSubscriptions/PersistentSubscriptionsManager.php index 3c8a46cf..f825ab6e 100644 --- a/src/Async/PersistentSubscriptions/PersistentSubscriptionsManager.php +++ b/src/Async/PersistentSubscriptions/PersistentSubscriptionsManager.php @@ -32,6 +32,6 @@ public function replayParkedMessages( ?UserCredentials $userCredentials = null ): Promise; - /** @return Promise */ + /** @return Promise> */ public function list(?string $stream = null, ?UserCredentials $userCredentials = null): Promise; } diff --git a/src/Async/Projections/ProjectionsManager.php b/src/Async/Projections/ProjectionsManager.php index 97879684..ebd1009a 100644 --- a/src/Async/Projections/ProjectionsManager.php +++ b/src/Async/Projections/ProjectionsManager.php @@ -67,21 +67,21 @@ public function createContinuousAsync( /** * Asynchronously lists all projections * - * @return Promise + * @return Promise> */ public function listAllAsync(?UserCredentials $userCredentials = null): Promise; /** * Asynchronously lists all one-time projections * - * @return Promise + * @return Promise> */ public function listOneTimeAsync(?UserCredentials $userCredentials = null): Promise; /** * Asynchronously lists this status of all continuous projections * - * @return Promise + * @return Promise> */ public function listContinuousAsync(?UserCredentials $userCredentials = null): Promise; diff --git a/src/Async/Projections/QueryManager.php b/src/Async/Projections/QueryManager.php index 0b18de3f..4e6be341 100644 --- a/src/Async/Projections/QueryManager.php +++ b/src/Async/Projections/QueryManager.php @@ -25,13 +25,6 @@ interface QueryManager * * returns String of JSON containing query result * - * @param string $name A name for the query - * @param string $query The source code for the query - * @param int $initialPollingDelay Initial time to wait between polling for projection status - * @param int $maximumPollingDelay Maximum time to wait between polling for projection status - * @param string $type The type to use, defaults to JS - * @param UserCredentials|null $userCredentials Credentials for a user with permission to create a query - * * @return Promise */ public function executeAsync( diff --git a/src/Async/UserManagement/UsersManager.php b/src/Async/UserManagement/UsersManager.php index b48bbdf7..be449933 100644 --- a/src/Async/UserManagement/UsersManager.php +++ b/src/Async/UserManagement/UsersManager.php @@ -20,14 +20,16 @@ interface UsersManager { + /** @return Promise */ public function enableAsync(string $login, ?UserCredentials $userCredentials = null): Promise; + /** @return Promise */ public function disableAsync(string $login, ?UserCredentials $userCredentials = null): Promise; /** @throws UserCommandFailed */ public function deleteUserAsync(string $login, ?UserCredentials $userCredentials = null): Promise; - /** @return Promise */ + /** @return Promise> */ public function listAllAsync(?UserCredentials $userCredentials = null): Promise; /** @return Promise */ @@ -37,12 +39,8 @@ public function getCurrentUserAsync(?UserCredentials $userCredentials = null): P public function getUserAsync(string $login, ?UserCredentials $userCredentials = null): Promise; /** - * @param string $login - * @param string $fullName - * @param string[] $groups - * @param string $password - * @param UserCredentials|null $userCredentials - * @return Promise + * @param list $groups + * @return Promise */ public function createUserAsync( string $login, @@ -53,11 +51,8 @@ public function createUserAsync( ): Promise; /** - * @param string $login - * @param string $fullName - * @param string[] $groups - * @param UserCredentials|null $userCredentials - * @return Promise + * @param list $groups + * @return Promise */ public function updateUserAsync( string $login, @@ -66,6 +61,7 @@ public function updateUserAsync( ?UserCredentials $userCredentials = null ): Promise; + /** @return Promise */ public function changePasswordAsync( string $login, string $oldPassword, @@ -73,6 +69,7 @@ public function changePasswordAsync( ?UserCredentials $userCredentials = null ): Promise; + /** @return Promise */ public function resetPasswordAsync( string $login, string $newPassword, diff --git a/src/CatchUpSubscriptionDropped.php b/src/CatchUpSubscriptionDropped.php deleted file mode 100644 index a43dc165..00000000 --- a/src/CatchUpSubscriptionDropped.php +++ /dev/null @@ -1,25 +0,0 @@ - - * (c) 2015-2020 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Prooph\EventStore; - -use Throwable; - -interface CatchUpSubscriptionDropped -{ - public function __invoke( - EventStoreCatchUpSubscription $subscription, - SubscriptionDropReason $reason, - ?Throwable $exception = null - ): void; -} diff --git a/src/ConditionalWriteResult.php b/src/ConditionalWriteResult.php index 9a4b85d6..71d8f65c 100644 --- a/src/ConditionalWriteResult.php +++ b/src/ConditionalWriteResult.php @@ -15,25 +15,23 @@ use Prooph\EventStore\Exception\InvalidArgumentException; +/** @psam-immutable */ class ConditionalWriteResult { private ConditionalWriteStatus $status; private ?int $nextExpectedVersion; private ?Position $logPosition; - private function __construct() + private function __construct(ConditionalWriteStatus $status, ?int $nextExpectedVersion, ?Position $logPosition) { + $this->status = $status; + $this->nextExpectedVersion = $nextExpectedVersion; + $this->logPosition = $logPosition; } public static function success(int $nextExpectedVersion, Position $logPosition): ConditionalWriteResult { - $self = new self(); - - $self->status = ConditionalWriteStatus::succeeded(); - $self->nextExpectedVersion = $nextExpectedVersion; - $self->logPosition = $logPosition; - - return $self; + return new self(ConditionalWriteStatus::succeeded(), $nextExpectedVersion, $logPosition); } public static function fail(ConditionalWriteStatus $status): ConditionalWriteResult @@ -42,22 +40,22 @@ public static function fail(ConditionalWriteStatus $status): ConditionalWriteRes throw new InvalidArgumentException('For successful write pass next expected version and log position'); } - $self = new self(); - $self->status = $status; - - return $self; + return new self($status, null, null); } + /** @psalm-pure */ public function status(): ConditionalWriteStatus { return $this->status; } + /** @psalm-pure */ public function nextExpectedVersion(): ?int { return $this->nextExpectedVersion; } + /** @psalm-pure */ public function logPosition(): ?Position { return $this->logPosition; diff --git a/src/ConditionalWriteStatus.php b/src/ConditionalWriteStatus.php index 311e0ff5..b611eaa0 100644 --- a/src/ConditionalWriteStatus.php +++ b/src/ConditionalWriteStatus.php @@ -15,6 +15,7 @@ use Prooph\EventStore\Exception\InvalidArgumentException; +/** @psalm-immutable */ class ConditionalWriteStatus { public const OPTIONS = [ @@ -57,35 +58,39 @@ public static function byName(string $value): self throw new InvalidArgumentException('Unknown enum name given'); } - return self::{$value}(); + return new self($value); } - public static function byValue($value): self + public static function byValue(int $value): self { foreach (self::OPTIONS as $name => $v) { if ($v === $value) { - return self::{$name}(); + return new self($name); } } throw new InvalidArgumentException('Unknown enum value given'); } + /** @psalm-pure */ public function equals(ConditionalWriteStatus $other): bool { return \get_class($this) === \get_class($other) && $this->name === $other->name; } + /** @psalm-pure */ public function name(): string { return $this->name; } - public function value() + /** @psalm-pure */ + public function value(): int { return $this->value; } + /** @psalm-pure */ public function __toString(): string { return $this->name; diff --git a/src/EventAppearedOnCatchupSubscription.php b/src/EventAppearedOnCatchupSubscription.php deleted file mode 100644 index 6912eab5..00000000 --- a/src/EventAppearedOnCatchupSubscription.php +++ /dev/null @@ -1,22 +0,0 @@ - - * (c) 2015-2020 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Prooph\EventStore; - -interface EventAppearedOnCatchupSubscription -{ - public function __invoke( - EventStoreCatchUpSubscription $subscription, - ResolvedEvent $resolvedEvent - ): void; -} diff --git a/src/EventAppearedOnPersistentSubscription.php b/src/EventAppearedOnPersistentSubscription.php deleted file mode 100644 index b6d4d65b..00000000 --- a/src/EventAppearedOnPersistentSubscription.php +++ /dev/null @@ -1,23 +0,0 @@ - - * (c) 2015-2020 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Prooph\EventStore; - -interface EventAppearedOnPersistentSubscription -{ - public function __invoke( - EventStorePersistentSubscription $subscription, - ResolvedEvent $resolvedEvent, - ?int $retryCount = null - ): void; -} diff --git a/src/EventAppearedOnSubscription.php b/src/EventAppearedOnSubscription.php deleted file mode 100644 index c85f75a9..00000000 --- a/src/EventAppearedOnSubscription.php +++ /dev/null @@ -1,22 +0,0 @@ - - * (c) 2015-2020 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Prooph\EventStore; - -interface EventAppearedOnSubscription -{ - public function __invoke( - EventStoreSubscription $subscription, - ResolvedEvent $resolvedEvent - ): void; -} diff --git a/src/EventId.php b/src/EventId.php index aa0adf71..11f9b1b9 100644 --- a/src/EventId.php +++ b/src/EventId.php @@ -16,6 +16,7 @@ use Prooph\EventStore\Util\Guid; use Ramsey\Uuid\UuidInterface; +/** @psalm-immutable */ class EventId { private UuidInterface $uuid; @@ -37,26 +38,35 @@ public static function fromBinary(string $bytes): EventId private function __construct(UuidInterface $eventId) { + /** @psalm-suppress ImpurePropertyAssignment */ $this->uuid = $eventId; } + /** @psalm-pure */ public function toString(): string { + /** @psalm-suppress ImpureMethodCall */ return $this->uuid->toString(); } + /** @psalm-pure */ public function toBinary(): string { + /** @psalm-suppress ImpureMethodCall */ return $this->uuid->getBytes(); } + /** @psalm-pure */ public function __toString(): string { + /** @psalm-suppress ImpureMethodCall */ return $this->uuid->toString(); } + /** @psalm-pure */ public function equals(EventId $other): bool { + /** @psalm-suppress ImpureMethodCall */ return $this->uuid->equals($other->uuid); } } diff --git a/src/EventReadStatus.php b/src/EventReadStatus.php index 1439dbc2..f4247f6e 100644 --- a/src/EventReadStatus.php +++ b/src/EventReadStatus.php @@ -15,6 +15,7 @@ use Prooph\EventStore\Exception\InvalidArgumentException; +/** @psalm-immutable */ class EventReadStatus { public const OPTIONS = [ @@ -64,35 +65,39 @@ public static function byName(string $value): self throw new InvalidArgumentException('Unknown enum name given'); } - return self::{$value}(); + return new self($value); } - public static function byValue($value): self + public static function byValue(int $value): self { foreach (self::OPTIONS as $name => $v) { if ($v === $value) { - return self::{$name}(); + return new self($name); } } throw new InvalidArgumentException('Unknown enum value given'); } + /** @psalm-pure */ public function equals(EventReadStatus $other): bool { return static::class === \get_class($other) && $this->name === $other->name; } + /** @psalm-pure */ public function name(): string { return $this->name; } - public function value() + /** @psalm-pure */ + public function value(): int { return $this->value; } + /** @psalm-pure */ public function __toString(): string { return $this->name; diff --git a/src/EventStoreCatchUpSubscription.php b/src/EventStoreCatchUpSubscription.php index 6ad827fb..c0c548c5 100644 --- a/src/EventStoreCatchUpSubscription.php +++ b/src/EventStoreCatchUpSubscription.php @@ -13,8 +13,6 @@ namespace Prooph\EventStore; -use Throwable; - interface EventStoreCatchUpSubscription { public function isSubscribedToAll(): bool; diff --git a/src/EventStoreConnection.php b/src/EventStoreConnection.php index b0c5c516..46f59a09 100644 --- a/src/EventStoreConnection.php +++ b/src/EventStoreConnection.php @@ -13,9 +13,11 @@ namespace Prooph\EventStore; +use Closure; use Prooph\EventStore\Internal\PersistentSubscriptionCreateResult; use Prooph\EventStore\Internal\PersistentSubscriptionDeleteResult; use Prooph\EventStore\Internal\PersistentSubscriptionUpdateResult; +use Throwable; interface EventStoreConnection { @@ -27,10 +29,7 @@ public function deleteStream( ): DeleteResult; /** - * @param string $stream - * @param int $expectedVersion - * @param EventData[] $events - * @param UserCredentials|null $userCredentials + * @param list $events * * @return WriteResult */ @@ -136,45 +135,67 @@ public function deletePersistentSubscription( ?UserCredentials $userCredentials = null ): PersistentSubscriptionDeleteResult; + /** + * @param Closure(EventStoreSubscription, ResolvedEvent): void $eventAppeared + * @param null|Closure(EventStoreSubscription, SubscriptionDropReason, ?Throwable): void $subscriptionDropped + */ public function subscribeToStream( string $stream, bool $resolveLinkTos, - EventAppearedOnSubscription $eventAppeared, - ?SubscriptionDropped $subscriptionDropped = null, + Closure $eventAppeared, + ?Closure $subscriptionDropped = null, ?UserCredentials $userCredentials = null ): EventStoreSubscription; + /** + * @param Closure(EventStoreCatchUpSubscription, ResolvedEvent): void $eventAppeared + * @param null|Closure(EventStoreCatchUpSubscription): void $liveProcessingStarted + * @param null|Closure(EventStoreCatchUpSubscription, SubscriptionDropReason, ?Throwable): void $subscriptionDropped + */ public function subscribeToStreamFrom( string $stream, ?int $lastCheckpoint, ?CatchUpSubscriptionSettings $settings, - EventAppearedOnCatchupSubscription $eventAppeared, - ?LiveProcessingStartedOnCatchUpSubscription $liveProcessingStarted = null, - ?CatchUpSubscriptionDropped $subscriptionDropped = null, + Closure $eventAppeared, + ?Closure $liveProcessingStarted = null, + ?Closure $subscriptionDropped = null, ?UserCredentials $userCredentials = null ): EventStoreStreamCatchUpSubscription; + /** + * @param Closure(EventStoreSubscription, ResolvedEvent): void $eventAppeared + * @param Closure(EventStoreSubscription, SubscriptionDropReason, ?Throwable): void $subscriptionDropped + */ public function subscribeToAll( bool $resolveLinkTos, - EventAppearedOnSubscription $eventAppeared, - ?SubscriptionDropped $subscriptionDropped = null, + Closure $eventAppeared, + ?Closure $subscriptionDropped = null, ?UserCredentials $userCredentials = null ): EventStoreSubscription; + /** + * @param Closure(EventStoreCatchUpSubscription, ResolvedEvent): void $eventAppeared + * @param null|Closure(EventStoreCatchUpSubscription): void $liveProcessingStarted + * @param null|Closure(EventStoreCatchUpSubscription, SubscriptionDropReason, ?Throwable): void $subscriptionDropped + */ public function subscribeToAllFrom( ?Position $lastCheckpoint, ?CatchUpSubscriptionSettings $settings, - EventAppearedOnCatchupSubscription $eventAppeared, - ?LiveProcessingStartedOnCatchUpSubscription $liveProcessingStarted = null, - ?CatchUpSubscriptionDropped $subscriptionDropped = null, + Closure $eventAppeared, + ?Closure $liveProcessingStarted = null, + ?Closure $subscriptionDropped = null, ?UserCredentials $userCredentials = null ): EventStoreAllCatchUpSubscription; + /** + * @param Closure(EventStorePersistentSubscription, ResolvedEvent, ?int): void $eventAppeared + * @param null|Closure(EventStorePersistentSubscription, SubscriptionDropReason, ?Throwable): void $subscriptionDropped + */ public function connectToPersistentSubscription( string $stream, string $groupName, - EventAppearedOnPersistentSubscription $eventAppeared, - ?PersistentSubscriptionDropped $subscriptionDropped = null, + Closure $eventAppeared, + ?Closure $subscriptionDropped = null, int $bufferSize = 10, bool $autoAck = true, ?UserCredentials $userCredentials = null diff --git a/src/EventStorePersistentSubscription.php b/src/EventStorePersistentSubscription.php index f853d412..ab98c0b5 100644 --- a/src/EventStorePersistentSubscription.php +++ b/src/EventStorePersistentSubscription.php @@ -31,7 +31,7 @@ public function acknowledge(ResolvedEvent $event): void; * Acknowledge that a message have completed processing (this will tell the server it has been processed) * Note: There is no need to ack a message if you have Auto Ack enabled * - * @param ResolvedEvent[] $events + * @param list $events */ public function acknowledgeMultiple(array $events): void; @@ -45,7 +45,7 @@ public function acknowledgeEventId(EventId $eventId): void; * Acknowledge that a message have completed processing (this will tell the server it has been processed) * Note: There is no need to ack a message if you have Auto Ack enabled * - * @param EventId[] $eventIds + * @param list $eventIds */ public function acknowledgeMultipleEventIds(array $eventIds): void; @@ -61,9 +61,7 @@ public function fail( /** * Mark n messages that have failed processing. The server will take action based upon the action parameter * - * @param ResolvedEvent[] $events - * @param PersistentSubscriptionNakEventAction $action - * @param string $reason + * @param list $events */ public function failMultiple( array $events, @@ -83,9 +81,7 @@ public function failEventId( /** * Mark n messages that have failed processing. The server will take action based upon the action parameter * - * @param EventId[] $eventIds - * @param PersistentSubscriptionNakEventAction $action - * @param string $reason + * @param list $eventIds */ public function failMultipleEventIds( array $eventIds, diff --git a/src/EventStoreTransaction.php b/src/EventStoreTransaction.php index 30324b6a..fcc671da 100644 --- a/src/EventStoreTransaction.php +++ b/src/EventStoreTransaction.php @@ -52,7 +52,7 @@ public function commit(): WriteResult } /** - * @param EventData[] $events + * @param list $events */ public function write(array $events = []): void { diff --git a/src/Exception/WrongExpectedVersion.php b/src/Exception/WrongExpectedVersion.php index ab9dc433..e72d2ad4 100644 --- a/src/Exception/WrongExpectedVersion.php +++ b/src/Exception/WrongExpectedVersion.php @@ -30,7 +30,7 @@ public static function with( $message, $stream, $expectedVersion, - $currentVersion + (string) $currentVersion )); } } diff --git a/src/Internal/ConnectToPersistentSubscriptions.php b/src/Internal/ConnectToPersistentSubscriptions.php index 2d4c7a73..1729a19a 100644 --- a/src/Internal/ConnectToPersistentSubscriptions.php +++ b/src/Internal/ConnectToPersistentSubscriptions.php @@ -19,13 +19,11 @@ /** @internal */ interface ConnectToPersistentSubscriptions { - /** @param EventId[] $eventIds */ + /** @param list $eventIds */ public function notifyEventsProcessed(array $eventIds): void; /** - * @param EventId[] $eventIds - * @param PersistentSubscriptionNakEventAction $action - * @param string $reason + * @param list $eventIds */ public function notifyEventsFailed( array $eventIds, diff --git a/src/Internal/PersistentEventStoreSubscription.php b/src/Internal/PersistentEventStoreSubscription.php index 65a8d2ce..f5d9e15d 100644 --- a/src/Internal/PersistentEventStoreSubscription.php +++ b/src/Internal/PersistentEventStoreSubscription.php @@ -42,16 +42,14 @@ public function unsubscribe(): void $this->subscriptionOperation->unsubscribe(); } - /** @param EventId[] $eventIds */ + /** @param list $eventIds */ public function notifyEventsProcessed(array $eventIds): void { $this->subscriptionOperation->notifyEventsProcessed($eventIds); } /** - * @param EventId[] $eventIds - * @param PersistentSubscriptionNakEventAction $action - * @param string $reason + * @param list $eventIds */ public function notifyEventsFailed( array $eventIds, diff --git a/src/Internal/PersistentSubscriptionCreateStatus.php b/src/Internal/PersistentSubscriptionCreateStatus.php index 34023eda..1e6ef957 100644 --- a/src/Internal/PersistentSubscriptionCreateStatus.php +++ b/src/Internal/PersistentSubscriptionCreateStatus.php @@ -15,7 +15,10 @@ use Prooph\EventStore\Exception\InvalidArgumentException; -/** @internal */ +/** + * @internal + * @psalm-immutable + */ class PersistentSubscriptionCreateStatus { public const OPTIONS = [ @@ -58,35 +61,39 @@ public static function byName(string $value): self throw new InvalidArgumentException('Unknown enum name given'); } - return self::{$value}(); + return new self($value); } - public static function byValue($value): self + public static function byValue(int $value): self { foreach (self::OPTIONS as $name => $v) { if ($v === $value) { - return self::{$name}(); + return new self($name); } } throw new InvalidArgumentException('Unknown enum value given'); } + /** @psalm-pure */ public function equals(PersistentSubscriptionCreateStatus $other): bool { return \get_class($this) === \get_class($other) && $this->name === $other->name; } + /** @psalm-pure */ public function name(): string { return $this->name; } - public function value() + /** @psalm-pure */ + public function value(): int { return $this->value; } + /** @psalm-pure */ public function __toString(): string { return $this->name; diff --git a/src/Internal/PersistentSubscriptionDeleteStatus.php b/src/Internal/PersistentSubscriptionDeleteStatus.php index 07be5563..0e14fd70 100644 --- a/src/Internal/PersistentSubscriptionDeleteStatus.php +++ b/src/Internal/PersistentSubscriptionDeleteStatus.php @@ -15,7 +15,10 @@ use Prooph\EventStore\Exception\InvalidArgumentException; -/** @internal */ +/** + * @internal + * @psalm-immutable + */ class PersistentSubscriptionDeleteStatus { public const OPTIONS = [ @@ -51,35 +54,39 @@ public static function byName(string $value): self throw new InvalidArgumentException('Unknown enum name given'); } - return self::{$value}(); + return new self($value); } - public static function byValue($value): self + public static function byValue(int $value): self { foreach (self::OPTIONS as $name => $v) { if ($v === $value) { - return self::{$name}(); + return new self($name); } } throw new InvalidArgumentException('Unknown enum value given'); } + /** @psalm-pure */ public function equals(PersistentSubscriptionDeleteStatus $other): bool { return \get_class($this) === \get_class($other) && $this->name === $other->name; } + /** @psalm-pure */ public function name(): string { return $this->name; } - public function value() + /** @psalm-pure */ + public function value(): int { return $this->value; } + /** @psalm-pure */ public function __toString(): string { return $this->name; diff --git a/src/Internal/PersistentSubscriptionUpdateStatus.php b/src/Internal/PersistentSubscriptionUpdateStatus.php index 5d2eae26..e067e373 100644 --- a/src/Internal/PersistentSubscriptionUpdateStatus.php +++ b/src/Internal/PersistentSubscriptionUpdateStatus.php @@ -15,7 +15,10 @@ use Prooph\EventStore\Exception\InvalidArgumentException; -/** @internal */ +/** + * @internal + * @psalm-immutable + */ class PersistentSubscriptionUpdateStatus { public const OPTIONS = [ @@ -58,35 +61,39 @@ public static function byName(string $value): self throw new InvalidArgumentException('Unknown enum name given'); } - return self::{$value}(); + return new self($value); } - public static function byValue($value): self + public static function byValue(int $value): self { foreach (self::OPTIONS as $name => $v) { if ($v === $value) { - return self::{$name}(); + return new self($name); } } throw new InvalidArgumentException('Unknown enum value given'); } + /** @psalm-pure */ public function equals(PersistentSubscriptionUpdateStatus $other): bool { return \get_class($this) === \get_class($other) && $this->name === $other->name; } + /** @psalm-pure */ public function name(): string { return $this->name; } - public function value() + /** @psalm-pure */ + public function value(): int { return $this->value; } + /** @psalm-pure */ public function __toString(): string { return $this->name; diff --git a/src/LiveProcessingStartedOnCatchUpSubscription.php b/src/LiveProcessingStartedOnCatchUpSubscription.php deleted file mode 100644 index e60aa76d..00000000 --- a/src/LiveProcessingStartedOnCatchUpSubscription.php +++ /dev/null @@ -1,19 +0,0 @@ - - * (c) 2015-2020 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Prooph\EventStore; - -interface LiveProcessingStartedOnCatchUpSubscription -{ - public function __invoke(EventStoreCatchUpSubscription $subscription): void; -} diff --git a/src/PersistentSubscriptionDropped.php b/src/PersistentSubscriptionDropped.php deleted file mode 100644 index 7aa810d0..00000000 --- a/src/PersistentSubscriptionDropped.php +++ /dev/null @@ -1,25 +0,0 @@ - - * (c) 2015-2020 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Prooph\EventStore; - -use Throwable; - -interface PersistentSubscriptionDropped -{ - public function __invoke( - EventStorePersistentSubscription $subscription, - SubscriptionDropReason $reason, - ?Throwable $exception = null - ): void; -} diff --git a/src/PersistentSubscriptionNakEventAction.php b/src/PersistentSubscriptionNakEventAction.php index da48df2a..59b9f0d5 100644 --- a/src/PersistentSubscriptionNakEventAction.php +++ b/src/PersistentSubscriptionNakEventAction.php @@ -15,6 +15,7 @@ use Prooph\EventStore\Exception\InvalidArgumentException; +/** @psalm-immutable */ class PersistentSubscriptionNakEventAction { public const OPTIONS = [ @@ -76,35 +77,39 @@ public static function byName(string $value): self throw new InvalidArgumentException('Unknown enum name given'); } - return self::{$value}(); + return new self($value); } - public static function byValue($value): self + public static function byValue(int $value): self { foreach (self::OPTIONS as $name => $v) { if ($v === $value) { - return self::{$name}(); + return new self($name); } } throw new InvalidArgumentException('Unknown enum value given'); } + /** @psalm-pure */ public function equals(PersistentSubscriptionNakEventAction $other): bool { return \get_class($this) === \get_class($other) && $this->name === $other->name; } + /** @psalm-pure */ public function name(): string { return $this->name; } - public function value() + /** @psalm-pure */ + public function value(): int { return $this->value; } + /** @psalm-pure */ public function __toString(): string { return $this->name; diff --git a/src/PersistentSubscriptions/PersistentSubscriptionConfigDetails.php b/src/PersistentSubscriptions/PersistentSubscriptionConfigDetails.php index d312e850..a24b046c 100644 --- a/src/PersistentSubscriptions/PersistentSubscriptionConfigDetails.php +++ b/src/PersistentSubscriptions/PersistentSubscriptionConfigDetails.php @@ -13,7 +13,10 @@ namespace Prooph\EventStore\PersistentSubscriptions; -/** @internal */ +/** + * @internal + * @psalm-immutable + */ final class PersistentSubscriptionConfigDetails { private bool $resolveLinktos; @@ -31,97 +34,140 @@ final class PersistentSubscriptionConfigDetails private string $namedConsumerStrategy; private bool $preferRoundRobin; - private function __construct() - { - } - + private function __construct( + bool $resolveLinktos, + int $startFrom, + int $messageTimeoutMilliseconds, + bool $extraStatistics, + int $maxRetryCount, + int $liveBufferSize, + int $bufferSize, + int $readBatchSize, + int $checkPointAfterMilliseconds, + int $minCheckPointCount, + int $maxCheckPointCount, + int $maxSubscriberCount, + string $namedConsumerStrategy, + bool $preferRoundRobin + ) { + $this->resolveLinktos = $resolveLinktos; + $this->startFrom = $startFrom; + $this->messageTimeoutMilliseconds = $messageTimeoutMilliseconds; + $this->extraStatistics = $extraStatistics; + $this->maxRetryCount = $maxRetryCount; + $this->liveBufferSize = $liveBufferSize; + $this->bufferSize = $bufferSize; + $this->readBatchSize = $readBatchSize; + $this->checkPointAfterMilliseconds = $checkPointAfterMilliseconds; + $this->minCheckPointCount = $minCheckPointCount; + $this->maxCheckPointCount = $maxCheckPointCount; + $this->maxSubscriberCount = $maxSubscriberCount; + $this->namedConsumerStrategy = $namedConsumerStrategy; + $this->preferRoundRobin = $preferRoundRobin; + } + + /** + * @param array $data + */ public static function fromArray(array $data): self { - $details = new self(); - - $details->resolveLinktos = $data['resolveLinktos']; - $details->startFrom = $data['startFrom']; - $details->messageTimeoutMilliseconds = $data['messageTimeoutMilliseconds']; - $details->extraStatistics = $data['extraStatistics']; - $details->maxRetryCount = $data['maxRetryCount']; - $details->liveBufferSize = $data['liveBufferSize']; - $details->bufferSize = $data['bufferSize']; - $details->readBatchSize = $data['readBatchSize']; - $details->checkPointAfterMilliseconds = $data['checkPointAfterMilliseconds']; - $details->minCheckPointCount = $data['minCheckPointCount']; - $details->maxCheckPointCount = $data['maxCheckPointCount']; - $details->maxSubscriberCount = $data['maxSubscriberCount']; - $details->namedConsumerStrategy = $data['namedConsumerStrategy']; - $details->preferRoundRobin = $data['preferRoundRobin']; - - return $details; - } - + return new self( + (bool) $data['resolveLinktos'], + (int) $data['startFrom'], + (int) $data['messageTimeoutMilliseconds'], + (bool) $data['extraStatistics'], + (int) $data['maxRetryCount'], + (int) $data['liveBufferSize'], + (int) $data['bufferSize'], + (int) $data['readBatchSize'], + (int) $data['checkPointAfterMilliseconds'], + (int) $data['minCheckPointCount'], + (int) $data['maxCheckPointCount'], + (int) $data['maxSubscriberCount'], + (string) $data['namedConsumerStrategy'], + (bool) $data['preferRoundRobin'] + ); + } + + /** @psalm-pure */ public function resolveLinktos(): bool { return $this->resolveLinktos; } + /** @psalm-pure */ public function startFrom(): int { return $this->startFrom; } + /** @psalm-pure */ public function messageTimeoutMilliseconds(): int { return $this->messageTimeoutMilliseconds; } + /** @psalm-pure */ public function extraStatistics(): bool { return $this->extraStatistics; } + /** @psalm-pure */ public function maxRetryCount(): int { return $this->maxRetryCount; } + /** @psalm-pure */ public function liveBufferSize(): int { return $this->liveBufferSize; } + /** @psalm-pure */ public function bufferSize(): int { return $this->bufferSize; } + /** @psalm-pure */ public function readBatchSize(): int { return $this->readBatchSize; } + /** @psalm-pure */ public function checkPointAfterMilliseconds(): int { return $this->checkPointAfterMilliseconds; } + /** @psalm-pure */ public function minCheckPointCount(): int { return $this->minCheckPointCount; } + /** @psalm-pure */ public function maxCheckPointCount(): int { return $this->maxCheckPointCount; } + /** @psalm-pure */ public function maxSubscriberCount(): int { return $this->maxSubscriberCount; } + /** @psalm-pure */ public function namedConsumerStrategy(): string { return $this->namedConsumerStrategy; } + /** @psalm-pure */ public function preferRoundRobin(): bool { return $this->preferRoundRobin; diff --git a/src/PersistentSubscriptions/PersistentSubscriptionConnectionDetails.php b/src/PersistentSubscriptions/PersistentSubscriptionConnectionDetails.php index 7ab94408..6bda2279 100644 --- a/src/PersistentSubscriptions/PersistentSubscriptionConnectionDetails.php +++ b/src/PersistentSubscriptions/PersistentSubscriptionConnectionDetails.php @@ -13,7 +13,10 @@ namespace Prooph\EventStore\PersistentSubscriptions; -/** @internal */ +/** + * @internal + * @psalm-immutable + */ final class PersistentSubscriptionConnectionDetails { private string $from; @@ -24,55 +27,78 @@ final class PersistentSubscriptionConnectionDetails private int $availableSlots; private int $inFlightMessages; - private function __construct() - { + private function __construct( + string $from, + string $username, + float $averageItemsPerSecond, + int $totalItemsProcessed, + int $countSinceLastMeasurement, + int $availableSlots, + int $inFlightMessages + ) { + $this->from = $from; + $this->username = $username; + $this->averageItemsPerSecond = $averageItemsPerSecond; + $this->totalItemsProcessed = $totalItemsProcessed; + $this->countSinceLastMeasurement = $countSinceLastMeasurement; + $this->availableSlots = $availableSlots; + $this->inFlightMessages = $inFlightMessages; } + /** + * @param array $data + * @psalm-pure + */ public static function fromArray(array $data): self { - $details = new self(); - - $details->from = $data['from']; - $details->username = $data['username']; - $details->averageItemsPerSecond = $data['averageItemsPerSecond']; - $details->totalItemsProcessed = $data['totalItemsProcessed']; - $details->countSinceLastMeasurement = $data['countSinceLastMeasurement']; - $details->availableSlots = $data['availableSlots']; - $details->inFlightMessages = $data['inFlightMessages']; - - return $details; + return new self( + (string) $data['from'], + (string) $data['username'], + (float) $data['averageItemsPerSecond'], + (int) $data['totalItemsProcessed'], + (int) $data['countSinceLastMeasurement'], + (int) $data['availableSlots'], + (int) $data['inFlightMessages'], + ); } + /** @psalm-pure */ public function from(): string { return $this->from; } + /** @psalm-pure */ public function username(): string { return $this->username; } + /** @psalm-pure */ public function averageItemsPerSecond(): float { return $this->averageItemsPerSecond; } + /** @psalm-pure */ public function totalItemsProcessed(): int { return $this->totalItemsProcessed; } + /** @psalm-pure */ public function countSinceLastMeasurement(): int { return $this->countSinceLastMeasurement; } + /** @psalm-pure */ public function availableSlots(): int { return $this->availableSlots; } + /** @psalm-pure */ public function inFlightMessages(): int { return $this->inFlightMessages; diff --git a/src/PersistentSubscriptions/PersistentSubscriptionDetails.php b/src/PersistentSubscriptions/PersistentSubscriptionDetails.php index e1c1d877..91886f7a 100644 --- a/src/PersistentSubscriptions/PersistentSubscriptionDetails.php +++ b/src/PersistentSubscriptions/PersistentSubscriptionDetails.php @@ -13,15 +13,16 @@ namespace Prooph\EventStore\PersistentSubscriptions; +/** @psalm-immutable */ final class PersistentSubscriptionDetails { /** * * Only populated when retrieved via PersistentSubscriptionsManager::describe() method. */ - private PersistentSubscriptionConfigDetails $config; + private ?PersistentSubscriptionConfigDetails $config; /** - * @var PersistentSubscriptionConfigDetails[] + * @var list * * Only populated when retrieved via PersistentSubscriptionsManager::describe() method. */ @@ -42,124 +43,186 @@ final class PersistentSubscriptionDetails private string $parkedMessageUri; private string $getMessagesUri; - private function __construct() - { + /** + * @param list $connections + */ + private function __construct( + ?PersistentSubscriptionConfigDetails $config, + array $connections, + string $eventStreamId, + string $groupName, + string $status, + float $averageItemsPerSecond, + int $totalItemsProcessed, + int $countSinceLastMeasurement, + int $lastProcessedEventNumber, + int $lastKnownEventNumber, + int $readBufferCount, + int $liveBufferCount, + int $retryBufferCount, + int $totalInFlightMessages, + int $connectionCount, + string $parkedMessageUri, + string $getMessagesUri + ) { + $this->config = $config; + $this->connections = $connections; + $this->eventStreamId = $eventStreamId; + $this->groupName = $groupName; + $this->status = $status; + $this->averageItemsPerSecond = $averageItemsPerSecond; + $this->totalItemsProcessed = $totalItemsProcessed; + $this->countSinceLastMeasurement = $countSinceLastMeasurement; + $this->lastProcessedEventNumber = $lastProcessedEventNumber; + $this->lastKnownEventNumber = $lastKnownEventNumber; + $this->readBufferCount = $readBufferCount; + $this->liveBufferCount = $liveBufferCount; + $this->retryBufferCount = $retryBufferCount; + $this->totalInFlightMessages = $totalInFlightMessages; + $this->connectionCount = $connectionCount; + $this->parkedMessageUri = $parkedMessageUri; + $this->getMessagesUri = $getMessagesUri; } public static function fromArray(array $data): self { - $details = new self(); + $config = null; if (isset($data['config'])) { - $details->config = PersistentSubscriptionConfigDetails::fromArray($data['config']); + /** @var array $data['config'] */ + $config = PersistentSubscriptionConfigDetails::fromArray($data['config']); } + $connections = []; + if (isset($data['connections'])) { + /** @var array $connection */ foreach ($data['connections'] as $connection) { - $details->connections[] = PersistentSubscriptionConnectionDetails::fromArray($connection); + $connections[] = PersistentSubscriptionConnectionDetails::fromArray($connection); } } - $details->eventStreamId = $data['eventStreamId']; - $details->groupName = $data['groupName']; - $details->status = $data['status']; - $details->averageItemsPerSecond = $data['averageItemsPerSecond']; - $details->totalItemsProcessed = $data['totalItemsProcessed']; - $details->countSinceLastMeasurement = $data['countSinceLastMeasurement'] ?? 0; - $details->lastProcessedEventNumber = $data['lastProcessedEventNumber']; - $details->lastKnownEventNumber = $data['lastKnownEventNumber']; - $details->readBufferCount = $data['readBufferCount'] ?? 0; - $details->liveBufferCount = $data['liveBufferCount'] ?? 0; - $details->retryBufferCount = $data['retryBufferCount'] ?? 0; - $details->totalInFlightMessages = $data['totalInFlightMessages']; - $details->parkedMessageUri = $data['parkedMessageUri']; - $details->getMessagesUri = $data['getMessagesUri']; - $details->connectionCount = $data['connectionCount'] ?? 0; - - return $details; - } - - public function config(): PersistentSubscriptionConfigDetails + return new self( + $config, + $connections, + (string) $data['eventStreamId'], + (string) $data['groupName'], + (string) $data['status'], + (float) $data['averageItemsPerSecond'], + (int) $data['totalItemsProcessed'], + (int) ($data['countSinceLastMeasurement'] ?? 0), + (int) $data['lastProcessedEventNumber'], + (int) $data['lastKnownEventNumber'], + (int) ($data['readBufferCount'] ?? 0), + (int) ($data['liveBufferCount'] ?? 0), + (int) ($data['retryBufferCount'] ?? 0), + (int) $data['totalInFlightMessages'], + (int) ($data['connectionCount'] ?? 0), + (string) $data['parkedMessageUri'], + (string) $data['getMessagesUri'] + ); + } + + /** @psalm-pure */ + public function config(): ?PersistentSubscriptionConfigDetails { return $this->config; } - /** @return PersistentSubscriptionConfigDetails[] */ + /** + * @return list + * @psalm-pure + */ public function connections(): array { return $this->connections; } + /** @psalm-pure */ public function eventStreamId(): string { return $this->eventStreamId; } + /** @psalm-pure */ public function groupName(): string { return $this->groupName; } + /** @psalm-pure */ public function status(): string { return $this->status; } + /** @psalm-pure */ public function averageItemsPerSecond(): float { return $this->averageItemsPerSecond; } + /** @psalm-pure */ public function totalItemsProcessed(): int { return $this->totalItemsProcessed; } + /** @psalm-pure */ public function countSinceLastMeasurement(): int { return $this->countSinceLastMeasurement; } + /** @psalm-pure */ public function lastProcessedEventNumber(): int { return $this->lastProcessedEventNumber; } + /** @psalm-pure */ public function lastKnownEventNumber(): int { return $this->lastKnownEventNumber; } + /** @psalm-pure */ public function readBufferCount(): int { return $this->readBufferCount; } + /** @psalm-pure */ public function liveBufferCount(): int { return $this->liveBufferCount; } + /** @psalm-pure */ public function retryBufferCount(): int { return $this->retryBufferCount; } + /** @psalm-pure */ public function totalInFlightMessages(): int { return $this->totalInFlightMessages; } + /** @psalm-pure */ public function connectionCount(): int { return $this->connectionCount; } + /** @psalm-pure */ public function parkedMessageUri(): string { return $this->parkedMessageUri; } + /** @psalm-pure */ public function getMessagesUri(): string { return $this->getMessagesUri; diff --git a/src/PersistentSubscriptions/PersistentSubscriptionsManager.php b/src/PersistentSubscriptions/PersistentSubscriptionsManager.php index 4ecbf3f4..2071d811 100644 --- a/src/PersistentSubscriptions/PersistentSubscriptionsManager.php +++ b/src/PersistentSubscriptions/PersistentSubscriptionsManager.php @@ -30,9 +30,7 @@ public function replayParkedMessages( ): void; /** - * @param null|string $stream - * @param null|UserCredentials $userCredentials - * @return PersistentSubscriptionDetails[] + * @return list */ public function list(?string $stream = null, ?UserCredentials $userCredentials = null): array; } diff --git a/src/Position.php b/src/Position.php index 4fbf87b0..33e5b14c 100644 --- a/src/Position.php +++ b/src/Position.php @@ -17,6 +17,8 @@ /** * Transaction File Position + * + * @psalm-immutable */ class Position { @@ -62,49 +64,58 @@ public static function parse(string $string): Position return new Position($commitPosition, $preparePosition); } + /** @psalm-pure */ public function commitPosition(): int { return $this->commitPosition; } + /** @psalm-pure */ public function preparePosition(): int { return $this->preparePosition; } + /** @psalm-pure */ public function asString(): string { return \substr('000000000000000' . \dechex($this->commitPosition), -16) . \substr('000000000000000' . \dechex($this->preparePosition), -16); } + /** @psalm-pure */ public function __toString(): string { return 'C:' . $this->commitPosition . '/P:' . $this->preparePosition; } + /** @psalm-pure */ public function equals(Position $other): bool { return $this->commitPosition === $other->commitPosition && $this->preparePosition === $other->preparePosition; } + /** @psalm-pure */ public function greater(Position $other): bool { return $this->commitPosition > $other->commitPosition || ($this->commitPosition === $other->commitPosition && $this->preparePosition > $other->preparePosition); } + /** @psalm-pure */ public function smaller(Position $other): bool { return $this->commitPosition < $other->commitPosition || ($this->commitPosition === $other->commitPosition && $this->preparePosition < $other->preparePosition); } + /** @psalm-pure */ public function greaterOrEquals(Position $other): bool { return $this->greater($other) || $this->equals($other); } + /** @psalm-pure */ public function smallerOrEquals(Position $other): bool { return $this->smaller($other) || $this->equals($other); diff --git a/src/Projections/ProjectionDetails.php b/src/Projections/ProjectionDetails.php index b3ef0b4e..2f997a45 100644 --- a/src/Projections/ProjectionDetails.php +++ b/src/Projections/ProjectionDetails.php @@ -13,6 +13,7 @@ namespace Prooph\EventStore\Projections; +/** @psalm-immutable */ final class ProjectionDetails { private int $coreProcessingTime; @@ -95,126 +96,151 @@ public function __construct( $this->writePendingEventsAfterCheckpoint = $writePendingEventsAfterCheckpoint; } + /** @psalm-pure */ public function coreProcessingTime(): int { return $this->coreProcessingTime; } + /** @psalm-pure */ public function version(): int { return $this->version; } + /** @psalm-pure */ public function epoch(): int { return $this->epoch; } + /** @psalm-pure */ public function effectiveName(): string { return $this->effectiveName; } + /** @psalm-pure */ public function writesInProgress(): int { return $this->writesInProgress; } + /** @psalm-pure */ public function readsInProgress(): int { return $this->readsInProgress; } + /** @psalm-pure */ public function partitionsCached(): int { return $this->partitionsCached; } + /** @psalm-pure */ public function status(): string { return $this->status; } + /** @psalm-pure */ public function stateReason(): ?string { return $this->stateReason; } + /** @psalm-pure */ public function name(): string { return $this->name; } + /** @psalm-pure */ public function mode(): string { return $this->mode; } + /** @psalm-pure */ public function position(): string { return $this->position; } + /** @psalm-pure */ public function progress(): float { return $this->progress; } + /** @psalm-pure */ public function lastCheckpoint(): ?string { return $this->lastCheckpoint; } + /** @psalm-pure */ public function eventsProcessedAfterRestart(): int { return $this->eventsProcessedAfterRestart; } + /** @psalm-pure */ public function statusUrl(): string { return $this->statusUrl; } + /** @psalm-pure */ public function stateUrl(): string { return $this->stateUrl; } + /** @psalm-pure */ public function resultUrl(): string { return $this->resultUrl; } + /** @psalm-pure */ public function queryUrl(): string { return $this->queryUrl; } + /** @psalm-pure */ public function enableCommandUrl(): string { return $this->enableCommandUrl; } + /** @psalm-pure */ public function disableCommandUrl(): string { return $this->disableCommandUrl; } + /** @psalm-pure */ public function checkpointStatus(): ?string { return $this->checkpointStatus; } + /** @psalm-pure */ public function bufferedEvents(): int { return $this->bufferedEvents; } + /** @psalm-pure */ public function writePendingEventsBeforeCheckpoint(): int { return $this->writePendingEventsBeforeCheckpoint; } + /** @psalm-pure */ public function writePendingEventsAfterCheckpoint(): int { return $this->writePendingEventsAfterCheckpoint; diff --git a/src/Projections/ProjectionsManager.php b/src/Projections/ProjectionsManager.php index 3ef0ed95..d42c6381 100644 --- a/src/Projections/ProjectionsManager.php +++ b/src/Projections/ProjectionsManager.php @@ -65,21 +65,21 @@ public function createContinuous( /** * Synchronously lists all projections * - * @return ProjectionDetails[] + * @return list */ public function listAll(?UserCredentials $userCredentials = null): array; /** * Synchronously lists all one-time projections * - * @return ProjectionDetails[] + * @return list */ public function listOneTime(?UserCredentials $userCredentials = null): array; /** * Synchronously lists this status of all continuous projections * - * @return ProjectionDetails[] + * @return list */ public function listContinuous(?UserCredentials $userCredentials = null): array; diff --git a/src/Projections/QueryManager.php b/src/Projections/QueryManager.php index b27b943f..6cf65ab5 100644 --- a/src/Projections/QueryManager.php +++ b/src/Projections/QueryManager.php @@ -23,15 +23,6 @@ interface QueryManager * Creates a new transient projection and polls its status until it is Completed * * returns String of JSON containing query result - * - * @param string $name A name for the query - * @param string $query The source code for the query - * @param int $initialPollingDelay Initial time to wait between polling for projection status - * @param int $maximumPollingDelay Maximum time to wait between polling for projection status - * @param string $type The type to use, defaults to JS - * @param UserCredentials|null $userCredentials Credentials for a user with permission to create a query - * - * @return string */ public function execute( string $name, diff --git a/src/ReadDirection.php b/src/ReadDirection.php index e767057a..7383117e 100644 --- a/src/ReadDirection.php +++ b/src/ReadDirection.php @@ -15,6 +15,7 @@ use Prooph\EventStore\Exception\InvalidArgumentException; +/** @psalm-immutable */ class ReadDirection { public const OPTIONS = [ @@ -50,14 +51,14 @@ public static function byName(string $value): self throw new InvalidArgumentException('Unknown enum name given'); } - return self::{$value}(); + return new self($value); } - public static function byValue($value): self + public static function byValue(int $value): self { foreach (self::OPTIONS as $name => $v) { if ($v === $value) { - return self::{$name}(); + return new self($name); } } @@ -69,16 +70,19 @@ public function equals(ReadDirection $other): bool return \get_class($this) === \get_class($other) && $this->name === $other->name; } + /** @psalm-pure */ public function name(): string { return $this->name; } - public function value() + /** @psalm-pure */ + public function value(): int { return $this->value; } + /** @psalm-pure */ public function __toString(): string { return $this->name; diff --git a/src/RecordedEvent.php b/src/RecordedEvent.php index 202a3cb8..08d5d31d 100644 --- a/src/RecordedEvent.php +++ b/src/RecordedEvent.php @@ -15,6 +15,7 @@ use DateTimeImmutable; +/** @psalm-immutable */ class RecordedEvent { protected string $eventStreamId; @@ -47,41 +48,49 @@ public function __construct( $this->created = $created; } + /** @psalm-pure */ public function eventStreamId(): string { return $this->eventStreamId; } + /** @psalm-pure */ public function eventNumber(): int { return $this->eventNumber; } + /** @psalm-pure */ public function eventId(): EventId { return $this->eventId; } + /** @psalm-pure */ public function eventType(): string { return $this->eventType; } + /** @psalm-pure */ public function isJson(): bool { return $this->isJson; } + /** @psalm-pure */ public function data(): string { return $this->data; } + /** @psalm-pure */ public function metadata(): string { return $this->metadata; } + /** @psalm-pure */ public function created(): DateTimeImmutable { return $this->created; diff --git a/src/ResolvedEvent.php b/src/ResolvedEvent.php index a503b40e..dbfd4c89 100644 --- a/src/ResolvedEvent.php +++ b/src/ResolvedEvent.php @@ -17,6 +17,8 @@ /** * A structure representing a single event or a resolved link event. + * + * @psalm-immutable */ class ResolvedEvent implements InternalResolvedEvent { @@ -53,38 +55,45 @@ public function __construct(?RecordedEvent $event, ?RecordedEvent $link, ?Positi $this->originalPosition = $originalPosition; } + /** @psalm-pure */ public function event(): ?RecordedEvent { return $this->event; } + /** @psalm-pure */ public function link(): ?RecordedEvent { return $this->link; } + /** @psalm-pure */ public function originalEvent(): ?RecordedEvent { return $this->originalEvent; } + /** @psalm-pure */ public function isResolved(): bool { return $this->isResolved; } + /** @psalm-pure */ public function originalPosition(): ?Position { return $this->originalPosition; } + /** @psalm-pure */ public function originalStreamName(): string { - return $this->originalEvent->eventStreamId(); + return null !== $this->originalEvent ? $this->originalEvent->eventStreamId() : ''; } + /** @psalm-pure */ public function originalEventNumber(): int { - return $this->originalEvent->eventNumber(); + return null !== $this->originalEvent ? $this->originalEvent->eventNumber() : 0; } } diff --git a/src/SliceReadStatus.php b/src/SliceReadStatus.php index e80a095b..3089d2a7 100644 --- a/src/SliceReadStatus.php +++ b/src/SliceReadStatus.php @@ -15,6 +15,7 @@ use Prooph\EventStore\Exception\InvalidArgumentException; +/** @psalm-immutable */ class SliceReadStatus { public const OPTIONS = [ @@ -57,14 +58,14 @@ public static function byName(string $value): self throw new InvalidArgumentException('Unknown enum name given'); } - return self::{$value}(); + return new self($value); } - public static function byValue($value): self + public static function byValue(int $value): self { foreach (self::OPTIONS as $name => $v) { if ($v === $value) { - return self::{$name}(); + return new self($name); } } @@ -76,16 +77,19 @@ public function equals(SliceReadStatus $other): bool return \get_class($this) === \get_class($other) && $this->name === $other->name; } + /** @psalm-pure */ public function name(): string { return $this->name; } - public function value() + /** @psalm-pure */ + public function value(): int { return $this->value; } + /** @psalm-pure */ public function __toString(): string { return $this->name; diff --git a/src/StreamAcl.php b/src/StreamAcl.php index 13e6f661..be996741 100644 --- a/src/StreamAcl.php +++ b/src/StreamAcl.php @@ -14,36 +14,42 @@ namespace Prooph\EventStore; use Prooph\EventStore\Common\SystemMetadata; -use Prooph\EventStore\Exception\InvalidArgumentException; class StreamAcl { /** * Roles and users permitted to read the stream - * @var string[] + * @var list */ private array $readRoles; /** * Roles and users permitted to write to the stream - * @var string[] + * @var list */ private array $writeRoles; /** * Roles and users permitted to delete the stream - * @var string[] + * @var list */ private array $deleteRoles; /** * Roles and users permitted to read stream metadata - * @var string[] + * @var list */ private array $metaReadRoles; /** * Roles and users permitted to write stream metadata - * @var string[] + * @var list */ private array $metaWriteRoles; + /** + * @param list $readRoles + * @param list $writeRoles + * @param list $deleteRoles + * @param list $metaReadRoles + * @param list $metaWriteRoles + */ public function __construct( array $readRoles = [], array $writeRoles = [], @@ -51,20 +57,6 @@ public function __construct( array $metaReadRoles = [], array $metaWriteRoles = [] ) { - $check = function (array $data): void { - foreach ($data as $value) { - if (! \is_string($value)) { - throw new InvalidArgumentException('Invalid roles given, expected an array of strings'); - } - } - }; - - $check($readRoles); - $check($writeRoles); - $check($deleteRoles); - $check($metaReadRoles); - $check($metaWriteRoles); - $this->readRoles = $readRoles; $this->writeRoles = $writeRoles; $this->deleteRoles = $deleteRoles; @@ -73,7 +65,7 @@ public function __construct( } /** - * @return string[] + * @return list */ public function readRoles(): array { @@ -81,7 +73,7 @@ public function readRoles(): array } /** - * @return string[] + * @return list */ public function writeRoles(): array { @@ -89,7 +81,7 @@ public function writeRoles(): array } /** - * @return string[] + * @return list */ public function deleteRoles(): array { @@ -97,7 +89,7 @@ public function deleteRoles(): array } /** - * @return string[] + * @return list */ public function metaReadRoles(): array { @@ -105,7 +97,7 @@ public function metaReadRoles(): array } /** - * @return string[] + * @return list */ public function metaWriteRoles(): array { @@ -139,6 +131,9 @@ public function toArray(): array return $data; } + /** + * @param array> $data + */ public static function fromArray(array $data): StreamAcl { return new self( @@ -150,6 +145,14 @@ public static function fromArray(array $data): StreamAcl ); } + /** + * @return list|string|null + * + * @psalm-pure + * @psalm-suppress MixedInferredReturnType + * @psalm-suppress MixedReturnStatement + * @psalm-suppress MixedReturnTypeCoercion + */ private function exportRoles(?array $roles) { if (null === $roles diff --git a/src/StreamEventsSlice.php b/src/StreamEventsSlice.php index 071fd15a..15ca6c34 100644 --- a/src/StreamEventsSlice.php +++ b/src/StreamEventsSlice.php @@ -13,19 +13,24 @@ namespace Prooph\EventStore; +/** @psalm-immutable */ class StreamEventsSlice { private SliceReadStatus $status; private string $stream; private int $fromEventNumber; private ReadDirection $readDirection; - /** @var ResolvedEvent[] */ + /** @var list */ private array $events; private int $nextEventNumber; private int $lastEventNumber; private bool $isEndOfStream; - /** @internal */ + /** + * @internal + * + * @param list $events + */ public function __construct( SliceReadStatus $status, string $stream, @@ -46,44 +51,53 @@ public function __construct( $this->isEndOfStream = $isEndOfStream; } + /** @psalm-pure */ public function status(): SliceReadStatus { return $this->status; } + /** @psalm-pure */ public function stream(): string { return $this->stream; } + /** @psalm-pure */ public function fromEventNumber(): int { return $this->fromEventNumber; } + /** @psalm-pure */ public function readDirection(): ReadDirection { return $this->readDirection; } /** - * @return ResolvedEvent[] + * @return list + * + * @psalm-pure */ public function events(): array { return $this->events; } + /** @psalm-pure */ public function nextEventNumber(): int { return $this->nextEventNumber; } + /** @psalm-pure */ public function lastEventNumber(): int { return $this->lastEventNumber; } + /** @psalm-pure */ public function isEndOfStream(): bool { return $this->isEndOfStream; diff --git a/src/StreamMetadata.php b/src/StreamMetadata.php index 382c5223..3dc9142e 100644 --- a/src/StreamMetadata.php +++ b/src/StreamMetadata.php @@ -44,9 +44,13 @@ class StreamMetadata implements JsonSerializable private ?StreamAcl $acl; /** * key => value pairs of custom metadata + * @var array */ private array $customMetadata; + /** + * @param array $customMetadata + */ public function __construct( ?int $maxCount = null, ?int $maxAge = null, @@ -71,12 +75,6 @@ public function __construct( throw new InvalidArgumentException('Cache control should be positive value'); } - foreach ($customMetadata as $key => $value) { - if (! \is_string($key)) { - throw new InvalidArgumentException('CustomMetadata key must be a string'); - } - } - $this->maxCount = $maxCount; $this->maxAge = $maxAge; $this->truncateBefore = $truncateBefore; @@ -116,7 +114,7 @@ public function acl(): ?StreamAcl } /** - * @return array + * @return array */ public function customMetadata(): array { @@ -124,7 +122,6 @@ public function customMetadata(): array } /** - * @param string $key * @return mixed */ public function getValue(string $key) @@ -164,6 +161,7 @@ public function jsonSerialize(): object } } + /** @psalm-suppress MixedAssignment */ foreach ($this->customMetadata as $key => $value) { $object->{$key} = $value; } @@ -171,6 +169,11 @@ public function jsonSerialize(): object return $object; } + /** + * @psalm-suppress PossiblyInvalidArgument + * @psalm-suppress MixedArgument + * @psalm-suppress MixedAssignment + */ public static function createFromArray(array $data): StreamMetadata { $internals = [ @@ -186,6 +189,7 @@ public static function createFromArray(array $data): StreamMetadata if (\in_array($key, $internals, true)) { $params[$key] = $value; } elseif ($key === SystemMetadata::ACL) { + /** @var array> $value */ $params[SystemMetadata::ACL] = StreamAcl::fromArray($value); } else { $params['customMetadata'][$key] = $value; diff --git a/src/StreamMetadataBuilder.php b/src/StreamMetadataBuilder.php index 520893d2..a6a0c2bf 100644 --- a/src/StreamMetadataBuilder.php +++ b/src/StreamMetadataBuilder.php @@ -21,13 +21,27 @@ class StreamMetadataBuilder private ?int $maxAge; private ?int $truncateBefore; private ?int $cacheControl; + /** @var list */ private array $aclRead; + /** @var list */ private array $aclWrite; + /** @var list */ private array $aclDelete; + /** @var list */ private array $aclMetaRead; + /** @var list */ private array $aclMetaWrite; + /** @var array */ private array $customMetadata; + /** + * @param list $aclRead + * @param list $aclWrite + * @param list $aclDelete + * @param list $aclMetaRead + * @param list $aclMetaWrite + * @param array $customMetadata + */ public function __construct( ?int $maxCount = null, ?int $maxAge = null, @@ -54,11 +68,11 @@ public function __construct( public function build(): StreamMetadata { - $acl = null === $this->aclRead - && null === $this->aclWrite - && null === $this->aclDelete - && null === $this->aclMetaRead - && null === $this->aclMetaWrite + $acl = empty($this->aclRead) + && empty($this->aclWrite) + && empty($this->aclDelete) + && empty($this->aclMetaRead) + && empty($this->aclMetaWrite) ? null : new StreamAcl($this->aclRead, $this->aclWrite, $this->aclDelete, $this->aclMetaRead, $this->aclMetaWrite); @@ -160,6 +174,9 @@ public function setMetadataWriteRoles(string ...$metaWriteRoles): StreamMetadata return $this; } + /** + * @param mixed $value + */ public function setCustomProperty(string $key, $value): StreamMetadataBuilder { $this->customMetadata[$key] = $value; diff --git a/src/SubscriptionDropReason.php b/src/SubscriptionDropReason.php index 41030b64..44069fb8 100644 --- a/src/SubscriptionDropReason.php +++ b/src/SubscriptionDropReason.php @@ -15,6 +15,7 @@ use Prooph\EventStore\Exception\InvalidArgumentException; +/** @psalm-immutable */ class SubscriptionDropReason { public const OPTIONS = [ @@ -127,35 +128,39 @@ public static function byName(string $value): self throw new InvalidArgumentException('Unknown enum name given'); } - return self::{$value}(); + return new self($value); } - public static function byValue($value): self + public static function byValue(int $value): self { foreach (self::OPTIONS as $name => $v) { if ($v === $value) { - return self::{$name}(); + return new self($name); } } throw new InvalidArgumentException('Unknown enum value given'); } + /** @psalm-pure */ public function equals(SubscriptionDropReason $other): bool { return \get_class($this) === \get_class($other) && $this->name === $other->name; } + /** @psalm-pure */ public function name(): string { return $this->name; } - public function value() + /** @psalm-pure */ + public function value(): int { return $this->value; } + /** @psalm-pure */ public function __toString(): string { return $this->name; diff --git a/src/SubscriptionDropped.php b/src/SubscriptionDropped.php deleted file mode 100644 index 623aafa4..00000000 --- a/src/SubscriptionDropped.php +++ /dev/null @@ -1,25 +0,0 @@ - - * (c) 2015-2020 Sascha-Oliver Prolic - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace Prooph\EventStore; - -use Throwable; - -interface SubscriptionDropped -{ - public function __invoke( - EventStoreSubscription $subscription, - SubscriptionDropReason $reason, - ?Throwable $exception = null - ): void; -} diff --git a/src/SystemSettings.php b/src/SystemSettings.php index de2fc134..faa542ae 100644 --- a/src/SystemSettings.php +++ b/src/SystemSettings.php @@ -82,6 +82,7 @@ public function jsonSerialize(): object public static function createFromArray(array $data): SystemSettings { + /** @var array>> $data */ return new self( isset($data[SystemMetadata::USER_STREAM_ACL]) ? StreamAcl::fromArray($data[SystemMetadata::USER_STREAM_ACL]) diff --git a/src/Transport/Http/EndpointExtensions.php b/src/Transport/Http/EndpointExtensions.php index ba83ed99..4368946e 100644 --- a/src/Transport/Http/EndpointExtensions.php +++ b/src/Transport/Http/EndpointExtensions.php @@ -38,7 +38,7 @@ public static function formatStringToHttpUrl( EndPoint $endPoint, string $schema, string $formatString, - ...$args + string ...$args ): string { return self::createHttpUrl( $schema, diff --git a/src/UserManagement/RelLink.php b/src/UserManagement/RelLink.php index 6659f1ab..62100903 100644 --- a/src/UserManagement/RelLink.php +++ b/src/UserManagement/RelLink.php @@ -13,6 +13,9 @@ namespace Prooph\EventStore\UserManagement; +/** + * @psalm-immutable + */ class RelLink { private string $href; @@ -24,11 +27,13 @@ public function __construct(string $href, string $rel) $this->rel = $rel; } + /** @psalm-pure */ public function href(): string { return $this->href; } + /** @psalm-pure */ public function rel(): string { return $this->rel; diff --git a/src/UserManagement/UserCreationInformation.php b/src/UserManagement/UserCreationInformation.php index dd8d67a3..385e9d54 100644 --- a/src/UserManagement/UserCreationInformation.php +++ b/src/UserManagement/UserCreationInformation.php @@ -20,10 +20,13 @@ final class UserCreationInformation implements JsonSerializable { private string $loginName; private string $fullName; - /** @var string[] */ + /** @var list */ private array $groups; private string $password; + /** + * @param list $groups + */ public function __construct(string $loginName, string $fullName, array $groups, string $password) { $this->loginName = $loginName; diff --git a/src/UserManagement/UserDetails.php b/src/UserManagement/UserDetails.php index 73af7dec..fe4a5244 100644 --- a/src/UserManagement/UserDetails.php +++ b/src/UserManagement/UserDetails.php @@ -17,79 +17,112 @@ use Prooph\EventStore\Exception\RuntimeException; use Prooph\EventStore\Util\DateTime; -/** @internal */ +/** @psalm-immutable */ final class UserDetails { private string $loginName; private string $fullName; - /** @var string[] */ + /** @var list */ private array $groups = []; private ?DateTimeImmutable $dateLastUpdated = null; private bool $disabled; - /** @var RelLink[] */ + /** @var list */ private array $links = []; - private function __construct() + /** + * @param list $groups + * @param list $links + */ + private function __construct( + string $loginName, + string $fullName, + array $groups, + ?DateTimeImmutable $dateLastUpdated, + bool $disabled, + array $links) { + $this->loginName = $loginName; + $this->fullName = $fullName; + $this->groups = $groups; + $this->dateLastUpdated = $dateLastUpdated; + $this->disabled = $disabled; + $this->links = $links; } + /** @internal */ public static function fromArray(array $data): self { - $details = new self(); - - $details->loginName = $data['loginName']; - $details->fullName = $data['fullName']; - $details->groups = $data['groups']; - $details->disabled = $data['disabled']; - - $details->dateLastUpdated = isset($data['dateLastUpdated']) - ? DateTime::create($data['dateLastUpdated']) + $dateLastUpdated = isset($data['dateLastUpdated']) + ? DateTime::create((string) $data['dateLastUpdated']) : null; $links = []; + if (isset($data['links'])) { + /** @var list> $data['links'] */ foreach ($data['links'] as $link) { - $links[] = new RelLink($link['href'], $link['rel']); + $links[] = new RelLink((string) $link['href'], (string) $link['rel']); } } - $details->links = $links; - return $details; + /** @var list $data['groups'] */ + + return new self( + (string) $data['loginName'], + (string) $data['fullName'], + $data['groups'], + $dateLastUpdated, + (bool) $data['disabled'], + $links + ); } + /** @psalm-pure */ public function loginName(): string { return $this->loginName; } + /** @psalm-pure */ public function fullName(): string { return $this->fullName; } - /** @return string[] */ + /** + * @return list + * @psalm-pure + */ public function groups(): array { return $this->groups; } + /** @psalm-pure */ public function dateLastUpdated(): ?DateTimeImmutable { return $this->dateLastUpdated; } + /** @psalm-pure */ public function disabled(): bool { return $this->disabled; } - /** @return RelLink[] */ + /** + * @return list + * @psalm-pure + */ public function links(): array { return $this->links; } - /** @throws RuntimeException if rel not found */ + /** + * @throws RuntimeException if rel not found + * @psalm-pure + */ public function getRelLink(string $rel): string { $rel = \strtolower($rel); diff --git a/src/UserManagement/UserUpdateInformation.php b/src/UserManagement/UserUpdateInformation.php index 55dc9370..dff0aa12 100644 --- a/src/UserManagement/UserUpdateInformation.php +++ b/src/UserManagement/UserUpdateInformation.php @@ -16,13 +16,20 @@ use JsonSerializable; use stdClass; -/** @internal */ +/** + * @internal + */ class UserUpdateInformation implements JsonSerializable { private string $fullName; - /** @var string[] */ + /** @var list */ private array $groups; + /** + * @param list $groups + * + * @psalm-pure + */ public function __construct(string $fullName, array $groups) { $this->fullName = $fullName; diff --git a/src/UserManagement/UsersManager.php b/src/UserManagement/UsersManager.php index dd60af6f..e3773132 100644 --- a/src/UserManagement/UsersManager.php +++ b/src/UserManagement/UsersManager.php @@ -25,7 +25,7 @@ public function disable(string $login, ?UserCredentials $userCredentials = null) /** @throws UserCommandFailed */ public function deleteUser(string $login, ?UserCredentials $userCredentials = null): void; - /** @return UserDetails[] */ + /** @return list */ public function listAll(?UserCredentials $userCredentials = null): array; public function getCurrentUser(?UserCredentials $userCredentials = null): UserDetails; @@ -33,13 +33,7 @@ public function getCurrentUser(?UserCredentials $userCredentials = null): UserDe public function getUser(string $login, ?UserCredentials $userCredentials = null): UserDetails; /** - * @param string $login - * @param string $fullName - * @param string[] $groups - * @param string $password - * @param UserCredentials|null $userCredentials - * - * @return void + * @param list $groups */ public function createUser( string $login, @@ -50,12 +44,7 @@ public function createUser( ): void; /** - * @param string $login - * @param string $fullName - * @param string[] $groups - * @param UserCredentials|null $userCredentials - * - * @return void + * @param list $groups */ public function updateUser( string $login, diff --git a/src/Util/DateTime.php b/src/Util/DateTime.php index b0b93aff..32a73b03 100644 --- a/src/Util/DateTime.php +++ b/src/Util/DateTime.php @@ -24,6 +24,7 @@ public static function utcNow(): DateTimeImmutable return new DateTimeImmutable('now', new DateTimeZone('UTC')); } + /** @psalm-pure */ public static function create(string $dateTimeString): DateTimeImmutable { $dateTime = DateTimeImmutable::createFromFormat( @@ -44,6 +45,7 @@ public static function create(string $dateTimeString): DateTimeImmutable return $dateTime; } + /** @psalm-pure */ public static function format(DateTimeImmutable $dateTime): string { return $dateTime->format('Y-m-d\TH:i:s.uP'); diff --git a/src/Util/Json.php b/src/Util/Json.php index c75de026..f735db8e 100644 --- a/src/Util/Json.php +++ b/src/Util/Json.php @@ -17,7 +17,6 @@ class Json { /** * @param mixed $value - * @return string */ public static function encode($value): string { @@ -27,7 +26,6 @@ public static function encode($value): string } /** - * @param string $json * @return mixed */ public static function decode(string $json)