From 840f7c932bd4b065336b2f2be0e42aacd16f1f15 Mon Sep 17 00:00:00 2001 From: craig410 Date: Wed, 25 Jun 2025 01:51:56 +0100 Subject: [PATCH 1/7] Support PHP 8.4, update CI workflow --- .github/workflows/test.yml | 34 +++++++--------------------------- composer.json | 8 ++++---- 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3f0673d..f511158 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,13 +16,11 @@ jobs: php_version: - '8.2' - '8.3' - dependencies: + - '8.4' + dependency-versions: + - 'lowest' - 'default' - include: - - php_version: '8.2' - dependencies: 'lowest' - - php_version: '8.3' - dependencies: 'lowest' + steps: - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -35,28 +33,10 @@ jobs: with: show-progress: false - - name: Get Composer Cache Directory - id: composer-cache - run: | - echo "::set-output name=dir::$(composer config cache-files-dir)" - - - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-composer-${{ matrix.dependencies }} - - name: Install composer dependencies - env: - DEPENDENCIES: ${{ matrix.dependencies }} - run: | - if [ $DEPENDENCIES == 'lowest' ] - then - composer update --prefer-lowest --no-interaction --no-progress - else - composer install --no-interaction --no-progress - fi + uses: ramsey/composer-install@v3 + with: + dependency-versions: ${{ matrix.dependency-versions }} - name: Run unit tests run: | diff --git a/composer.json b/composer.json index 873dc88..9131e73 100644 --- a/composer.json +++ b/composer.json @@ -16,10 +16,10 @@ ], "require": { "ext-json": "*", - "ext-mbstring": "~8.2.0 || ~8.3.0", - "ext-pdo": "~8.2.0 || ~8.3.0", - "ext-sodium": "~8.2.0 || ~8.3.0", - "php": "~8.2.0 || ~8.3.0", + "ext-mbstring": "~8.2.0 || ~8.3.0 || ~8.4.0", + "ext-pdo": "~8.2.0 || ~8.3.0 || ~8.4.0", + "ext-sodium": "~8.2.0 || ~8.3.0 || ~8.4.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "require-dev": { From 5b932e05d1a0997a2057eb07ce0dadf3c997e017 Mon Sep 17 00:00:00 2001 From: craig410 Date: Wed, 25 Jun 2025 01:52:49 +0100 Subject: [PATCH 2/7] fgetcsv needs escape parameter explicitly specified --- test/unit/CSV/CSVWriterTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/CSV/CSVWriterTest.php b/test/unit/CSV/CSVWriterTest.php index 899f1ef..61dfb98 100644 --- a/test/unit/CSV/CSVWriterTest.php +++ b/test/unit/CSV/CSVWriterTest.php @@ -248,7 +248,7 @@ public function test_it_optionally_writes_byte_order_mark_at_start_of_file($writ if ($write_bom) { $this->assertSame(CSVWriter::UTF8_BOM, fread($file, strlen(CSVWriter::UTF8_BOM))); } - $this->assertSame(['first'], fgetcsv($file)); + $this->assertSame(['first'], fgetcsv($file, escape: '\\')); } #[TestWith([['is' => 'jumbled', 'our' => 'up']])] @@ -284,7 +284,7 @@ protected function assertCSVContent(array $expect, $file) { rewind($file); $actual = []; - while ($row = fgetcsv($file)) { + while ($row = fgetcsv($file, escape: '\\')) { $actual[] = $row; } From 2acf23962fcf4f51911c40b5aec40453324b7620 Mon Sep 17 00:00:00 2001 From: craig410 Date: Wed, 25 Jun 2025 01:53:43 +0100 Subject: [PATCH 3/7] Fix nullable property deprecations for 8.4 --- src/DateTime/DateString.php | 6 +++--- src/DeploymentConfig/DeploymentConfig.php | 2 +- src/Monitoring/OperationTimer.php | 2 +- test/unit/Mutex/BasicPDOStatementStub.php | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/DateTime/DateString.php b/src/DateTime/DateString.php index 8fa4d46..059415e 100644 --- a/src/DateTime/DateString.php +++ b/src/DateTime/DateString.php @@ -22,7 +22,7 @@ class DateString * * @return string */ - public static function format(\DateTimeImmutable $date = NULL, $format, $empty_value = '') + public static function format(?\DateTimeImmutable $date, $format, $empty_value = '') { if ( ! $date) { return $empty_value; @@ -52,7 +52,7 @@ public static function isoMS(?\DateTimeImmutable $date, ?string $empty_value = ' * * @return string */ - public static function ymd(\DateTimeImmutable $date = NULL, $empty_value = '') + public static function ymd(?\DateTimeImmutable $date, $empty_value = '') { return static::format($date, 'Y-m-d', $empty_value); } @@ -65,7 +65,7 @@ public static function ymd(\DateTimeImmutable $date = NULL, $empty_value = '') * * @return string */ - public static function ymdhis(\DateTimeImmutable $date = NULL, $empty_value = '') + public static function ymdhis(?\DateTimeImmutable $date, $empty_value = '') { return static::format($date, 'Y-m-d H:i:s', $empty_value); } diff --git a/src/DeploymentConfig/DeploymentConfig.php b/src/DeploymentConfig/DeploymentConfig.php index 7d24655..cf57a67 100644 --- a/src/DeploymentConfig/DeploymentConfig.php +++ b/src/DeploymentConfig/DeploymentConfig.php @@ -58,7 +58,7 @@ public static function instance() * * @param array|NULL $env_vars - if missing will default to reading from $_SERVER */ - protected function __construct(array $env_vars = NULL) + protected function __construct(?array $env_vars = NULL) { if ($env_vars === NULL) { $env_vars = $_SERVER; diff --git a/src/Monitoring/OperationTimer.php b/src/Monitoring/OperationTimer.php index 3ed2b97..28547c5 100644 --- a/src/Monitoring/OperationTimer.php +++ b/src/Monitoring/OperationTimer.php @@ -17,7 +17,7 @@ class OperationTimer protected RealtimeClock $realtime_clock; - public function __construct(MetricsAgent $metrics_agent, RealtimeClock $realtime_clock = NULL) + public function __construct(MetricsAgent $metrics_agent, ?RealtimeClock $realtime_clock = NULL) { $this->metrics_agent = $metrics_agent; $this->realtime_clock = $realtime_clock ?? new RealtimeClock(); diff --git a/test/unit/Mutex/BasicPDOStatementStub.php b/test/unit/Mutex/BasicPDOStatementStub.php index ad45199..e63689e 100644 --- a/test/unit/Mutex/BasicPDOStatementStub.php +++ b/test/unit/Mutex/BasicPDOStatementStub.php @@ -38,7 +38,7 @@ class BasicPDOStatementStub extends \PDOStatement public function __construct(array $result) { $this->result = $result; } - public function fetchAll(int $fetch_style = NULL, mixed ...$fetch_argument): array + public function fetchAll(?int $fetch_style = NULL, mixed ...$fetch_argument): array { if (PHP_VERSION_ID < 80100) { // Before 8.1, ints and floats were returned as strings From a7bb40a980bfede9c3fa2ce47f1d8515b66fa8a8 Mon Sep 17 00:00:00 2001 From: craig410 Date: Wed, 25 Jun 2025 15:27:38 +0100 Subject: [PATCH 4/7] Fix closure location message assertion for 8.4 --- test/unit/Logging/ExternalCallSiteFinderTest.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/unit/Logging/ExternalCallSiteFinderTest.php b/test/unit/Logging/ExternalCallSiteFinderTest.php index 4583f1f..22f46a4 100644 --- a/test/unit/Logging/ExternalCallSiteFinderTest.php +++ b/test/unit/Logging/ExternalCallSiteFinderTest.php @@ -129,8 +129,20 @@ function log_ab8123723123($asserter) { [ <<<'PHP' // Anonymous func + use test\unit\Ingenerator\PHPUtils\Logging\ExternalCallSiteFinderTest; return (function ($asserter) { - return $asserter->test(['file' => __FILE__, 'line' => __LINE__, 'function' => '{closure}']); + if (PHP_VERSION_ID < 80400) { + $expected_func_name = '{closure}'; + } else { + $expected_func_name = sprintf("%s->{closure:%s:%d}", + ExternalCallSiteFinderTest::class, + __FILE__, + // it's the line with the 'function' keyword + (__LINE__-8) + ); + } + return $asserter->test(['file' => __FILE__, 'line' => __LINE__, 'function' => $expected_func_name]); + })($asserter); PHP , From baae0517e0ab3a492f3201c2551c758fa1683a34 Mon Sep 17 00:00:00 2001 From: craig410 Date: Thu, 26 Jun 2025 00:31:46 +0100 Subject: [PATCH 5/7] Bump phpunit for 8.4 support --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 9131e73..9cf80e3 100644 --- a/composer.json +++ b/composer.json @@ -23,8 +23,8 @@ "psr/log": "^1.1 || ^2.0 || ^3.0" }, "require-dev": { - "mikey179/vfsstream": "^1.6.11", - "phpunit/phpunit": "^11.0", + "mikey179/vfsstream": "^1.6.12", + "phpunit/phpunit": "^11.5", "ergebnis/phpunit-slow-test-detector": "^2.15" }, "support": { From 7022e70dbff6a6e45914fca2ee6403136ea3eea1 Mon Sep 17 00:00:00 2001 From: craig410 Date: Thu, 26 Jun 2025 00:33:07 +0100 Subject: [PATCH 6/7] Bump changelog for release --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc07d26..72fd167 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ### Unreleased +### v2.5.0 (2025-06-26) + +* Support PHP 8.4 + ### v2.4.0 (2025-06-05) * Fix `ObjectPropertyRipper` to handle `stdClass` objects From 8fd121e7441b66a1a4d7ed684f76f482079ba189 Mon Sep 17 00:00:00 2001 From: craig410 Date: Thu, 26 Jun 2025 14:17:49 +0100 Subject: [PATCH 7/7] Remove workaround to provide 8.1 support in PDOStatementStub --- test/unit/Mutex/BasicPDOStatementStub.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/unit/Mutex/BasicPDOStatementStub.php b/test/unit/Mutex/BasicPDOStatementStub.php index e63689e..9a05926 100644 --- a/test/unit/Mutex/BasicPDOStatementStub.php +++ b/test/unit/Mutex/BasicPDOStatementStub.php @@ -40,18 +40,6 @@ public function __construct(array $result) { $this->result = $result; } public function fetchAll(?int $fetch_style = NULL, mixed ...$fetch_argument): array { - if (PHP_VERSION_ID < 80100) { - // Before 8.1, ints and floats were returned as strings - // https://www.php.net/manual/en/migration81.incompatible.php#migration81.incompatible.pdo.mysql - $this->result = array_map( - fn($row) => array_map( - fn($column) => (is_int($column) || \is_float($column)) ? (string) $column : $column, - $row - ), - $this->result - ); - } - if ($fetch_style === NULL) { return $this->result; } elseif ($fetch_style === PDO::FETCH_COLUMN) {