Skip to content

Commit

Permalink
qa: remove purity of provided callbacks
Browse files Browse the repository at this point in the history
It is almost impossible to ensure purity and in some cases that also makes no sense. Especially when it comes to mappings, purity is impossible and therefore, dropping this in a bugfix release might suffice.

Signed-off-by: Maximilian Bösing <[email protected]>
  • Loading branch information
boesing committed Jun 13, 2022
1 parent 34c5f65 commit a539007
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 48 deletions.
8 changes: 4 additions & 4 deletions src/ArrayInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ public function isEmpty(): bool;
/**
* Tests if all elements satisfy the given predicate.
*
* @psalm-param pure-callable(TValue):bool $callback
* @psalm-param callable(TValue):bool $callback
*/
public function allSatisfy(callable $callback): bool;

/**
* Tests for the existence of an element that satisfies the given predicate.
*
* @psalm-param pure-callable(TValue):bool $callback
* @psalm-param callable(TValue):bool $callback
*/
public function exists(callable $callback): bool;

Expand All @@ -71,8 +71,8 @@ public function count(): int;

/**
* @template TReducedValue
* @param pure-callable(TReducedValue,TValue):TReducedValue $callback
* @param TReducedValue $initial
* @param callable(TReducedValue,TValue):TReducedValue $callback
* @param TReducedValue $initial
* @return TReducedValue
*/
public function reduce(callable $callback, $initial);
Expand Down
14 changes: 13 additions & 1 deletion src/Array_.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ protected function valueComparator(): callable
public function allSatisfy(callable $callback): bool
{
foreach ($this->data as $value) {
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
if (! $callback($value)) {
return false;
}
Expand All @@ -118,6 +122,10 @@ public function allSatisfy(callable $callback): bool
public function exists(callable $callback): bool
{
foreach ($this->data as $value) {
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
if ($callback($value)) {
return true;
}
Expand All @@ -131,7 +139,11 @@ public function reduce(callable $callback, $initial)
{
$instance = clone $this;

/** @psalm-suppress MixedReturnStatement The return value of the callback ensures the return type. */
/**
* @psalm-suppress MixedReturnStatement The return value of the callback ensures the return type.
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
return array_reduce($instance->data, $callback, $initial);
}
}
58 changes: 52 additions & 6 deletions src/Map.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ public function sort(?callable $callback = null): MapInterface
return $instance;
}

/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
uasort($data, $callback);
$instance->data = $data;

Expand All @@ -84,10 +88,14 @@ public function diffKeys(MapInterface $other, ?callable $keyComparator = null):

/**
* @psalm-var array<TKey,TValue> $diff1
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$diff1 = array_diff_ukey($instance->data, $otherData, $keyComparator);
/**
* @psalm-var array<TKey,TValue> $diff2
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$diff2 = array_diff_ukey($otherData, $instance->data, $keyComparator);
$merged = array_merge(
Expand All @@ -101,7 +109,7 @@ public function diffKeys(MapInterface $other, ?callable $keyComparator = null):
}

/**
* @psalm-return pure-callable(TKey,TKey):int
* @psalm-return callable(TKey,TKey):int
*/
private function keyComparator(): callable
{
Expand All @@ -118,6 +126,10 @@ public function toOrderedList(?callable $sorter = null): OrderedListInterface

$data = $this->data;

/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
usort($data, $sorter);

return new GenericOrderedList($data);
Expand All @@ -128,6 +140,10 @@ public function filter(callable $callback): MapInterface
$instance = clone $this;
$filtered = [];
foreach ($instance->data as $key => $value) {
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
if (! $callback($value, $key)) {
continue;
}
Expand Down Expand Up @@ -179,8 +195,8 @@ public function intersect(MapInterface $other, ?callable $valueComparator = null

/**
* @psalm-param MapInterface<TKey,TValue> $other
* @psalm-param (pure-callable(TValue,TValue):int)|null $valueComparator
* @psalm-param (pure-callable(TKey,TKey):int)|null $keyComparator
* @psalm-param (callable(TValue,TValue):int)|null $valueComparator
* @psalm-param (callable(TKey,TKey):int)|null $keyComparator
* @psalm-return array<TKey,TValue>
* @phpcsSuppress SlevomatCodingStandard.Classes.UnusedPrivateElements.UnusedMethod
*/
Expand All @@ -189,6 +205,8 @@ private function intersection(MapInterface $other, ?callable $valueComparator, ?
if ($valueComparator && $keyComparator) {
/**
* @psalm-var array<TKey,TValue> $intersection
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$intersection = array_uintersect_uassoc(
$this->data,
Expand All @@ -203,6 +221,8 @@ private function intersection(MapInterface $other, ?callable $valueComparator, ?
if ($keyComparator) {
/**
* @psalm-var array<TKey,TValue> $intersection
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$intersection = array_intersect_ukey($this->data, $other->toNativeArray(), $keyComparator);

Expand All @@ -215,6 +235,8 @@ private function intersection(MapInterface $other, ?callable $valueComparator, ?

/**
* @psalm-var array<TKey,TValue> $intersection
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$intersection = array_uintersect($this->data, $other->toNativeArray(), $valueComparator);

Expand Down Expand Up @@ -252,6 +274,8 @@ public function diff(MapInterface $other, ?callable $valueComparator = null): Ma
{
/**
* @psalm-var array<TKey,TValue> $diff1
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$diff1 = array_udiff(
$this->toNativeArray(),
Expand All @@ -261,6 +285,8 @@ public function diff(MapInterface $other, ?callable $valueComparator = null): Ma

/**
* @psalm-var array<TKey,TValue> $diff2
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$diff2 = array_udiff(
$other->toNativeArray(),
Expand Down Expand Up @@ -303,13 +329,17 @@ public function removeElement($element): MapInterface

/**
* @template TNewValue
* @psalm-param pure-callable(TValue,TKey):TNewValue $callback
* @psalm-param callable(TValue,TKey):TNewValue $callback
* @psalm-return MapInterface<TKey,TNewValue>
*/
public function map(callable $callback): MapInterface
{
$data = [];
foreach ($this->data as $key => $value) {
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$data[$key] = $callback($value, $key);
}

Expand All @@ -326,6 +356,10 @@ public function partition(callable $callback): array
$filtered = $unfiltered = [];

foreach ($this->data as $key => $element) {
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
if ($callback($element)) {
$filtered[$key] = $element;
continue;
Expand All @@ -344,7 +378,7 @@ public function partition(callable $callback): array

/**
* @template TGroup of non-empty-string
* @psalm-param pure-callable(TValue):TGroup $callback
* @psalm-param callable(TValue):TGroup $callback
*
* @psalm-return MapInterface<TGroup,MapInterface<TKey,TValue>>
*/
Expand All @@ -356,6 +390,10 @@ public function group(callable $callback): MapInterface
$groups = new GenericMap([]);

foreach ($this->data as $key => $value) {
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$groupIdentifier = $callback($value);
try {
$group = $groups->get($groupIdentifier);
Expand Down Expand Up @@ -411,6 +449,10 @@ public function sortByKey(?callable $sorter = null): MapInterface
$sorter = $sorter ?? $this->keyComparator();
$data = $this->data;
$instance = clone $this;
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
uksort($data, $sorter);
$instance->data = $data;

Expand All @@ -429,7 +471,7 @@ public function join(string $separator = ''): string

/**
* @template TNewKey of string
* @param pure-callable(TKey,TValue):TNewKey $keyGenerator
* @param callable(TKey,TValue):TNewKey $keyGenerator
*
* @return MapInterface<TNewKey,TValue>
* @throws RuntimeException if a new key is being generated more than once.
Expand All @@ -440,6 +482,10 @@ public function keyExchange(callable $keyGenerator): MapInterface
$exchanged = new GenericMap();

foreach ($this->data as $key => $value) {
/**
* @psalm-suppress ImpureFunctionCall Upstream projects have to ensure that they do not manipulate the
* value here.
*/
$newKey = $keyGenerator($key, $value);
if ($exchanged->has($newKey)) {
throw new RuntimeException(sprintf(
Expand Down
32 changes: 16 additions & 16 deletions src/MapInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface MapInterface extends ArrayInterface, JsonSerializable
* Filters out all values not matched by the callback.
* This method is the equivalent of `array_filter`.
*
* @psalm-param pure-callable(TValue,TKey):bool $callback
* @psalm-param callable(TValue,TKey):bool $callback
* @psalm-return MapInterface<TKey,TValue>
*/
public function filter(callable $callback): MapInterface;
Expand All @@ -29,7 +29,7 @@ public function filter(callable $callback): MapInterface;
* Sorts the items by using either the given callback or the native `SORT_NATURAL` logic of PHP.
* This method is the equivalent of `sort`/`usort`.
*
* @psalm-param (pure-callable(TValue,TValue):int)|null $callback
* @psalm-param (callable(TValue,TValue):int)|null $callback
* @psalm-return MapInterface<TKey,TValue>
*/
public function sort(?callable $callback = null): MapInterface;
Expand All @@ -51,7 +51,7 @@ public function merge(MapInterface ...$stack): MapInterface;
* This method is the equivalent of `array_diff`.
*
* @psalm-param MapInterface<TKey,TValue> $other
* @psalm-param (pure-callable(TKey,TKey):int)|null $keyComparator
* @psalm-param (callable(TKey,TKey):int)|null $keyComparator
* @psalm-return MapInterface<TKey,TValue>
*/
public function diffKeys(MapInterface $other, ?callable $keyComparator = null): MapInterface;
Expand All @@ -61,7 +61,7 @@ public function diffKeys(MapInterface $other, ?callable $keyComparator = null):
* This method is the equivalent of `array_map`.
*
* @template TNewValue
* @psalm-param pure-callable(TValue,TKey):TNewValue $callback
* @psalm-param callable(TValue,TKey):TNewValue $callback
* @psalm-return MapInterface<TKey,TNewValue>
*/
public function map(callable $callback): MapInterface;
Expand All @@ -74,7 +74,7 @@ public function map(callable $callback): MapInterface;
* This method is the equivalent of `array_intersect`.
*
* @psalm-param MapInterface<TKey,TValue> $other
* @psalm-param (pure-callable(TValue,TValue):int)|null $valueComparator
* @psalm-param (callable(TValue,TValue):int)|null $valueComparator
* @psalm-return MapInterface<TKey,TValue>
*/
public function intersect(MapInterface $other, ?callable $valueComparator = null): MapInterface;
Expand All @@ -87,7 +87,7 @@ public function intersect(MapInterface $other, ?callable $valueComparator = null
* This method is the equivalent of `array_diff`.
*
* @psalm-param MapInterface<TKey,TValue> $other
* @psalm-param (pure-callable(TValue,TValue):int)|null $valueComparator
* @psalm-param (callable(TValue,TValue):int)|null $valueComparator
* @psalm-return MapInterface<TKey,TValue>
*/
public function diff(MapInterface $other, ?callable $valueComparator = null): MapInterface;
Expand All @@ -97,7 +97,7 @@ public function diff(MapInterface $other, ?callable $valueComparator = null): Ma
* The items are being sorted by using the provided sorter. In case there is no sorter provided, the values
* are just passed in the order they werestored in this map.
*
* @psalm-param (pure-callable(TValue,TValue):int)|null $sorter
* @psalm-param (callable(TValue,TValue):int)|null $sorter
* @psalm-return OrderedListInterface<TValue>
*/
public function toOrderedList(?callable $sorter = null): OrderedListInterface;
Expand Down Expand Up @@ -163,7 +163,7 @@ public function get(string $key);
*
* @psalm-param MapInterface<TKey,TValue> $other
* @psalm-return MapInterface<TKey,TValue>
* @psalm-param (pure-callable(TValue,TValue):int)|null $valueComparator
* @psalm-param (callable(TValue,TValue):int)|null $valueComparator
*/
public function intersectAssoc(MapInterface $other, ?callable $valueComparator = null): MapInterface;

Expand All @@ -174,7 +174,7 @@ public function intersectAssoc(MapInterface $other, ?callable $valueComparator =
*
* @psalm-param MapInterface<TKey,TValue> $other
* @psalm-return MapInterface<TKey,TValue>
* @psalm-param (pure-callable(TKey,TKey):int)|null $keyComparator
* @psalm-param (callable(TKey,TKey):int)|null $keyComparator
*/
public function intersectUsingKeys(MapInterface $other, ?callable $keyComparator = null): MapInterface;

Expand All @@ -186,8 +186,8 @@ public function intersectUsingKeys(MapInterface $other, ?callable $keyComparator
* This method is the equivalent of `array_intersect_assoc`/`array_intersect_uassoc`/`array_intersect_key`/`array_intersect_ukey`.
*
* @psalm-param MapInterface<TKey,TValue> $other
* @psalm-param (pure-callable(TValue,TValue):int)|null $valueComparator
* @psalm-param (pure-callable(TKey,TKey):int)|null $keyComparator
* @psalm-param (callable(TValue,TValue):int)|null $valueComparator
* @psalm-param (callable(TKey,TKey):int)|null $keyComparator
* @psalm-return MapInterface<TKey,TValue>
*/
public function intersectUserAssoc(
Expand All @@ -207,7 +207,7 @@ public function has(string $key): bool;
/**
* Partitions the current map into those items which are filtered by the callback and those which don't.
*
* @psalm-param pure-callable(TValue):bool $callback
* @psalm-param callable(TValue):bool $callback
* @psalm-return array{0:MapInterface<TKey,TValue>,1:MapInterface<TKey,TValue>}
*/
public function partition(callable $callback): array;
Expand All @@ -216,7 +216,7 @@ public function partition(callable $callback): array;
* Groups the items of this object by using the callback.
*
* @template TGroup of non-empty-string
* @psalm-param pure-callable(TValue):TGroup $callback
* @psalm-param callable(TValue):TGroup $callback
*
* @psalm-return MapInterface<TGroup,MapInterface<TKey,TValue>>
*/
Expand All @@ -235,15 +235,15 @@ public function slice(int $length): MapInterface;
* After the method has been executed properly, an `MappedErrorCollection` is being thrown so one can
* see what item key actually failed executing the callback.
*
* @param pure-callable(TValue,TKey):void $callback
* @param callable(TValue,TKey):void $callback
* @throws MappedErrorCollection If an error occured during execution.
*/
public function forAll(callable $callback): ForAllPromiseInterface;

/**
* Sorts the map by sorting its keys.
*
* @param (pure-callable(TKey,TKey):int)|null $sorter
* @param (callable(TKey,TKey):int)|null $sorter
*
* @psalm-return MapInterface<TKey,TValue>
*/
Expand All @@ -261,7 +261,7 @@ public function join(string $separator = ''): string;
* Creates a new map where the keys of items might have been exchanged with another key.
*
* @template TNewKey of string
* @param pure-callable(TKey,TValue):TNewKey $keyGenerator
* @param callable(TKey,TValue):TNewKey $keyGenerator
*
* @return MapInterface<TNewKey,TValue>
* @throws RuntimeException if a new key is being generated more than once.
Expand Down
Loading

0 comments on commit a539007

Please sign in to comment.