From e6c4cc04da78b9267553a27f48ddd73baf9a4789 Mon Sep 17 00:00:00 2001 From: azjezz Date: Wed, 3 Apr 2024 18:48:29 +0100 Subject: [PATCH] chore: miscellaneous changes Signed-off-by: azjezz --- docs/component/class.md | 1 + docs/component/env.md | 4 +- docs/component/network.md | 2 +- docs/component/option.md | 6 +- docs/component/random-sequence.md | 2 +- src/Psl/Async/Awaitable.php | 4 +- src/Psl/Async/Deferred.php | 6 +- src/Psl/Async/Internal/State.php | 4 ++ src/Psl/Async/OptionalIncrementalTimeout.php | 2 - src/Psl/Async/Scheduler.php | 2 +- src/Psl/Channel/ChannelInterface.php | 10 +-- .../Channel/Internal/BoundedChannelState.php | 29 ++++++--- src/Psl/Channel/Internal/ChannelSideTrait.php | 17 ++++- .../Internal/UnboundedChannelState.php | 16 +++-- src/Psl/Class/is_readonly.php | 18 ++++++ .../Exception/OutOfBoundsException.php | 3 + src/Psl/Collection/Map.php | 15 +++-- src/Psl/Collection/MutableMap.php | 25 ++++++-- src/Psl/Collection/MutableVector.php | 17 ++++- src/Psl/Collection/Vector.php | 9 ++- src/Psl/DataStructure/PriorityQueue.php | 10 +-- src/Psl/DataStructure/Queue.php | 10 +-- src/Psl/DataStructure/Stack.php | 10 +-- src/Psl/Env/join_paths.php | 2 + src/Psl/Env/split_paths.php | 2 + src/Psl/File/Lock.php | 1 + src/Psl/Hash/Context.php | 2 +- src/Psl/IO/MemoryHandle.php | 24 +++++++ src/Psl/Internal/Loader.php | 1 + src/Psl/Iter/Iterator.php | 16 ++--- src/Psl/Locale/Locale.php | 16 ++--- src/Psl/Network/Address.php | 59 ++++++++++++++--- src/Psl/Network/Internal/get_peer_name.php | 6 +- src/Psl/Network/Internal/get_sock_name.php | 3 + src/Psl/Network/SocketOptions.php | 30 +++++---- src/Psl/Option/Option.php | 41 +++++++++--- src/Psl/Option/from_nullable.php | 2 + src/Psl/Option/none.php | 2 + src/Psl/Option/some.php | 2 + src/Psl/Password/Algorithm.php | 2 +- src/Psl/Password/hash.php | 1 - src/Psl/Password/needs_rehash.php | 1 - .../Internal/MersenneTwisterTrait.php | 2 +- src/Psl/RandomSequence/SecureSequence.php | 18 ++++-- src/Psl/Range/BetweenRange.php | 63 ++++++++++--------- src/Psl/Range/FromRange.php | 24 +++---- src/Psl/Range/FullRange.php | 2 +- src/Psl/Range/LowerBoundRangeInterface.php | 4 +- src/Psl/Range/RangeInterface.php | 2 +- src/Psl/Range/ToRange.php | 33 +++++----- src/Psl/Range/UpperBoundRangeInterface.php | 4 +- src/Psl/Ref.php | 13 ++-- src/Psl/Regex/every_match.php | 8 +-- src/Psl/Regex/first_match.php | 8 +-- src/Psl/Result/Failure.php | 15 +++-- src/Psl/Result/Stats.php | 41 +++++++++--- src/Psl/Result/Success.php | 6 +- src/Psl/TCP/ConnectOptions.php | 28 +++++---- src/Psl/TCP/ServerOptions.php | 40 +++++++----- src/Psl/Unix/connect.php | 2 +- tests/unit/Option/SomeTest.php | 9 --- 61 files changed, 501 insertions(+), 256 deletions(-) create mode 100644 src/Psl/Class/is_readonly.php diff --git a/docs/component/class.md b/docs/component/class.md index ff36d90e..fe4df9d9 100644 --- a/docs/component/class.md +++ b/docs/component/class.md @@ -18,5 +18,6 @@ - [has_method](./../../src/Psl/Class/has_method.php#L14) - [is_abstract](./../../src/Psl/Class/is_abstract.php#L14) - [is_final](./../../src/Psl/Class/is_final.php#L14) +- [is_readonly](./../../src/Psl/Class/is_readonly.php#L14) diff --git a/docs/component/env.md b/docs/component/env.md index 0e9f546f..8269e6b9 100644 --- a/docs/component/env.md +++ b/docs/component/env.md @@ -17,11 +17,11 @@ - [current_exec](./../../src/Psl/Env/current_exec.php#L12) - [get_var](./../../src/Psl/Env/get_var.php#L19) - [get_vars](./../../src/Psl/Env/get_vars.php#L14) -- [join_paths](./../../src/Psl/Env/join_paths.php#L18) +- [join_paths](./../../src/Psl/Env/join_paths.php#L20) - [remove_var](./../../src/Psl/Env/remove_var.php#L19) - [set_current_dir](./../../src/Psl/Env/set_current_dir.php#L16) - [set_var](./../../src/Psl/Env/set_var.php#L20) -- [split_paths](./../../src/Psl/Env/split_paths.php#L16) +- [split_paths](./../../src/Psl/Env/split_paths.php#L18) - [temp_dir](./../../src/Psl/Env/temp_dir.php#L21) diff --git a/docs/component/network.md b/docs/component/network.md index c6dbeaee..60089787 100644 --- a/docs/component/network.md +++ b/docs/component/network.md @@ -19,7 +19,7 @@ #### `Classes` -- [Address](./../../src/Psl/Network/Address.php#L7) +- [Address](./../../src/Psl/Network/Address.php#L10) - [SocketOptions](./../../src/Psl/Network/SocketOptions.php#L14) #### `Enums` diff --git a/docs/component/option.md b/docs/component/option.md index 03e95348..6595813a 100644 --- a/docs/component/option.md +++ b/docs/component/option.md @@ -12,9 +12,9 @@ #### `Functions` -- [from_nullable](./../../src/Psl/Option/from_nullable.php#L16) -- [none](./../../src/Psl/Option/none.php#L12) -- [some](./../../src/Psl/Option/some.php#L16) +- [from_nullable](./../../src/Psl/Option/from_nullable.php#L18) +- [none](./../../src/Psl/Option/none.php#L14) +- [some](./../../src/Psl/Option/some.php#L18) #### `Classes` diff --git a/docs/component/random-sequence.md b/docs/component/random-sequence.md index b2702ead..bb080dff 100644 --- a/docs/component/random-sequence.md +++ b/docs/component/random-sequence.md @@ -18,6 +18,6 @@ - [MersenneTwisterPHPVariantSequence](./../../src/Psl/RandomSequence/MersenneTwisterPHPVariantSequence.php#L10) - [MersenneTwisterSequence](./../../src/Psl/RandomSequence/MersenneTwisterSequence.php#L10) -- [SecureSequence](./../../src/Psl/RandomSequence/SecureSequence.php#L15) +- [SecureSequence](./../../src/Psl/RandomSequence/SecureSequence.php#L13) diff --git a/src/Psl/Async/Awaitable.php b/src/Psl/Async/Awaitable.php index 3b7b9d0a..1d8ab514 100644 --- a/src/Psl/Async/Awaitable.php +++ b/src/Psl/Async/Awaitable.php @@ -27,7 +27,7 @@ * * @implements PromiseInterface */ -final class Awaitable implements PromiseInterface +final readonly class Awaitable implements PromiseInterface { private State $state; @@ -117,6 +117,8 @@ public static function error(Throwable $throwable): self /** * @return bool True if the operation has completed. + * + * @psalm-mutation-free */ public function isComplete(): bool { diff --git a/src/Psl/Async/Deferred.php b/src/Psl/Async/Deferred.php index 62e98dbb..d4dd77ce 100644 --- a/src/Psl/Async/Deferred.php +++ b/src/Psl/Async/Deferred.php @@ -18,7 +18,7 @@ * * @template T */ -final class Deferred +final readonly class Deferred { /** * @var Internal\State @@ -62,6 +62,8 @@ public function error(Throwable $throwable): void /** * @return bool True if the operation has completed. + * + * @psalm-mutation-free */ public function isComplete(): bool { @@ -70,6 +72,8 @@ public function isComplete(): bool /** * @return Awaitable The awaitable associated with this Deferred. + * + * @psalm-mutation-free */ public function getAwaitable(): Awaitable { diff --git a/src/Psl/Async/Internal/State.php b/src/Psl/Async/Internal/State.php index 70d404f1..1d1e1aac 100644 --- a/src/Psl/Async/Internal/State.php +++ b/src/Psl/Async/Internal/State.php @@ -133,6 +133,8 @@ public function error(Throwable $throwable): void /** * Suppress the `Throwable`s thrown to the loop error handler if and operation error is not handled by a callback. + * + * @psalm-external-mutation-free */ public function ignore(): void { @@ -141,6 +143,8 @@ public function ignore(): void /** * @return bool True if the operation has completed. + * + * @psalm-mutation-free */ public function isComplete(): bool { diff --git a/src/Psl/Async/OptionalIncrementalTimeout.php b/src/Psl/Async/OptionalIncrementalTimeout.php index 99a7349d..4aa89451 100644 --- a/src/Psl/Async/OptionalIncrementalTimeout.php +++ b/src/Psl/Async/OptionalIncrementalTimeout.php @@ -46,8 +46,6 @@ public function __construct(?float $timeout, Closure $handler) * If the timeout has already been exceeded, the handler is invoked, and its return value is provided. * * @return float|null The remaining time in seconds, null if no timeout is set, or the handler's return value if the timeout is exceeded. - * - * @external-mutation-free */ public function getRemaining(): ?float { diff --git a/src/Psl/Async/Scheduler.php b/src/Psl/Async/Scheduler.php index 60af0867..75c79dad 100644 --- a/src/Psl/Async/Scheduler.php +++ b/src/Psl/Async/Scheduler.php @@ -18,7 +18,7 @@ * * @codeCoverageIgnore */ -final class Scheduler +final readonly class Scheduler { private function __construct() { diff --git a/src/Psl/Channel/ChannelInterface.php b/src/Psl/Channel/ChannelInterface.php index b043e9d7..4f7afa3d 100644 --- a/src/Psl/Channel/ChannelInterface.php +++ b/src/Psl/Channel/ChannelInterface.php @@ -16,7 +16,7 @@ interface ChannelInterface extends Countable * * @return null|positive-int * - * @mutation-free + * @psalm-mutation-free */ public function getCapacity(): ?int; @@ -30,7 +30,7 @@ public function close(): void; /** * Returns true if the channel is closed. * - * @mutation-free + * @psalm-mutation-free */ public function isClosed(): bool; @@ -39,7 +39,7 @@ public function isClosed(): bool; * * @return int<0, max> * - * @mutation-free + * @psalm-mutation-free */ public function count(): int; @@ -48,14 +48,14 @@ public function count(): int; * * Unbounded channels are never full. * - * @mutation-free + * @psalm-mutation-free */ public function isFull(): bool; /** * Returns true if the channel is empty. * - * @mutation-free + * @psalm-mutation-free */ public function isEmpty(): bool; } diff --git a/src/Psl/Channel/Internal/BoundedChannelState.php b/src/Psl/Channel/Internal/BoundedChannelState.php index 889e5798..77289bf8 100644 --- a/src/Psl/Channel/Internal/BoundedChannelState.php +++ b/src/Psl/Channel/Internal/BoundedChannelState.php @@ -37,16 +37,24 @@ final class BoundedChannelState implements ChannelInterface */ private array $messages = []; + /** + * @var int<0, max> + */ private int $size = 0; public bool $closed = false; + /** + * @var positive-int + */ + private readonly int $capacity; + /** * @param positive-int $capacity */ - public function __construct( - private int $capacity - ) { + public function __construct(int $capacity) + { + $this->capacity = $capacity; } public function __destruct() @@ -71,7 +79,9 @@ public function waitForMessage(Suspension $suspension): void } /** - * {@inheritDoc} + * @return positive-int + * + * @psalm-mutation-free */ public function getCapacity(): int { @@ -99,7 +109,7 @@ public function close(): void } /** - * {@inheritDoc} + * @psalm-mutation-free */ public function isClosed(): bool { @@ -107,7 +117,9 @@ public function isClosed(): bool } /** - * {@inheritDoc} + * @return int<0, max> + * + * @psalm-mutation-free */ public function count(): int { @@ -115,7 +127,7 @@ public function count(): int } /** - * {@inheritDoc} + * @psalm-mutation-free */ public function isFull(): bool { @@ -123,7 +135,7 @@ public function isFull(): bool } /** - * {@inheritDoc} + * @psalm-mutation-free */ public function isEmpty(): bool { @@ -171,6 +183,7 @@ public function receive(): mixed } $item = array_shift($this->messages); + /** @psalm-suppress InvalidPropertyAssignmentValue - The size is always in sync with messages */ $this->size--; if ($suspension = array_shift($this->waitingForSpace)) { diff --git a/src/Psl/Channel/Internal/ChannelSideTrait.php b/src/Psl/Channel/Internal/ChannelSideTrait.php index 3776d7a1..33ce5f6b 100644 --- a/src/Psl/Channel/Internal/ChannelSideTrait.php +++ b/src/Psl/Channel/Internal/ChannelSideTrait.php @@ -15,7 +15,11 @@ trait ChannelSideTrait protected UnboundedChannelState|BoundedChannelState $state; /** - * @return int<1, max>|null + * Returns the channel capacity if it’s bounded. + * + * @return null|positive-int + * + * @psalm-mutation-free */ public function getCapacity(): ?int { @@ -28,6 +32,9 @@ public function close(): void $this->state->close(); } + /** + * @psalm-mutation-free + */ public function isClosed(): bool { return $this->state->isClosed(); @@ -35,6 +42,8 @@ public function isClosed(): bool /** * @return int<0, max> + * + * @psalm-mutation-free */ public function count(): int { @@ -42,11 +51,17 @@ public function count(): int return $this->state->count(); } + /** + * @psalm-mutation-free + */ public function isFull(): bool { return $this->state->isFull(); } + /** + * @psalm-mutation-free + */ public function isEmpty(): bool { return $this->state->isEmpty(); diff --git a/src/Psl/Channel/Internal/UnboundedChannelState.php b/src/Psl/Channel/Internal/UnboundedChannelState.php index c537cba5..f43e06df 100644 --- a/src/Psl/Channel/Internal/UnboundedChannelState.php +++ b/src/Psl/Channel/Internal/UnboundedChannelState.php @@ -44,9 +44,11 @@ public function waitForMessage(Suspension $suspension): void } /** - * {@inheritDoc} + * @return null + * + * @psalm-mutation-free */ - public function getCapacity(): ?int + public function getCapacity(): null { return null; } @@ -66,7 +68,7 @@ public function close(): void } /** - * {@inheritDoc} + * @psalm-mutation-free */ public function isClosed(): bool { @@ -74,7 +76,9 @@ public function isClosed(): bool } /** - * {@inheritDoc} + * @return int<0, max> + * + * @psalm-mutation-free */ public function count(): int { @@ -82,7 +86,7 @@ public function count(): int } /** - * {@inheritDoc} + * @psalm-mutation-free */ public function isFull(): bool { @@ -90,7 +94,7 @@ public function isFull(): bool } /** - * {@inheritDoc} + * @psalm-mutation-free */ public function isEmpty(): bool { diff --git a/src/Psl/Class/is_readonly.php b/src/Psl/Class/is_readonly.php new file mode 100644 index 00000000..d127bfcb --- /dev/null +++ b/src/Psl/Class/is_readonly.php @@ -0,0 +1,18 @@ +isReadOnly(); +} diff --git a/src/Psl/Collection/Exception/OutOfBoundsException.php b/src/Psl/Collection/Exception/OutOfBoundsException.php index 693a2eb4..94c8630c 100644 --- a/src/Psl/Collection/Exception/OutOfBoundsException.php +++ b/src/Psl/Collection/Exception/OutOfBoundsException.php @@ -9,6 +9,9 @@ final class OutOfBoundsException extends Exception\OutOfBoundsException implements ExceptionInterface { + /** + * @psalm-mutation-free + */ public static function for(string|int $offset): OutOfBoundsException { return new self(Str\format('Key (%s) was out-of-bounds.', $offset)); diff --git a/src/Psl/Collection/Map.php b/src/Psl/Collection/Map.php index aee6b319..52f4f97c 100644 --- a/src/Psl/Collection/Map.php +++ b/src/Psl/Collection/Map.php @@ -22,16 +22,21 @@ * * @implements MapInterface */ -final class Map implements MapInterface +final readonly class Map implements MapInterface { + /** + * @var array $elements + */ + private array $elements; + /** * @param array $elements * - * @pure + * @psalm-mutation-free */ - public function __construct( - private readonly array $elements - ) { + public function __construct(array $elements) + { + $this->elements = $elements; } /** diff --git a/src/Psl/Collection/MutableMap.php b/src/Psl/Collection/MutableMap.php index 10faa92d..581a2286 100644 --- a/src/Psl/Collection/MutableMap.php +++ b/src/Psl/Collection/MutableMap.php @@ -24,14 +24,19 @@ */ final class MutableMap implements MutableMapInterface { + /** + * @var array $elements + */ + private array $elements; + /** * @param array $elements * - * @pure + * @psalm-mutation-free */ - public function __construct( - private array $elements - ) { + public function __construct(array $elements) + { + $this->elements = $elements; } /** @@ -570,6 +575,8 @@ static function (MutableVector $vector): MutableMap { * @throws Exception\OutOfBoundsException If $k is out-of-bounds. * * @return MutableMap Returns itself + * + * @psalm-external-mutation-free */ public function set(int|string $k, mixed $v): MutableMap { @@ -596,6 +603,8 @@ public function set(int|string $k, mixed $v): MutableMap * @param array $elements The elements with the new values to set * * @return MutableMap Returns itself + * + * @psalm-external-mutation-free */ public function setAll(array $elements): MutableMap { @@ -613,6 +622,8 @@ public function setAll(array $elements): MutableMap * @param Tv $v The value to set * * @return MutableMap Returns itself + * + * @psalm-external-mutation-free */ public function add(int|string $k, mixed $v): MutableMap { @@ -627,6 +638,8 @@ public function add(int|string $k, mixed $v): MutableMap * @param array $elements The elements with the new values to add. * * @return MutableMap Returns itself. + * + * @psalm-external-mutation-free */ public function addAll(array $elements): MutableMap { @@ -650,6 +663,8 @@ public function addAll(array $elements): MutableMap * @param Tk $k The key to remove. * * @return MutableMap Returns itself. + * + * @psalm-external-mutation-free */ public function remove(int|string $k): MutableMap { @@ -664,6 +679,8 @@ public function remove(int|string $k): MutableMap * Removes all elements from the map. * * @return MutableMap + * + * @psalm-external-mutation-free */ public function clear(): MutableMap { diff --git a/src/Psl/Collection/MutableVector.php b/src/Psl/Collection/MutableVector.php index d5e59c78..fe0e3d2b 100644 --- a/src/Psl/Collection/MutableVector.php +++ b/src/Psl/Collection/MutableVector.php @@ -32,7 +32,7 @@ final class MutableVector implements MutableVectorInterface * * @param array $elements * - * @external-mutation-free + * @psalm-mutation-free */ public function __construct(array $elements) { @@ -46,7 +46,7 @@ public function __construct(array $elements) * * @return static A default instance of {@see MutableVector}. * - * @external-mutation-free + * @psalm-external-mutation-free */ public static function default(): static { @@ -66,7 +66,6 @@ public static function default(): static */ public static function fromArray(array $elements): MutableVector { - /** @psalm-suppress ImpureMethodCall - conditionally pure */ return new self($elements); } @@ -269,6 +268,8 @@ public function linearSearch(mixed $search_value): ?int * @throws Exception\OutOfBoundsException If $k is out-of-bounds. * * @return MutableVector returns itself + * + * @psalm-external-mutation-free */ public function set(int|string $k, mixed $v): MutableVector { @@ -295,6 +296,8 @@ public function set(int|string $k, mixed $v): MutableVector * @param array, T> $elements The elements with the new values to set * * @return MutableVector returns itself + * + * @psalm-external-mutation-free */ public function setAll(array $elements): MutableVector { @@ -321,6 +324,8 @@ public function setAll(array $elements): MutableVector * @param int<0, max> $k The key to remove. * * @return MutableVector returns itself. + * + * @psalm-external-mutation-free */ public function remove(int|string $k): MutableVector { @@ -337,6 +342,8 @@ public function remove(int|string $k): MutableVector * Removes all elements from the vector. * * @return MutableVector Returns itself + * + * @psalm-external-mutation-free */ public function clear(): MutableVector { @@ -351,6 +358,8 @@ public function clear(): MutableVector * @param T $v The value to add. * * @return MutableVector Returns itself. + * + * @psalm-external-mutation-free */ public function add(mixed $v): MutableVector { @@ -365,6 +374,8 @@ public function add(mixed $v): MutableVector * @param array $elements The elements with the new values to add * * @return MutableVector returns itself. + * + * @psalm-external-mutation-free */ public function addAll(array $elements): MutableVector { diff --git a/src/Psl/Collection/Vector.php b/src/Psl/Collection/Vector.php index 527adadd..56095ce6 100644 --- a/src/Psl/Collection/Vector.php +++ b/src/Psl/Collection/Vector.php @@ -19,17 +19,17 @@ * * @implements VectorInterface */ -final class Vector implements VectorInterface +final readonly class Vector implements VectorInterface { /** * @var list $elements */ - private readonly array $elements; + private array $elements; /** * @param array $elements * - * @external-mutation-free + * @psalm-mutation-free */ public function __construct(array $elements) { @@ -46,7 +46,7 @@ public function __construct(array $elements) * * @return static A default instance of {@see Vector}. * - * @external-mutation-free + * @pure */ public static function default(): static { @@ -66,7 +66,6 @@ public static function default(): static */ public static function fromArray(array $elements): Vector { - /** @psalm-suppress ImpureMethodCall - safe */ return new self($elements); } diff --git a/src/Psl/DataStructure/PriorityQueue.php b/src/Psl/DataStructure/PriorityQueue.php index 1b726af1..03c46a6a 100644 --- a/src/Psl/DataStructure/PriorityQueue.php +++ b/src/Psl/DataStructure/PriorityQueue.php @@ -39,7 +39,7 @@ public static function default(): static * * @param T $node * - * @external-mutation-free + * @psalm-external-mutation-free */ public function enqueue(mixed $node, int $priority = 0): void { @@ -55,7 +55,7 @@ public function enqueue(mixed $node, int $priority = 0): void * * @return null|T * - * @mutation-free + * @psalm-mutation-free */ public function peek(): mixed { @@ -81,7 +81,7 @@ public function peek(): mixed * * @return null|T * - * @external-mutation-free + * @psalm-external-mutation-free */ public function pull(): mixed { @@ -99,7 +99,7 @@ public function pull(): mixed * * @return T * - * @external-mutation-free + * @psalm-external-mutation-free */ public function dequeue(): mixed { @@ -138,7 +138,7 @@ public function dequeue(): mixed * * @return int<0, max> * - * @mutation-free + * @psalm-mutation-free */ public function count(): int { diff --git a/src/Psl/DataStructure/Queue.php b/src/Psl/DataStructure/Queue.php index 34d00d7a..c0c5961e 100644 --- a/src/Psl/DataStructure/Queue.php +++ b/src/Psl/DataStructure/Queue.php @@ -38,7 +38,7 @@ public static function default(): static * * @param T $node * - * @external-mutation-free + * @psalm-external-mutation-free */ public function enqueue(mixed $node): void { @@ -51,7 +51,7 @@ public function enqueue(mixed $node): void * * @return null|T * - * @mutation-free + * @psalm-mutation-free */ public function peek(): mixed { @@ -64,7 +64,7 @@ public function peek(): mixed * * @return null|T * - * @external-mutation-free + * @psalm-external-mutation-free */ public function pull(): mixed { @@ -78,7 +78,7 @@ public function pull(): mixed * * @return T * - * @external-mutation-free + * @psalm-external-mutation-free */ public function dequeue(): mixed { @@ -95,7 +95,7 @@ public function dequeue(): mixed * * @return int<0, max> * - * @mutation-free + * @psalm-mutation-free */ public function count(): int { diff --git a/src/Psl/DataStructure/Stack.php b/src/Psl/DataStructure/Stack.php index e2204cb0..716b685e 100644 --- a/src/Psl/DataStructure/Stack.php +++ b/src/Psl/DataStructure/Stack.php @@ -38,7 +38,7 @@ public static function default(): static * * @param T $item * - * @external-mutation-free + * @psalm-external-mutation-free */ public function push(mixed $item): void { @@ -51,7 +51,7 @@ public function push(mixed $item): void * * @return null|T * - * @mutation-free + * @psalm-mutation-free */ public function peek(): mixed { @@ -66,7 +66,7 @@ public function peek(): mixed * * @return null|T * - * @external-mutation-free + * @psalm-external-mutation-free */ public function pull(): mixed { @@ -80,7 +80,7 @@ public function pull(): mixed * * @return T * - * @external-mutation-free + * @psalm-external-mutation-free */ public function pop(): mixed { @@ -97,7 +97,7 @@ public function pop(): mixed * * @return int<0, max> * - * @mutation-free + * @psalm-mutation-free */ public function count(): int { diff --git a/src/Psl/Env/join_paths.php b/src/Psl/Env/join_paths.php index 95018f52..dad9ca86 100644 --- a/src/Psl/Env/join_paths.php +++ b/src/Psl/Env/join_paths.php @@ -14,6 +14,8 @@ * @param string ...$paths * * @no-named-arguments + * + * @pure */ function join_paths(string ...$paths): string { diff --git a/src/Psl/Env/split_paths.php b/src/Psl/Env/split_paths.php index 1a77173e..6f0f18c9 100644 --- a/src/Psl/Env/split_paths.php +++ b/src/Psl/Env/split_paths.php @@ -12,6 +12,8 @@ * Parses input according to platform conventions for the PATH environment variable. * * @return string[] + * + * @pure */ function split_paths(string $path): array { diff --git a/src/Psl/File/Lock.php b/src/Psl/File/Lock.php index af0c0b58..b74c0668 100644 --- a/src/Psl/File/Lock.php +++ b/src/Psl/File/Lock.php @@ -10,6 +10,7 @@ final class Lock { private bool $released = false; + /** * @param (Closure(): void) $releaseCallback * diff --git a/src/Psl/Hash/Context.php b/src/Psl/Hash/Context.php index b2982fee..0fe2b7c9 100644 --- a/src/Psl/Hash/Context.php +++ b/src/Psl/Hash/Context.php @@ -23,7 +23,7 @@ * ->finalize() * => Str("5c6ffbdd40d9556b73a21e63c3e0e904") * - * @immutable + * @psalm-immutable */ final class Context { diff --git a/src/Psl/IO/MemoryHandle.php b/src/Psl/IO/MemoryHandle.php index 623b5a5b..a1fda6e9 100644 --- a/src/Psl/IO/MemoryHandle.php +++ b/src/Psl/IO/MemoryHandle.php @@ -23,6 +23,9 @@ final class MemoryHandle implements CloseSeekReadWriteHandleInterface private bool $closed = false; private bool $reachedEof = false; + /** + * @psalm-external-mutation-free + */ public function __construct(string $buffer = '') { $this->buffer = $buffer; @@ -30,6 +33,8 @@ public function __construct(string $buffer = '') /** * {@inheritDoc} + * + * @psalm-mutation-free */ public function reachedEndOfDataSource(): bool { @@ -40,6 +45,8 @@ public function reachedEndOfDataSource(): bool /** * {@inheritDoc} + * + * @psalm-external-mutation-free */ public function tryRead(?int $max_bytes = null): string { @@ -66,6 +73,8 @@ public function tryRead(?int $max_bytes = null): string /** * {@inheritDoc} + * + * @psalm-external-mutation-free */ public function read(?int $max_bytes = null, ?float $timeout = null): string { @@ -74,6 +83,8 @@ public function read(?int $max_bytes = null, ?float $timeout = null): string /** * {@inheritDoc} + * + * @psalm-external-mutation-free */ public function seek(int $offset): void { @@ -84,6 +95,8 @@ public function seek(int $offset): void /** * {@inheritDoc} + * + * @psalm-mutation-free */ public function tell(): int { @@ -94,6 +107,8 @@ public function tell(): int /** * {@inheritDoc} + * + * @psalm-external-mutation-free */ public function tryWrite(string $bytes, ?float $timeout = null): int { @@ -119,6 +134,8 @@ public function tryWrite(string $bytes, ?float $timeout = null): int /** * {@inheritDoc} + * + * @psalm-external-mutation-free */ public function write(string $bytes, ?float $timeout = null): int { @@ -127,12 +144,17 @@ public function write(string $bytes, ?float $timeout = null): int /** * {@inheritDoc} + * + * @psalm-external-mutation-free */ public function close(): void { $this->closed = true; } + /** + * @psalm-mutation-free + */ public function getBuffer(): string { return $this->buffer; @@ -140,6 +162,8 @@ public function getBuffer(): string /** * @throws Exception\AlreadyClosedException If the handle has been already closed. + * + * @psalm-mutation-free */ private function assertHandleIsOpen(): void { diff --git a/src/Psl/Internal/Loader.php b/src/Psl/Internal/Loader.php index 0b43e06d..4d9ea29e 100644 --- a/src/Psl/Internal/Loader.php +++ b/src/Psl/Internal/Loader.php @@ -467,6 +467,7 @@ final class Loader 'Psl\\Class\\has_method' => 'Psl/Class/has_method.php', 'Psl\\Class\\is_abstract' => 'Psl/Class/is_abstract.php', 'Psl\\Class\\is_final' => 'Psl/Class/is_final.php', + 'Psl\\Class\\is_readonly' => 'Psl/Class/is_readonly.php', 'Psl\\Interface\\exists' => 'Psl/Interface/exists.php', 'Psl\\Interface\\defined' => 'Psl/Interface/defined.php', 'Psl\\Trait\\exists' => 'Psl/Trait/exists.php', diff --git a/src/Psl/Iter/Iterator.php b/src/Psl/Iter/Iterator.php index 6efede58..58ac0a55 100644 --- a/src/Psl/Iter/Iterator.php +++ b/src/Psl/Iter/Iterator.php @@ -30,7 +30,7 @@ final class Iterator implements Countable, SeekableIterator private array $entries = []; /** - * Whether or not the current value/key pair has been added to the local entries. + * Whether the current value/key pair has been added to the local entries. */ private bool $saved = true; @@ -164,14 +164,14 @@ public function rewind(): void /** * Seek to the given position. * - * @param int<0, max> $position + * @param int<0, max> $offset * * @throws Exception\OutOfBoundsException If $position is out-of-bounds. */ - public function seek(int $position): void + public function seek(int $offset): void { - if ($position <= $this->position) { - $this->position = $position; + if ($offset <= $this->position) { + $this->position = $offset; return; } @@ -184,16 +184,16 @@ public function seek(int $position): void $this->generator = null; throw new Exception\OutOfBoundsException('Position is out-of-bounds.'); } - } while ($this->position < $position); + } while ($this->position < $offset); return; } - if ($position >= $this->count()) { + if ($offset >= $this->count()) { throw new Exception\OutOfBoundsException('Position is out-of-bounds.'); } - $this->position = $position; + $this->position = $offset; } /** diff --git a/src/Psl/Locale/Locale.php b/src/Psl/Locale/Locale.php index f069219a..e93d9ed2 100644 --- a/src/Psl/Locale/Locale.php +++ b/src/Psl/Locale/Locale.php @@ -844,7 +844,7 @@ public static function default(): self * * @return non-empty-string The human-readable name of the locale. * - * @mutation-free + * @psalm-mutation-free */ public function getDisplayName(?Locale $locale = null): string { @@ -857,7 +857,7 @@ public function getDisplayName(?Locale $locale = null): string * * @return non-empty-string The language code. * - * @mutation-free + * @psalm-mutation-free */ public function getLanguage(): string { @@ -872,7 +872,7 @@ public function getLanguage(): string * * @return non-empty-string The display name of the language. * - * @mutation-free + * @psalm-mutation-free */ public function getDisplayLanguage(?Locale $locale = null): string { @@ -885,7 +885,7 @@ public function getDisplayLanguage(?Locale $locale = null): string * * @return bool True if the locale has a script, false otherwise. * - * @mutation-free + * @psalm-mutation-free */ public function hasScript(): bool { @@ -897,7 +897,7 @@ public function hasScript(): bool * * @return non-empty-string|null The script of the locale, or null if not applicable. * - * @mutation-free + * @psalm-mutation-free */ public function getScript(): ?string { @@ -909,7 +909,7 @@ public function getScript(): ?string * * @return bool True if the locale has a region, false otherwise. * - * @mutation-free + * @psalm-mutation-free */ public function hasRegion(): bool { @@ -923,7 +923,7 @@ public function hasRegion(): bool * * @return non-empty-string|null The display name of the region, or null if not applicable. * - * @mutation-free + * @psalm-mutation-free */ public function getDisplayRegion(?Locale $locale = null): ?string { @@ -935,7 +935,7 @@ public function getDisplayRegion(?Locale $locale = null): ?string * * @return non-empty-string|null The alpha-2 country code, or null if not present. * - * @mutation-free + * @psalm-mutation-free */ public function getRegion(): ?string { diff --git a/src/Psl/Network/Address.php b/src/Psl/Network/Address.php index d33dafff..8a2a59f3 100644 --- a/src/Psl/Network/Address.php +++ b/src/Psl/Network/Address.php @@ -4,28 +4,68 @@ namespace Psl\Network; -final class Address +/** + * @psalm-immutable + */ +final readonly class Address { public const DEFAULT_HOST = '127.0.0.1'; public const DEFAULT_PORT = 0; - private function __construct( - public readonly SocketScheme $scheme, - public readonly string $host, - public readonly ?int $port = null, - ) { + public SocketScheme $scheme; + + /** + * @var non-empty-string + */ + public string $host; + + /** + * @var int<0, 65535>|null + */ + public ?int $port; + + /** + * @param SocketScheme $scheme + * @param non-empty-string $host + * @param int<0,65535>|null $port + * + * @psalm-mutation-free + */ + private function __construct(SocketScheme $scheme, string $host, ?int $port) + { + $this->scheme = $scheme; + $this->host = $host; + $this->port = $port; } + /** + * @param SocketScheme $scheme + * @param non-empty-string $host + * @param int<0,65535>|null $port + * + * @pure + */ public static function create(SocketScheme $scheme, string $host, ?int $port = null): self { return new self($scheme, $host, $port); } + /** + * @param non-empty-string $host + * + * @pure + */ public static function unix(string $host): self { return new self(SocketScheme::Unix, $host, null); } + /** + * @param non-empty-string $host + * @param int<0,65535> $port + * + * @pure + */ public static function tcp(string $host = self::DEFAULT_HOST, int $port = self::DEFAULT_PORT): self { return new self(SocketScheme::Tcp, $host, $port); @@ -33,15 +73,16 @@ public static function tcp(string $host = self::DEFAULT_HOST, int $port = self:: /** * @return non-empty-string + * + * @psalm-mutation-free */ public function toString(): string { - $address = "{$this->scheme->value}://{$this->host}"; - + $address = "{$this->scheme->value}://$this->host"; if (null === $this->port) { return $address; } - return "{$address}:{$this->port}"; + return "$address:$this->port"; } } diff --git a/src/Psl/Network/Internal/get_peer_name.php b/src/Psl/Network/Internal/get_peer_name.php index f64472e7..22d34cfb 100644 --- a/src/Psl/Network/Internal/get_peer_name.php +++ b/src/Psl/Network/Internal/get_peer_name.php @@ -23,6 +23,7 @@ function get_peer_name(mixed $socket): Network\Address { error_clear_last(); + /** @var non-empty-string|false $result */ $result = stream_socket_get_name($socket, true); if ($result !== false && $result !== "\0") { $separator_position = strrpos($result, ':'); @@ -30,11 +31,12 @@ function get_peer_name(mixed $socket): Network\Address return Network\Address::unix($result); } - $scheme = Network\SocketScheme::Tcp; + /** @var non-empty-string $host */ $host = substr($result, 0, $separator_position); + /** @var int<0, 65535> $port */ $port = (int) substr($result, $separator_position + 1); - return Network\Address::create($scheme, $host, $port); + return Network\Address::tcp($host, $port); } return get_sock_name($socket); diff --git a/src/Psl/Network/Internal/get_sock_name.php b/src/Psl/Network/Internal/get_sock_name.php index 13b9e5fd..b503673e 100644 --- a/src/Psl/Network/Internal/get_sock_name.php +++ b/src/Psl/Network/Internal/get_sock_name.php @@ -24,6 +24,7 @@ function get_sock_name(mixed $socket): Network\Address { error_clear_last(); + /** @var non-empty-string|false $result */ $result = stream_socket_get_name($socket, false); if ($result !== false) { $separator_position = strrpos($result, ':'); @@ -31,7 +32,9 @@ function get_sock_name(mixed $socket): Network\Address return Network\Address::unix($result); } + /** @var non-empty-string $host */ $host = substr($result, 0, $separator_position); + /** @var int<0, 65535> $port */ $port = (int) substr($result, $separator_position + 1); return Network\Address::tcp($host, $port); diff --git a/src/Psl/Network/SocketOptions.php b/src/Psl/Network/SocketOptions.php index deb0b7b2..1a59a6ca 100644 --- a/src/Psl/Network/SocketOptions.php +++ b/src/Psl/Network/SocketOptions.php @@ -9,24 +9,28 @@ /** * Encapsulates socket options for network operations. * - * @immutable + * @psalm-immutable */ -final class SocketOptions implements DefaultInterface +final readonly class SocketOptions implements DefaultInterface { + public bool $addressReuse; + public bool $portReuse; + public bool $broadcast; + /** * Initializes a new instance of SocketOptions with the specified settings. * - * @param bool $addressReuse Enables or disables the SO_REUSEADDR socket option. - * @param bool $portReuse Enables or disables the SO_REUSEPORT socket option. + * @param bool $address_reuse Enables or disables the SO_REUSEADDR socket option. + * @param bool $port_reuse Enables or disables the SO_REUSEPORT socket option. * @param bool $broadcast Enables or disables the SO_BROADCAST socket option. * - * @pure + * @psalm-mutation-free */ - public function __construct( - public readonly bool $addressReuse, - public readonly bool $portReuse, - public readonly bool $broadcast, - ) { + public function __construct(bool $address_reuse, bool $port_reuse, bool $broadcast) + { + $this->addressReuse = $address_reuse; + $this->portReuse = $port_reuse; + $this->broadcast = $broadcast; } /** @@ -64,7 +68,7 @@ public static function default(): static * * @param bool $enabled The desired state for the SO_REUSEADDR option. * - * @mutation-free + * @psalm-mutation-free */ public function withAddressReuse(bool $enabled = true): SocketOptions { @@ -76,7 +80,7 @@ public function withAddressReuse(bool $enabled = true): SocketOptions * * @param bool $enabled The desired state for the SO_REUSEPORT option. * - * @mutation-free + * @psalm-mutation-free */ public function withPortReuse(bool $enabled = true): SocketOptions { @@ -88,7 +92,7 @@ public function withPortReuse(bool $enabled = true): SocketOptions * * @param bool $enabled The desired state for the SO_BROADCAST option. * - * @mutation-free + * @psalm-mutation-free */ public function withBroadcast(bool $enabled = true): SocketOptions { diff --git a/src/Psl/Option/Option.php b/src/Psl/Option/Option.php index 93cc3cdc..4ac1575c 100644 --- a/src/Psl/Option/Option.php +++ b/src/Psl/Option/Option.php @@ -14,16 +14,21 @@ * @implements Comparison\Comparable> * @implements Comparison\Equable> */ -final class Option implements Comparison\Comparable, Comparison\Equable +final readonly class Option implements Comparison\Comparable, Comparison\Equable { + /** + * @var ?array{T} $option + */ + private null|array $option; + /** * @param ?array{T} $option * - * @internal + * @psalm-mutation-free */ - private function __construct( - private readonly null|array $option, - ) { + private function __construct(?array $option) + { + $this->option = $option; } /** @@ -34,6 +39,8 @@ private function __construct( * @param Tv $value * * @return Option + * + * @pure */ public static function some(mixed $value): Option { @@ -44,6 +51,8 @@ public static function some(mixed $value): Option * Create an option with none value. * * @return Option + * + * @pure */ public static function none(): Option { @@ -53,6 +62,8 @@ public static function none(): Option /** * Returns true if the option is a some value. + * + * @psalm-mutation-free */ public function isSome(): bool { @@ -71,6 +82,8 @@ public function isSomeAnd(Closure $predicate): bool /** * Returns true if the option is a none. + * + * @psalm-mutation-free */ public function isNone(): bool { @@ -86,6 +99,8 @@ public function isNone(): bool * @throws Exception\NoneException If the option is none. * * @return T + * + * @psalm-mutation-free */ public function unwrap(): mixed { @@ -107,6 +122,8 @@ public function unwrap(): mixed * @param O $default * * @return T|O + * + * @psalm-mutation-free */ public function unwrapOr(mixed $default): mixed { @@ -143,6 +160,8 @@ public function unwrapOrElse(Closure $default): mixed * @param Option $other * * @return Option + * + * @psalm-mutation-free */ public function and(Option $other): Option { @@ -162,6 +181,8 @@ public function and(Option $other): Option * @param Option $option * * @return Option + * + * @psalm-mutation-free */ public function or(Option $option): Option { @@ -194,6 +215,8 @@ public function filter(Closure $predicate): Option * Returns true if the option is a `Option::some()` value containing the given value. * * @psalm-assert-if-true T $value + * + * @psalm-mutation-free */ public function contains(mixed $value): bool { @@ -398,6 +421,8 @@ static function ($a) use ($other, $closure) { * @throws Type\Exception\AssertException * * @return array{Option, Option} + * + * @psalm-mutation-free */ public function unzip(): array { @@ -405,11 +430,7 @@ public function unzip(): array return [none(), none()]; } - // Assertion done in a separate variable to avoid Psalm inferring the type of $this->option as mixed - $option = $this->option[0]; - Type\shape([Type\mixed(), Type\mixed()])->assert($option); - - [$a, $b] = $option; + [$a, $b] = $this->option[0]; return [some($a), some($b)]; } diff --git a/src/Psl/Option/from_nullable.php b/src/Psl/Option/from_nullable.php index 8310a021..300064a0 100644 --- a/src/Psl/Option/from_nullable.php +++ b/src/Psl/Option/from_nullable.php @@ -12,6 +12,8 @@ * @param null|T $value * * @return Option + * + * @pure */ function from_nullable(mixed $value): Option { diff --git a/src/Psl/Option/none.php b/src/Psl/Option/none.php index fa0f9e5e..a22642c0 100644 --- a/src/Psl/Option/none.php +++ b/src/Psl/Option/none.php @@ -8,6 +8,8 @@ * Create an option with none value. * * @return Option + * + * @pure */ function none(): Option { diff --git a/src/Psl/Option/some.php b/src/Psl/Option/some.php index 96610e9a..0f55e76e 100644 --- a/src/Psl/Option/some.php +++ b/src/Psl/Option/some.php @@ -12,6 +12,8 @@ * @param T $value * * @return Option + * + * @pure */ function some(mixed $value): Option { diff --git a/src/Psl/Password/Algorithm.php b/src/Psl/Password/Algorithm.php index f1c6d5e4..440097d3 100644 --- a/src/Psl/Password/Algorithm.php +++ b/src/Psl/Password/Algorithm.php @@ -75,7 +75,7 @@ enum Algorithm: string implements DefaultInterface * values used by the password hashing API. It enables seamless integration between * the type-safe enum approach and PHP's underlying password hashing mechanism. * - * @mutation-free + * @psalm-mutation-free */ public function getBuiltinConstantValue(): string { diff --git a/src/Psl/Password/hash.php b/src/Psl/Password/hash.php index 89826d69..df8ee50a 100644 --- a/src/Psl/Password/hash.php +++ b/src/Psl/Password/hash.php @@ -15,6 +15,5 @@ */ function hash(string $password, Algorithm $algorithm = Algorithm::Default, array $options = []): string { - /** @psalm-suppress ImpureMethodCall */ return password_hash($password, $algorithm->getBuiltinConstantValue(), $options); } diff --git a/src/Psl/Password/needs_rehash.php b/src/Psl/Password/needs_rehash.php index b100881d..92df608a 100644 --- a/src/Psl/Password/needs_rehash.php +++ b/src/Psl/Password/needs_rehash.php @@ -24,6 +24,5 @@ */ function needs_rehash(string $hash, Algorithm $algorithm = Algorithm::Default, array $options = []): bool { - /** @psalm-suppress ImpureMethodCall */ return password_needs_rehash($hash, $algorithm->getBuiltinConstantValue(), $options); } diff --git a/src/Psl/RandomSequence/Internal/MersenneTwisterTrait.php b/src/Psl/RandomSequence/Internal/MersenneTwisterTrait.php index 78f3745c..f0c5d41a 100644 --- a/src/Psl/RandomSequence/Internal/MersenneTwisterTrait.php +++ b/src/Psl/RandomSequence/Internal/MersenneTwisterTrait.php @@ -40,7 +40,7 @@ final public function __construct( /** * Generates the next pseudorandom number. * - * @external-mutation-free + * @psalm-external-mutation-free */ final public function next(): int { diff --git a/src/Psl/RandomSequence/SecureSequence.php b/src/Psl/RandomSequence/SecureSequence.php index cf07ceaf..6f96d99c 100644 --- a/src/Psl/RandomSequence/SecureSequence.php +++ b/src/Psl/RandomSequence/SecureSequence.php @@ -9,11 +9,18 @@ /** * A Cryptographically Secure PRNG. - * - * @immutable */ final class SecureSequence implements DefaultInterface, SequenceInterface { + /** + * Construct a new secure sequence. + * + * @pure + */ + public function __construct() + { + } + /** * @pure */ @@ -25,11 +32,14 @@ public static function default(): static /** * Generates the next pseudorandom number. * - * @external-mutation-free + * @psalm-external-mutation-free */ public function next(): int { - /** @psalm-suppress MissingThrowsDocblock */ + /** + * @psalm-suppress MissingThrowsDocblock + * @psalm-suppress ImpureFunctionCall + */ return SecureRandom\int(); } } diff --git a/src/Psl/Range/BetweenRange.php b/src/Psl/Range/BetweenRange.php index 9ff76916..3c5d592e 100644 --- a/src/Psl/Range/BetweenRange.php +++ b/src/Psl/Range/BetweenRange.php @@ -38,26 +38,31 @@ * @see UpperBoundRangeInterface::withUpperInclusive() * @see UpperBoundRangeInterface::isUpperInclusive() * - * @immutable + * @psalm-immutable */ -final class BetweenRange implements LowerBoundRangeInterface, UpperBoundRangeInterface +final readonly class BetweenRange implements LowerBoundRangeInterface, UpperBoundRangeInterface { + private int $lowerBound; + private int $upperBound; + private bool $upperInclusive; + /** * @throws Exception\InvalidRangeException If the lower bound is greater than the upper bound. * * @psalm-mutation-free */ - public function __construct( - private readonly int $lower_bound, - private readonly int $upper_bound, - private readonly bool $upper_inclusive = false, - ) { - if ($this->lower_bound > $this->upper_bound) { + public function __construct(int $lower_bound, int $upper_bound, bool $upper_inclusive = false) + { + if ($lower_bound > $upper_bound) { throw Exception\InvalidRangeException::lowerBoundIsGreaterThanUpperBound( - $this->lower_bound, - $this->upper_bound + $lower_bound, + $upper_bound ); } + + $this->lowerBound = $lower_bound; + $this->upperBound = $upper_bound; + $this->upperInclusive = $upper_inclusive; } /** @@ -67,15 +72,15 @@ public function __construct( */ public function contains(int $value): bool { - if ($value < $this->lower_bound) { + if ($value < $this->lowerBound) { return false; } - if ($this->upper_inclusive) { - return $value <= $this->upper_bound; + if ($this->upperInclusive) { + return $value <= $this->upperBound; } - return $value < $this->upper_bound; + return $value < $this->upperBound; } /** @@ -88,7 +93,7 @@ public function contains(int $value): bool public function withUpperBound(int $upper_bound, bool $upper_inclusive): BetweenRange { return new BetweenRange( - $this->lower_bound, + $this->lowerBound, $upper_bound, $upper_inclusive, ); @@ -104,7 +109,7 @@ public function withUpperBound(int $upper_bound, bool $upper_inclusive): Between public function withUpperBoundInclusive(int $upper_bound): BetweenRange { return new BetweenRange( - $this->lower_bound, + $this->lowerBound, $upper_bound, true, ); @@ -120,7 +125,7 @@ public function withUpperBoundInclusive(int $upper_bound): BetweenRange public function withUpperBoundExclusive(int $upper_bound): BetweenRange { return new BetweenRange( - $this->lower_bound, + $this->lowerBound, $upper_bound, false, ); @@ -133,7 +138,7 @@ public function withUpperBoundExclusive(int $upper_bound): BetweenRange */ public function withoutLowerBound(): ToRange { - return new ToRange($this->upper_bound, $this->upper_inclusive); + return new ToRange($this->upperBound, $this->upperInclusive); } /** @@ -147,8 +152,8 @@ public function withLowerBound(int $lower_bound): BetweenRange { return new static( $lower_bound, - $this->upper_bound, - $this->upper_inclusive, + $this->upperBound, + $this->upperInclusive, ); } @@ -159,7 +164,7 @@ public function withLowerBound(int $lower_bound): BetweenRange */ public function withoutUpperBound(): FromRange { - return new FromRange($this->lower_bound); + return new FromRange($this->lowerBound); } /** @@ -169,7 +174,7 @@ public function withoutUpperBound(): FromRange */ public function getUpperBound(): int { - return $this->upper_bound; + return $this->upperBound; } /** @@ -179,7 +184,7 @@ public function getUpperBound(): int */ public function isUpperInclusive(): bool { - return $this->upper_inclusive; + return $this->upperInclusive; } /** @@ -191,8 +196,8 @@ public function withUpperInclusive(bool $upper_inclusive): static { /** @psalm-suppress MissingThrowsDocblock */ return new static( - $this->lower_bound, - $this->upper_bound, + $this->lowerBound, + $this->upperBound, $upper_inclusive, ); } @@ -204,7 +209,7 @@ public function withUpperInclusive(bool $upper_inclusive): static */ public function getLowerBound(): int { - return $this->lower_bound; + return $this->lowerBound; } /** @@ -218,9 +223,9 @@ public function getLowerBound(): int */ public function getIterator(): Iter\Iterator { - $lower = $this->lower_bound; - $upper = $this->upper_bound; - $inclusive = $this->upper_inclusive; + $lower = $this->lowerBound; + $upper = $this->upperBound; + $inclusive = $this->upperInclusive; return Iter\Iterator::from(static function () use ($lower, $upper, $inclusive): Generator { $to = $inclusive ? $upper : $upper - 1; diff --git a/src/Psl/Range/FromRange.php b/src/Psl/Range/FromRange.php index 23ba1bba..88eecdc7 100644 --- a/src/Psl/Range/FromRange.php +++ b/src/Psl/Range/FromRange.php @@ -28,16 +28,18 @@ * @see RangeInterface::contains() * @see LowerBoundRangeInterface::getLowerBound() * - * @immutable + * @psalm-immutable */ -final class FromRange implements LowerBoundRangeInterface +final readonly class FromRange implements LowerBoundRangeInterface { + private int $lowerBound; + /** * @psalm-mutation-free */ - public function __construct( - private readonly int $lower_bound, - ) { + public function __construct(int $lower_bound) + { + $this->lowerBound = $lower_bound; } /** @@ -47,7 +49,7 @@ public function __construct( */ public function contains(int $value): bool { - return $value >= $this->lower_bound; + return $value >= $this->lowerBound; } /** @@ -72,7 +74,7 @@ public function withLowerBound(int $lower_bound): FromRange public function withUpperBound(int $upper_bound, bool $upper_inclusive): BetweenRange { return new BetweenRange( - $this->lower_bound, + $this->lowerBound, $upper_bound, $upper_inclusive, ); @@ -88,7 +90,7 @@ public function withUpperBound(int $upper_bound, bool $upper_inclusive): Between public function withUpperBoundInclusive(int $upper_bound): BetweenRange { return new BetweenRange( - $this->lower_bound, + $this->lowerBound, $upper_bound, true, ); @@ -104,7 +106,7 @@ public function withUpperBoundInclusive(int $upper_bound): BetweenRange public function withUpperBoundExclusive(int $upper_bound): BetweenRange { return new BetweenRange( - $this->lower_bound, + $this->lowerBound, $upper_bound, false, ); @@ -127,7 +129,7 @@ public function withoutLowerBound(): FullRange */ public function getLowerBound(): int { - return $this->lower_bound; + return $this->lowerBound; } /** @@ -141,7 +143,7 @@ public function getLowerBound(): int */ public function getIterator(): Iter\Iterator { - $bound = $this->lower_bound; + $bound = $this->lowerBound; return Iter\Iterator::from(static function () use ($bound): Generator { $value = $bound; diff --git a/src/Psl/Range/FullRange.php b/src/Psl/Range/FullRange.php index 3e93ef29..93a91d86 100644 --- a/src/Psl/Range/FullRange.php +++ b/src/Psl/Range/FullRange.php @@ -11,7 +11,7 @@ * * @see RangeInterface::contains() * - * @immutable + * @psalm-immutable */ final class FullRange implements RangeInterface { diff --git a/src/Psl/Range/LowerBoundRangeInterface.php b/src/Psl/Range/LowerBoundRangeInterface.php index 51a56ff3..b4191002 100644 --- a/src/Psl/Range/LowerBoundRangeInterface.php +++ b/src/Psl/Range/LowerBoundRangeInterface.php @@ -11,7 +11,7 @@ /** * @extends IteratorAggregate * - * @immutable + * @psalm-immutable */ interface LowerBoundRangeInterface extends IteratorAggregate, RangeInterface { @@ -55,7 +55,7 @@ public function withoutLowerBound(): RangeInterface; * @psalm-mutation-free */ public function getLowerBound(): int; - + /** * Returns an iterator for the range. * diff --git a/src/Psl/Range/RangeInterface.php b/src/Psl/Range/RangeInterface.php index 17842b8a..bd3a8fd2 100644 --- a/src/Psl/Range/RangeInterface.php +++ b/src/Psl/Range/RangeInterface.php @@ -7,7 +7,7 @@ /** * a range is a set of values that are contained in the range. * - * @immutable + * @psalm-immutable */ interface RangeInterface { diff --git a/src/Psl/Range/ToRange.php b/src/Psl/Range/ToRange.php index 9d64fbbc..3ee3c5f3 100644 --- a/src/Psl/Range/ToRange.php +++ b/src/Psl/Range/ToRange.php @@ -13,17 +13,20 @@ * @see UpperBoundRangeInterface::getUpperBound() * @see UpperBoundRangeInterface::isUpperInclusive() * - * @immutable + * @psalm-immutable */ -final class ToRange implements UpperBoundRangeInterface +final readonly class ToRange implements UpperBoundRangeInterface { + private int $upperBound; + private bool $upperInclusive; + /** * @psalm-mutation-free */ - public function __construct( - private readonly int $upper_bound, - private readonly bool $upper_inclusive = false, - ) { + public function __construct(int $upper_bound, bool $upper_inclusive = false) + { + $this->upperBound = $upper_bound; + $this->upperInclusive = $upper_inclusive; } /** @@ -33,11 +36,11 @@ public function __construct( */ public function contains(int $value): bool { - if ($this->upper_inclusive) { - return $value <= $this->upper_bound; + if ($this->upperInclusive) { + return $value <= $this->upperBound; } - return $value < $this->upper_bound; + return $value < $this->upperBound; } /** @@ -51,8 +54,8 @@ public function withLowerBound(int $lower_bound): BetweenRange { return new BetweenRange( $lower_bound, - $this->upper_bound, - $this->upper_inclusive, + $this->upperBound, + $this->upperInclusive, ); } @@ -103,9 +106,9 @@ public function withUpperBoundExclusive(int $upper_bound): ToRange */ public function getUpperBound(): int { - return $this->upper_bound; + return $this->upperBound; } - + /** * {@inheritDoc} * @@ -113,7 +116,7 @@ public function getUpperBound(): int */ public function isUpperInclusive(): bool { - return $this->upper_inclusive; + return $this->upperInclusive; } /** @@ -124,7 +127,7 @@ public function isUpperInclusive(): bool public function withUpperInclusive(bool $upper_inclusive): static { return new static( - $this->upper_bound, + $this->upperBound, $upper_inclusive, ); } diff --git a/src/Psl/Range/UpperBoundRangeInterface.php b/src/Psl/Range/UpperBoundRangeInterface.php index 1c47ff8b..28a112b4 100644 --- a/src/Psl/Range/UpperBoundRangeInterface.php +++ b/src/Psl/Range/UpperBoundRangeInterface.php @@ -5,7 +5,7 @@ namespace Psl\Range; /** - * @immutable + * @psalm-immutable */ interface UpperBoundRangeInterface extends RangeInterface { @@ -24,7 +24,7 @@ public function withLowerBound(int $lower_bound): UpperBoundRangeInterface&Lower * @psalm-mutation-free */ public function withoutUpperBound(): RangeInterface; - + /** * Returns the upper bound of the range. * diff --git a/src/Psl/Ref.php b/src/Psl/Ref.php index d0bcb703..8a0308e9 100644 --- a/src/Psl/Ref.php +++ b/src/Psl/Ref.php @@ -7,7 +7,7 @@ /** * Wrapper class for getting object (byref) semantics for a value type. * - * This is especially useful for mutating values outside of a lambda's scope. + * This is especially useful for mutating values outside a lambda's scope. * * In general, it's preferable to refactor to use return values. * @@ -17,11 +17,16 @@ */ final class Ref { + /** + * @var T + */ + public mixed $value; + /** * @param T $value */ - public function __construct( - public mixed $value - ) { + public function __construct(mixed $value) + { + $this->value = $value; } } diff --git a/src/Psl/Regex/every_match.php b/src/Psl/Regex/every_match.php index cb5941e2..1a02b061 100644 --- a/src/Psl/Regex/every_match.php +++ b/src/Psl/Regex/every_match.php @@ -21,12 +21,8 @@ * * @return (T is null ? list> : list)|null */ -function every_match( - string $subject, - string $pattern, - ?Type\TypeInterface $capture_groups = null, - int $offset = 0 -): ?array { +function every_match(string $subject, string $pattern, ?Type\TypeInterface $capture_groups = null, int $offset = 0): ?array +{ $matching = Internal\call_preg( 'preg_match_all', static function () use ($subject, $pattern, $offset): ?array { diff --git a/src/Psl/Regex/first_match.php b/src/Psl/Regex/first_match.php index a7fd80bf..b6bf6622 100644 --- a/src/Psl/Regex/first_match.php +++ b/src/Psl/Regex/first_match.php @@ -21,12 +21,8 @@ * * @return (T is null ? array : T)|null */ -function first_match( - string $subject, - string $pattern, - ?Type\TypeInterface $capture_groups = null, - int $offset = 0 -): ?array { +function first_match(string $subject, string $pattern, ?Type\TypeInterface $capture_groups = null, int $offset = 0): ?array +{ $matching = Internal\call_preg( 'preg_match', static function () use ($subject, $pattern, $offset): ?array { diff --git a/src/Psl/Result/Failure.php b/src/Psl/Result/Failure.php index 7431c0ac..71db339b 100644 --- a/src/Psl/Result/Failure.php +++ b/src/Psl/Result/Failure.php @@ -15,14 +15,21 @@ * * @implements ResultInterface */ -final class Failure implements ResultInterface +final readonly class Failure implements ResultInterface { + /** + * @var Te + */ + private Throwable $throwable; + /** * @param Te $throwable + * + * @psalm-mutation-free */ - public function __construct( - private readonly Throwable $throwable - ) { + public function __construct(Throwable $throwable) + { + $this->throwable = $throwable; } /** diff --git a/src/Psl/Result/Stats.php b/src/Psl/Result/Stats.php index 272a6dce..9e75f014 100644 --- a/src/Psl/Result/Stats.php +++ b/src/Psl/Result/Stats.php @@ -7,32 +7,53 @@ /** * @psalm-immutable */ -final class Stats +final readonly class Stats { - private int $total = 0; - private int $succeeded = 0; - private int $failed = 0; + private int $total; + private int $succeeded; + private int $failed; + + /** + * @psalm-mutation-free + */ + public function __construct(int $total = 0, int $succeeded = 0, int $failed = 0) + { + $this->total = $total; + $this->succeeded = $succeeded; + $this->failed = $failed; + } + /** + * @psalm-mutation-free + */ public function apply(ResultInterface $result): self { - $new = new self(); - $new->total = $this->total + 1; - $new->succeeded = $result->isSucceeded() ? $this->succeeded + 1 : $this->succeeded; - $new->failed = $result->isFailed() ? $this->failed + 1 : $this->failed; - - return $new; + return new self( + $this->total + 1, + $result->isSucceeded() ? $this->succeeded + 1 : $this->succeeded, + $result->isFailed() ? $this->failed + 1 : $this->failed, + ); } + /** + * @psalm-mutation-free + */ public function total(): int { return $this->total; } + /** + * @psalm-mutation-free + */ public function succeeded(): int { return $this->succeeded; } + /** + * @psalm-mutation-free + */ public function failed(): int { return $this->failed; diff --git a/src/Psl/Result/Success.php b/src/Psl/Result/Success.php index 90033ceb..6b0e0ff7 100644 --- a/src/Psl/Result/Success.php +++ b/src/Psl/Result/Success.php @@ -15,17 +15,17 @@ * * @implements ResultInterface */ -final class Success implements ResultInterface +final readonly class Success implements ResultInterface { /** * @var T - * - * @readonly */ private mixed $value; /** * @param T $value + * + * @psalm-mutation-free */ public function __construct(mixed $value) { diff --git a/src/Psl/TCP/ConnectOptions.php b/src/Psl/TCP/ConnectOptions.php index 98da8263..533f595b 100644 --- a/src/Psl/TCP/ConnectOptions.php +++ b/src/Psl/TCP/ConnectOptions.php @@ -9,22 +9,24 @@ /** * Represents the configuration options for TCP connections. * - * @immutable + * @psalm-immutable */ -final class ConnectOptions implements DefaultInterface +final readonly class ConnectOptions implements DefaultInterface { + public bool $noDelay; + /** * Initializes a new instance of {@see ConnectOptions} with the specified settings. * - * @param bool $noDelay Determines whether the TCP_NODELAY option is enabled, controlling - * the use of the Nagle algorithm. When true, TCP_NODELAY is enabled, - * and the Nagle algorithm is disabled. + * @param bool $no_delay Determines whether the TCP_NODELAY option is enabled, controlling + * the use of the Nagle algorithm. When true, TCP_NODELAY is enabled, + * and the Nagle algorithm is disabled. * - * @pure + * @psalm-mutation-free */ - public function __construct( - public readonly bool $noDelay, - ) { + public function __construct(bool $no_delay) + { + $this->noDelay = $no_delay; } /** @@ -35,13 +37,13 @@ public function __construct( * to the default constructor for cases where named constructors improve readability * and usage clarity. * - * @param bool $noDelay Specifies whether the TCP_NODELAY option should be enabled. + * @param bool $no_delay Specifies whether the TCP_NODELAY option should be enabled. * * @pure */ - public static function create(bool $noDelay = false): ConnectOptions + public static function create(bool $no_delay = false): ConnectOptions { - return new self($noDelay); + return new self($no_delay); } /** @@ -65,7 +67,7 @@ public static function default(): static * * @param bool $enabled Specifies the desired state of the TCP_NODELAY option. * - * @mutation-free + * @psalm-mutation-free */ public function withNoDelay(bool $enabled = true): ConnectOptions { diff --git a/src/Psl/TCP/ServerOptions.php b/src/Psl/TCP/ServerOptions.php index df75efed..ac8cb41b 100644 --- a/src/Psl/TCP/ServerOptions.php +++ b/src/Psl/TCP/ServerOptions.php @@ -10,31 +10,39 @@ /** * Configures options for a TCP server. * - * @immutable + * @psalm-immutable */ -final class ServerOptions implements DefaultInterface +final readonly class ServerOptions implements DefaultInterface { /** * Default number of idle connections allowed. */ public const DEFAULT_IDLE_CONNECTIONS = 256; + public bool $noDelay; + + /** + * @var int<1, max> + */ + public int $idleConnections; + public Network\SocketOptions $socketOptions; + /** * Initializes a new instance of ServerOptions with specified settings. * - * @param bool $noDelay Determines whether the TCP_NODELAY option is enabled, controlling - * the use of the Nagle algorithm. When true, TCP_NODELAY is enabled, - * and the Nagle algorithm is disabled. - * @param int<1, max> $idleConnections The maximum number of idle connections the server will keep open. - * @param Network\SocketOptions $socketOptions Socket configuration options. + * @param bool $no_delay Determines whether the TCP_NODELAY option is enabled, controlling + * the use of the Nagle algorithm. When true, TCP_NODELAY is enabled, + * and the Nagle algorithm is disabled. + * @param int<1, max> $idle_connections The maximum number of idle connections the server will keep open. + * @param Network\SocketOptions $socket_options Socket configuration options. * - * @pure + * @psalm-mutation-free */ - public function __construct( - public readonly bool $noDelay, - public readonly int $idleConnections, - public readonly Network\SocketOptions $socketOptions, - ) { + public function __construct(bool $no_delay, int $idle_connections, Network\SocketOptions $socket_options) + { + $this->noDelay = $no_delay; + $this->idleConnections = $idle_connections; + $this->socketOptions = $socket_options; } /** @@ -75,7 +83,7 @@ public static function default(): static * * @param Network\SocketOptions $socket_options New socket configuration options. * - * @mutation-free + * @psalm-mutation-free */ public function withSocketOptions(Network\SocketOptions $socket_options): ServerOptions { @@ -87,7 +95,7 @@ public function withSocketOptions(Network\SocketOptions $socket_options): Server * * @param bool $enabled The desired state for the TCP_NODELAY option. * - * @mutation-free + * @psalm-mutation-free */ public function withNoDelay(bool $enabled = true): ServerOptions { @@ -99,7 +107,7 @@ public function withNoDelay(bool $enabled = true): ServerOptions * * @param int<1, max> $idleConnections The new maximum number of idle connections to allow. * - * @mutation-free + * @psalm-mutation-free */ public function withIdleConnections(int $idleConnections): ServerOptions { diff --git a/src/Psl/Unix/connect.php b/src/Psl/Unix/connect.php index acca9b49..bdcbf34b 100644 --- a/src/Psl/Unix/connect.php +++ b/src/Psl/Unix/connect.php @@ -23,7 +23,7 @@ function connect(string $path, ?float $timeout = null): Network\StreamSocketInte } // @codeCoverageIgnoreEnd - $socket = Network\Internal\socket_connect("unix://{$path}", timeout: $timeout); + $socket = Network\Internal\socket_connect("unix://$path", timeout: $timeout); /** @psalm-suppress MissingThrowsDocblock */ return new Network\Internal\Socket($socket); diff --git a/tests/unit/Option/SomeTest.php b/tests/unit/Option/SomeTest.php index 26240c6b..94feee49 100644 --- a/tests/unit/Option/SomeTest.php +++ b/tests/unit/Option/SomeTest.php @@ -194,15 +194,6 @@ private function provideTestUnzip(): iterable yield [Option\some([true, false]), true, false]; } - /** - * @dataProvider provideTestUnzipAssertionException - */ - public function testUnzipAssertionException(Option\Option $option): void - { - static::expectException(Type\Exception\AssertException::class); - $option->unzip(); - } - private function provideTestUnzipAssertionException(): iterable { yield [Option\some(null)];