From 9b98077b6b01ee5a1352125bb8a2a6efcf386a28 Mon Sep 17 00:00:00 2001 From: Michael D Johnson Date: Sun, 8 Dec 2024 23:21:03 -0600 Subject: [PATCH 01/15] Explicitly mark all nullable parameters This removes the deprecations introduced in PHP 8.4 --- src/Functional/CompareObjectHashOn.php | 4 ++-- src/Functional/CompareOn.php | 4 ++-- src/Functional/Every.php | 2 +- src/Functional/First.php | 4 ++-- src/Functional/Head.php | 4 ++-- src/Functional/Last.php | 4 ++-- src/Functional/Memoize.php | 2 +- src/Functional/None.php | 2 +- src/Functional/Pick.php | 2 +- src/Functional/Poll.php | 2 +- src/Functional/Reject.php | 2 +- src/Functional/Retry.php | 2 +- src/Functional/Select.php | 2 +- src/Functional/Some.php | 2 +- src/Functional/Tail.php | 4 ++-- src/Functional/Unique.php | 4 ++-- 16 files changed, 23 insertions(+), 23 deletions(-) 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/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/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); From 38356cbfc9f86cf53a4bf52538099030c27e8aa0 Mon Sep 17 00:00:00 2001 From: Michael D Johnson Date: Mon, 9 Dec 2024 00:33:03 -0600 Subject: [PATCH 02/15] Make iterator return types match Iterator PHP 8.4 makes not doing so a deprecation. --- src/Functional/Sequences/ExponentialSequence.php | 10 +++++----- src/Functional/Sequences/LinearSequence.php | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) 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; } From ebaacaacde4eeb9ef86d8199165d873cac0d92ec Mon Sep 17 00:00:00 2001 From: Michael D Johnson Date: Mon, 9 Dec 2024 00:54:04 -0600 Subject: [PATCH 03/15] Skip tesetErrorIsThrownAsException for PHP >= 8.4 PHP 8.4 deprecates the use of E_USER_ERROR. This can mangle the exception thrown. So, skip the test in PHP 8.4. It may be possible to fix up what's being thrown, but I haven't found anything. Since using trigger_error for user errors is now deprecated, error_to_exception() should probably be deprecated as well. But that's a greater decision than making sure tests work correctly. --- tests/Functional/ErrorToExceptionTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) 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); }; From bdfef5f03589778dcf50f052f3d4c83f45815280 Mon Sep 17 00:00:00 2001 From: Michael D Johnson Date: Mon, 9 Dec 2024 00:57:10 -0600 Subject: [PATCH 04/15] Fix message matching in RepeatTest::testNegativeRepeatedtimes PHP 8.4 has changed the format of __FUNCTION__ for closures, at least. --- tests/Functional/RepeatTest.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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); From 885f28b1c482c54d17c77a761f28a6e36ea5ac49 Mon Sep 17 00:00:00 2001 From: Michael D Johnson Date: Mon, 9 Dec 2024 01:08:50 -0600 Subject: [PATCH 05/15] Force doubles to ints in group() PHP 8.4 deprecates the implicit conversion from doubles to ints when doing so would lose precision. However, disallowing doubles as array keys in Functional would be a BC break. --- src/Functional/Group.php | 5 +++++ 1 file changed, 5 insertions(+) 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] = []; } From 129179d25fe535644bec47861fc21b23c0c4b743 Mon Sep 17 00:00:00 2001 From: mattiabasone Date: Mon, 28 Apr 2025 14:28:26 +0200 Subject: [PATCH 06/15] update CI matrix and actions --- .github/workflows/test.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 2c581fd1..2ed4aa3f 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -6,7 +6,7 @@ jobs: test: name: PHP ${{ matrix.php-version }} (${{ matrix.experimental && 'experimental' || 'full support' }}) - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false @@ -17,9 +17,12 @@ jobs: - 7.3 - 7.4 - 8.0 + - 8.1 + - 8.2 + - 8.3 experimental: [false] include: - - php-version: 8.1 + - php-version: 8.4 experimental: true continue-on-error: ${{ matrix.experimental }} @@ -35,7 +38,7 @@ jobs: tools: composer:v2 - name: Install Composer dependencies - uses: ramsey/composer-install@v1 + uses: ramsey/composer-install@v3 with: composer-options: --prefer-dist continue-on-error: ${{ matrix.experimental }} From 30a8115548530677203a3e796e63a0dbef9bba0d Mon Sep 17 00:00:00 2001 From: mattiabasone Date: Mon, 28 Apr 2025 14:36:59 +0200 Subject: [PATCH 07/15] use E_USER_DEPRECATED instead of E_USER_ERROR in test --- tests/Functional/ErrorToExceptionTest.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/tests/Functional/ErrorToExceptionTest.php b/tests/Functional/ErrorToExceptionTest.php index f5280cd8..14c20b65 100644 --- a/tests/Functional/ErrorToExceptionTest.php +++ b/tests/Functional/ErrorToExceptionTest.php @@ -21,16 +21,8 @@ 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); + \trigger_error('Some error', E_USER_DEPRECATED); }; $fn = error_to_exception($origFn); From abeac97daf3ae7ec7333fd315bf2359e2f9615f3 Mon Sep 17 00:00:00 2001 From: mattiabasone Date: Mon, 28 Apr 2025 14:37:22 +0200 Subject: [PATCH 08/15] cs fixes --- src/Functional/Functional.php | 1 - src/Functional/Group.php | 6 +++--- tests/Functional/PartialMethodTest.php | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Functional/Functional.php b/src/Functional/Functional.php index 2856d6a1..5b9fc761 100644 --- a/src/Functional/Functional.php +++ b/src/Functional/Functional.php @@ -12,7 +12,6 @@ final class Functional { - /** * @see \Function\ary */ diff --git a/src/Functional/Group.php b/src/Functional/Group.php index 0a533d44..3cf054bf 100644 --- a/src/Functional/Group.php +++ b/src/Functional/Group.php @@ -21,7 +21,7 @@ * @return array * @no-named-arguments */ -function group($collection, callable $callback) +function group($collection, callable $callback): array { InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1); @@ -33,8 +33,8 @@ 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 (\is_numeric($groupKey)) { + $groupKey = (int) $groupKey; } if (!isset($groups[$groupKey])) { diff --git a/tests/Functional/PartialMethodTest.php b/tests/Functional/PartialMethodTest.php index 2417ed90..e52946b1 100644 --- a/tests/Functional/PartialMethodTest.php +++ b/tests/Functional/PartialMethodTest.php @@ -14,7 +14,6 @@ class PartialMethodTest extends AbstractPartialTestCase { - public function testWithNoArgs(): void { $method = partial_method('execute'); From 9e98ea3a57d2e6152269d1c6de1794d78549d7a5 Mon Sep 17 00:00:00 2001 From: mattiabasone Date: Mon, 28 Apr 2025 14:53:44 +0200 Subject: [PATCH 09/15] update workflows --- .github/workflows/test.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 2ed4aa3f..e5f66d60 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,6 +1,18 @@ name: Test -on: [push, pull_request] +on: + pull_request: + branches: ["main", "master"] + paths-ignore: + - "docs/**" + - "LICENSE" + - "README.md" + push: + branches: ["main", "master"] + paths-ignore: + - "docs/**" + - "LICENSE" + - "README.md" jobs: test: From ffef0ebbfa3912b3511887a74bd8ec49bca068f6 Mon Sep 17 00:00:00 2001 From: mattiabasone Date: Mon, 28 Apr 2025 14:53:59 +0200 Subject: [PATCH 10/15] add test for float keys --- tests/Functional/GroupTest.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/Functional/GroupTest.php b/tests/Functional/GroupTest.php index fd9fe353..62e41749 100644 --- a/tests/Functional/GroupTest.php +++ b/tests/Functional/GroupTest.php @@ -118,4 +118,28 @@ public function testPassNonCallable(): void $this->expectCallableArgumentError('Functional\group', 2); group($this->list, 'undefinedFunction'); } + + public function testFloatGroupKeysAreBeingCastToInteger(): void + { + $values = [5, 10, 11, 15]; + $fn = function ($v, $k, $collection) { + return $v / 5; + }; + + $actual = group($values, $fn); + $expected = [ + 1 => [ + 0 => 5 + ], + 2 => [ + 1 => 10, + 2 => 11 + ], + 3 => [ + 3 => 15 + ] + ]; + + self::assertEquals($expected, $actual); + } } From 62f8d794f5213b8b635b2c3685019d9313fd33bf Mon Sep 17 00:00:00 2001 From: mattiabasone Date: Mon, 1 Sep 2025 11:45:34 +0200 Subject: [PATCH 11/15] update test workflow --- .github/workflows/test.yaml | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e5f66d60..cf85268f 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -3,16 +3,18 @@ name: Test on: pull_request: branches: ["main", "master"] - paths-ignore: - - "docs/**" - - "LICENSE" - - "README.md" + paths: + - "src/**" + - "tests/**" + - "composer.json" + - ".github/workflows/test.yaml" push: branches: ["main", "master"] - paths-ignore: - - "docs/**" - - "LICENSE" - - "README.md" + paths: + - "src/**" + - "tests/**" + - "composer.json" + - ".github/workflows/test.yaml" jobs: test: @@ -32,15 +34,16 @@ jobs: - 8.1 - 8.2 - 8.3 + - 8.4 experimental: [false] include: - - php-version: 8.4 + - php-version: 8.5 experimental: true continue-on-error: ${{ matrix.experimental }} steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v5 - name: Install PHP with extensions uses: shivammathur/setup-php@v2 From cb9a4f4f9717e34bae53fde5ef887353efb31a7d Mon Sep 17 00:00:00 2001 From: mattiabasone Date: Mon, 1 Sep 2025 11:56:33 +0200 Subject: [PATCH 12/15] use the right method for phpunit < 9 --- tests/Functional/RepeatTest.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/Functional/RepeatTest.php b/tests/Functional/RepeatTest.php index b58ee698..6b25d5a8 100644 --- a/tests/Functional/RepeatTest.php +++ b/tests/Functional/RepeatTest.php @@ -12,6 +12,7 @@ use Functional\Exceptions\InvalidArgumentException; use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Runner\Version as PHPUnitVersion; use function Functional\repeat; @@ -48,11 +49,20 @@ public function testNegativeRepeatedTimes(): void // 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 + if (\version_compare(PHPUnitVersion::id(), '9.0.0', '>=') >= 0) { + // PHPUnit 10 changed the wording of the exception message + $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/' - ); + ); + } else { + $this->expectExceptionMessageRegExp( + '/(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); } From 928b9af2874a0b4b345cce169c0a86a6d60b7d73 Mon Sep 17 00:00:00 2001 From: mattiabasone Date: Mon, 1 Sep 2025 12:08:26 +0200 Subject: [PATCH 13/15] cs-fixer ignore env --- .github/workflows/test.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index cf85268f..d52382b0 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -71,3 +71,5 @@ jobs: - name: Check coding style run: composer coding-style continue-on-error: ${{ matrix.experimental }} + env: + PHP_CS_FIXER_IGNORE_ENV: 1 From 00fe3ea084ab8561b6e30e8e65d91bc9f033640c Mon Sep 17 00:00:00 2001 From: mattiabasone Date: Mon, 1 Sep 2025 12:11:15 +0200 Subject: [PATCH 14/15] use the right method for phpunit < 9 --- tests/Functional/RepeatTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Functional/RepeatTest.php b/tests/Functional/RepeatTest.php index 6b25d5a8..de60cb89 100644 --- a/tests/Functional/RepeatTest.php +++ b/tests/Functional/RepeatTest.php @@ -49,7 +49,7 @@ public function testNegativeRepeatedTimes(): void // See https://3v4l.org/Ms79G for message formats // See https://regex101.com/r/hTvW3o/1 for regex setup - if (\version_compare(PHPUnitVersion::id(), '9.0.0', '>=') >= 0) { + if (\version_compare(PHPUnitVersion::id(), '9.0.0') >= 0) { // PHPUnit 10 changed the wording of the exception message $this->expectExceptionMessageMatches( '/(Functional\\\\{closure}' // PHP < 8.4 From 16f9a23ab6a089dfbf4309470c2c417875e7debb Mon Sep 17 00:00:00 2001 From: mattiabasone Date: Mon, 1 Sep 2025 12:17:35 +0200 Subject: [PATCH 15/15] run pcov step only for php version 7.1 which uses older version of phpunit --- .github/workflows/test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index d52382b0..a361c88b 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -63,6 +63,7 @@ jobs: composer require pcov/clobber vendor/bin/pcov clobber continue-on-error: true + if: ${{ matrix.php-version == '7.1' }} - name: Run Tests run: composer tests