diff --git a/src/Functional/CompareObjectHashOn.php b/src/Functional/CompareObjectHashOn.php index c4194479..35e0ce04 100644 --- a/src/Functional/CompareObjectHashOn.php +++ b/src/Functional/CompareObjectHashOn.php @@ -14,11 +14,11 @@ * Returns a comparison function that can be used with e.g. `usort()` * * @param callable $comparison A function that compares the two values. Pick e.g. strcmp() or strnatcasecmp() - * @param callable $keyFunction A function that takes an argument and returns the value that should be compared + * @param callable|null $keyFunction A function that takes an argument and returns the value that should be compared * @return callable * @no-named-arguments */ -function compare_object_hash_on(callable $comparison, callable $keyFunction = null) +function compare_object_hash_on(callable $comparison, ?callable $keyFunction = null) { $keyFunction = $keyFunction ? compose($keyFunction, 'spl_object_hash') : 'spl_object_hash'; diff --git a/src/Functional/CompareOn.php b/src/Functional/CompareOn.php index b0e5cda0..2d7f2486 100644 --- a/src/Functional/CompareOn.php +++ b/src/Functional/CompareOn.php @@ -14,11 +14,11 @@ * Returns a comparison function that can be used with e.g. `usort()` * * @param callable $comparison A function that compares the two values. Pick e.g. strcmp() or strnatcasecmp() - * @param callable $reducer A function that takes an argument and returns the value that should be compared + * @param callable|null $reducer A function that takes an argument and returns the value that should be compared * @return callable * @no-named-arguments */ -function compare_on(callable $comparison, callable $reducer = null) +function compare_on(callable $comparison, ?callable $reducer = null) { if ($reducer === null) { return static function ($left, $right) use ($comparison) { diff --git a/src/Functional/Every.php b/src/Functional/Every.php index e4d8ff62..667cd72b 100644 --- a/src/Functional/Every.php +++ b/src/Functional/Every.php @@ -22,7 +22,7 @@ * @return bool * @no-named-arguments */ -function every($collection, callable $callback = null) +function every($collection, ?callable $callback = null) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); diff --git a/src/Functional/First.php b/src/Functional/First.php index 87db945e..20c39274 100644 --- a/src/Functional/First.php +++ b/src/Functional/First.php @@ -19,11 +19,11 @@ * arguments will be element, index, collection * * @param Traversable|array $collection - * @param callable $callback + * @param callable|null $callback * @return mixed * @no-named-arguments */ -function first($collection, callable $callback = null) +function first($collection, ?callable $callback = null) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); diff --git a/src/Functional/Group.php b/src/Functional/Group.php index af2ed3a1..0a533d44 100644 --- a/src/Functional/Group.php +++ b/src/Functional/Group.php @@ -32,6 +32,11 @@ function group($collection, callable $callback) InvalidArgumentException::assertValidArrayKey($groupKey, __FUNCTION__); + // Avoid implicit precision-loss from doubles (which cannot be keys) + if (is_numeric($groupKey)) { + $groupKey = intval($groupKey); + } + if (!isset($groups[$groupKey])) { $groups[$groupKey] = []; } diff --git a/src/Functional/Head.php b/src/Functional/Head.php index c6de5e86..3019ee86 100644 --- a/src/Functional/Head.php +++ b/src/Functional/Head.php @@ -17,11 +17,11 @@ * Alias for Functional\first * * @param Traversable|array $collection - * @param callable $callback + * @param callable|null $callback * @return mixed * @no-named-arguments */ -function head($collection, callable $callback = null) +function head($collection, ?callable $callback = null) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); diff --git a/src/Functional/Last.php b/src/Functional/Last.php index 8989f945..3c861d31 100644 --- a/src/Functional/Last.php +++ b/src/Functional/Last.php @@ -18,11 +18,11 @@ * Callback arguments will be element, index, collection * * @param Traversable|array $collection - * @param callable $callback + * @param callable|null $callback * @return mixed * @no-named-arguments */ -function last($collection, callable $callback = null) +function last($collection, ?callable $callback = null) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); diff --git a/src/Functional/Memoize.php b/src/Functional/Memoize.php index 3813e3ce..aca716b5 100644 --- a/src/Functional/Memoize.php +++ b/src/Functional/Memoize.php @@ -21,7 +21,7 @@ * @return mixed * @no-named-arguments */ -function memoize(callable $callback = null, $arguments = [], $key = null) +function memoize(?callable $callback = null, $arguments = [], $key = null) { static $storage = []; if ($callback === null) { diff --git a/src/Functional/None.php b/src/Functional/None.php index edbd33a9..c90e4055 100644 --- a/src/Functional/None.php +++ b/src/Functional/None.php @@ -22,7 +22,7 @@ * @return bool * @no-named-arguments */ -function none($collection, callable $callback = null) +function none($collection, ?callable $callback = null) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); diff --git a/src/Functional/Pick.php b/src/Functional/Pick.php index b4be3c13..7d29f92c 100644 --- a/src/Functional/Pick.php +++ b/src/Functional/Pick.php @@ -24,7 +24,7 @@ * @return mixed * @no-named-arguments */ -function pick($collection, $index, $default = null, callable $callback = null) +function pick($collection, $index, $default = null, ?callable $callback = null) { InvalidArgumentException::assertArrayAccess($collection, __FUNCTION__, 1); diff --git a/src/Functional/Poll.php b/src/Functional/Poll.php index 7c632d77..8f920d24 100644 --- a/src/Functional/Poll.php +++ b/src/Functional/Poll.php @@ -26,7 +26,7 @@ * @return boolean * @no-named-arguments */ -function poll(callable $callback, $timeout, Traversable $delaySequence = null) +function poll(callable $callback, $timeout, ?Traversable $delaySequence = null) { InvalidArgumentException::assertIntegerGreaterThanOrEqual($timeout, 0, __FUNCTION__, 2); diff --git a/src/Functional/Reject.php b/src/Functional/Reject.php index 505acc91..0efddeee 100644 --- a/src/Functional/Reject.php +++ b/src/Functional/Reject.php @@ -22,7 +22,7 @@ * @return array * @no-named-arguments */ -function reject($collection, callable $callback = null) +function reject($collection, ?callable $callback = null) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); diff --git a/src/Functional/Retry.php b/src/Functional/Retry.php index 40ef0fa7..4466542a 100644 --- a/src/Functional/Retry.php +++ b/src/Functional/Retry.php @@ -29,7 +29,7 @@ * @return mixed Return value of the function * @no-named-arguments */ -function retry(callable $callback, $retries, Traversable $delaySequence = null) +function retry(callable $callback, $retries, ?Traversable $delaySequence = null) { InvalidArgumentException::assertIntegerGreaterThanOrEqual($retries, 1, __FUNCTION__, 2); diff --git a/src/Functional/Select.php b/src/Functional/Select.php index 93420357..eb0ab73d 100644 --- a/src/Functional/Select.php +++ b/src/Functional/Select.php @@ -22,7 +22,7 @@ * @return array * @no-named-arguments */ -function select($collection, callable $callback = null) +function select($collection, ?callable $callback = null) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); diff --git a/src/Functional/Sequences/ExponentialSequence.php b/src/Functional/Sequences/ExponentialSequence.php index 0a8b8d47..b550545a 100644 --- a/src/Functional/Sequences/ExponentialSequence.php +++ b/src/Functional/Sequences/ExponentialSequence.php @@ -38,28 +38,28 @@ public function __construct($start, $percentage) $this->percentage = $percentage; } - public function current() + public function current(): int { return $this->value; } - public function next() + public function next(): void { $this->value = (int) \round(\pow($this->start * (1 + $this->percentage / 100), $this->times)); $this->times++; } - public function key() + public function key(): ?int { return null; } - public function valid() + public function valid(): bool { return true; } - public function rewind() + public function rewind(): void { $this->times = 1; $this->value = $this->start; diff --git a/src/Functional/Sequences/LinearSequence.php b/src/Functional/Sequences/LinearSequence.php index ad99dfbb..e7345a6a 100644 --- a/src/Functional/Sequences/LinearSequence.php +++ b/src/Functional/Sequences/LinearSequence.php @@ -34,27 +34,27 @@ public function __construct($start, $amount) $this->amount = $amount; } - public function current() + public function current(): int { return $this->value; } - public function next() + public function next(): void { $this->value += $this->amount; } - public function key() + public function key(): int { return 0; } - public function valid() + public function valid(): bool { return true; } - public function rewind() + public function rewind(): void { $this->value = $this->start; } diff --git a/src/Functional/Some.php b/src/Functional/Some.php index df27e0a8..9ae81ade 100644 --- a/src/Functional/Some.php +++ b/src/Functional/Some.php @@ -22,7 +22,7 @@ * @return bool * @no-named-arguments */ -function some($collection, callable $callback = null) +function some($collection, ?callable $callback = null) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); diff --git a/src/Functional/Tail.php b/src/Functional/Tail.php index 495131d2..33637ffe 100644 --- a/src/Functional/Tail.php +++ b/src/Functional/Tail.php @@ -18,11 +18,11 @@ * Takes an optional callback for filtering the collection. * * @param Traversable|array $collection - * @param callable $callback + * @param callable|null $callback * @return array * @no-named-arguments */ -function tail($collection, callable $callback = null) +function tail($collection, ?callable $callback = null) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); diff --git a/src/Functional/Unique.php b/src/Functional/Unique.php index 656ac2be..cfa12442 100644 --- a/src/Functional/Unique.php +++ b/src/Functional/Unique.php @@ -17,12 +17,12 @@ * Returns an array of unique elements * * @param Traversable|array $collection - * @param callable $callback + * @param callable|null $callback * @param bool $strict * @return array * @no-named-arguments */ -function unique($collection, callable $callback = null, $strict = true) +function unique($collection, ?callable $callback = null, $strict = true) { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); diff --git a/tests/Functional/ErrorToExceptionTest.php b/tests/Functional/ErrorToExceptionTest.php index e68893e0..f5280cd8 100644 --- a/tests/Functional/ErrorToExceptionTest.php +++ b/tests/Functional/ErrorToExceptionTest.php @@ -21,6 +21,14 @@ class ErrorToExceptionTest extends AbstractTestCase { public function testErrorIsThrownAsException(): void { + if (PHP_VERSION_ID >= 80401) { + // As of PHP 8.4, trigger_error('...', E_USER_ERROR) is deprecated + // This means the actual error thrown is different from the one + // attempted + // See https://3v4l.org/MmFA6. + self::markTestSkipped('Only works with PHP <8.4 due to deprecated E_USER_ERROR'); + } + $origFn = function () { \trigger_error('Some error', E_USER_ERROR); }; diff --git a/tests/Functional/RepeatTest.php b/tests/Functional/RepeatTest.php index 9ab5e8a4..b58ee698 100644 --- a/tests/Functional/RepeatTest.php +++ b/tests/Functional/RepeatTest.php @@ -45,8 +45,13 @@ public function test(): void public function testNegativeRepeatedTimes(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage( - 'Functional\{closure}() expects parameter 1 to be positive integer, negative integer given' + + // See https://3v4l.org/Ms79G for message formats + // See https://regex101.com/r/hTvW3o/1 for regex setup + $this->expectExceptionMessageMatches( + '/(Functional\\\\{closure}' // PHP < 8.4 + . '|{closure:Functional\\\\repeat\(\):[0-9]+})' // PHP 8.4+ + . '\(\) expects parameter 1 to be positive integer, negative integer given/' ); repeat([$this->repeated, 'foo'])(-1);