From a776db259991340705746a985bb7282543cc471d Mon Sep 17 00:00:00 2001 From: craig410 Date: Wed, 21 Oct 2026 10:21:49 +0100 Subject: [PATCH 1/7] Support PHP 8.5 & 8.6 --- .github/workflows/test.yml | 2 ++ CHANGELOG.md | 1 + composer.json | 8 ++++---- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 51c2ebb..1fa3afc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,6 +17,8 @@ jobs: - '8.2' - '8.3' - '8.4' + - '8.5' + - '8.6' dependency-versions: - 'lowest' - 'default' diff --git a/CHANGELOG.md b/CHANGELOG.md index 9203ca5..a5b304d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ### Unreleased +* Support PHP 8.5 & 8.6 * Drop support for psr/log 1.x and 2.x. * BREAKING: AbstractArrayRepository::getEntityBaseClass() is now abstract. * Refactored ConsistentStringScrambler to use Randomizer rather than mt_rand. diff --git a/composer.json b/composer.json index bec034e..289066c 100644 --- a/composer.json +++ b/composer.json @@ -16,10 +16,10 @@ ], "require": { "ext-json": "*", - "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", + "ext-mbstring": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0", + "ext-pdo": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0", + "ext-sodium": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0", "psr/log": "^3.0", "rector/rector": "2.8.1" }, From 4abed94d3dd774e59e34f647e4d3a82bc9b43326 Mon Sep 17 00:00:00 2001 From: craig410 Date: Thu, 22 Oct 2026 11:03:28 +0100 Subject: [PATCH 2/7] [tmp] patch vfsStream to replace deprecated spl_object_hash with spl_object_id --- .gitignore | 1 + composer.json | 6 +++++- patches.json | 7 +++++++ patches/fix-spl-object-hash.txt | 13 +++++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 patches.json create mode 100644 patches/fix-spl-object-hash.txt diff --git a/.gitignore b/.gitignore index 7d85d95..d263153 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,6 @@ Thumbs.db thumbs.db /composer.lock +/patches.lock.json .phpunit.result.cache .php-cs-fixer.cache diff --git a/composer.json b/composer.json index 289066c..555062c 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ "mikey179/vfsstream": "^1.6.12", "phpunit/phpunit": "^11.5", "ergebnis/phpunit-slow-test-detector": "^2.15", + "cweagans/composer-patches": "~2.0", "php-cs-fixer/shim": "3.95.27", "phpstan/phpstan": "2.3.2" }, @@ -45,7 +46,10 @@ } }, "config": { - "preferred-install": "dist" + "preferred-install": "dist", + "allow-plugins": { + "cweagans/composer-patches": true + } }, "scripts": { "cs": "php-cs-fixer fix --dry-run --verbose --diff", diff --git a/patches.json b/patches.json new file mode 100644 index 0000000..6dcd679 --- /dev/null +++ b/patches.json @@ -0,0 +1,7 @@ +{ + "patches": { + "mikey179/vfsstream": { + "Replace deprecated in 8.6 spl_object_hash with spl_object_id": "patches/fix-spl-object-hash.txt" + } + } +} \ No newline at end of file diff --git a/patches/fix-spl-object-hash.txt b/patches/fix-spl-object-hash.txt new file mode 100644 index 0000000..d7f52a2 --- /dev/null +++ b/patches/fix-spl-object-hash.txt @@ -0,0 +1,13 @@ +diff --git a/src/main/php/org/bovigo/vfs/vfsStreamFile.php b/src/main/php/org/bovigo/vfs/vfsStreamFile.php +index 406252a..3deb270 100644 +--- a/src/main/php/org/bovigo/vfs/vfsStreamFile.php ++++ b/src/main/php/org/bovigo/vfs/vfsStreamFile.php +@@ -371,7 +371,7 @@ class vfsStreamFile extends vfsStreamAbstractContent + $resource = $data['wrapper_data']; + } + +- return spl_object_hash($resource); ++ return (string) spl_object_id($resource); + } + + /** From cc8ba9e9e24af37fc29e5358c1310b48f73dd235 Mon Sep 17 00:00:00 2001 From: craig410 Date: Wed, 28 Oct 2026 10:45:51 +0000 Subject: [PATCH 3/7] Drop support for php 8.2 and php 8.3 --- .github/workflows/test.yml | 10 ++++------ CHANGELOG.md | 1 + composer.json | 8 ++++---- rector.php | 2 +- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1fa3afc..2fcedb1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,8 +14,6 @@ jobs: fail-fast: false matrix: php_version: - - '8.2' - - '8.3' - '8.4' - '8.5' - '8.6' @@ -31,7 +29,7 @@ jobs: tools: composer:v2 - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: show-progress: false @@ -51,7 +49,7 @@ jobs: strategy: fail-fast: false matrix: - php: [8.2, 8.4] + php: [8.4, 8.6] steps: - uses: actions/checkout@v7 @@ -78,7 +76,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: "8.2" + php-version: "8.4" ini-values: "zend.exception_ignore_args=Off" coverage: none @@ -97,7 +95,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: "8.2" + php-version: "8.4" ini-values: "zend.exception_ignore_args=Off" coverage: none diff --git a/CHANGELOG.md b/CHANGELOG.md index a5b304d..19c1800 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ### Unreleased * Support PHP 8.5 & 8.6 +* Drop support for PHP 8.2 & 8.3 * Drop support for psr/log 1.x and 2.x. * BREAKING: AbstractArrayRepository::getEntityBaseClass() is now abstract. * Refactored ConsistentStringScrambler to use Randomizer rather than mt_rand. diff --git a/composer.json b/composer.json index 555062c..f6a246a 100644 --- a/composer.json +++ b/composer.json @@ -16,10 +16,10 @@ ], "require": { "ext-json": "*", - "ext-mbstring": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0", - "ext-pdo": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0", - "ext-sodium": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0", + "ext-mbstring": "~8.4.0 || ~8.5.0 || ~8.6.0", + "ext-pdo": "~8.4.0 || ~8.5.0 || ~8.6.0", + "ext-sodium": "~8.4.0 || ~8.5.0 || ~8.6.0", + "php": "~8.4.0 || ~8.5.0 || ~8.6.0", "psr/log": "^3.0", "rector/rector": "2.8.1" }, diff --git a/rector.php b/rector.php index 1ee4710..04dbe18 100644 --- a/rector.php +++ b/rector.php @@ -19,7 +19,7 @@ privatization: true, phpunitCodeQuality: true ) - ->withPhpSets(php82: true) + ->withPhpSets(php84: true) ->withSkip([ // Don't make whole classes read-only in libraries, it makes BC awkward ReadOnlyClassRector::class, From 1b93c4a24c35a8df1ce25ee070e58163fbd70148 Mon Sep 17 00:00:00 2001 From: craig410 Date: Wed, 28 Oct 2026 10:49:04 +0000 Subject: [PATCH 4/7] Apply strict constant types --- src/CSV/CSVWriter.php | 2 +- src/DateTime/DateIntervalUtils.php | 2 +- src/DeploymentConfig/DeploymentConfig.php | 14 +++++++------- src/Logging/DeviceIdentifier.php | 6 +++--- test/unit/Object/ConstantDirectoryTest.php | 8 ++++---- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/CSV/CSVWriter.php b/src/CSV/CSVWriter.php index c98995d..a275669 100644 --- a/src/CSV/CSVWriter.php +++ b/src/CSV/CSVWriter.php @@ -23,7 +23,7 @@ class CSVWriter { - final public const UTF8_BOM = "\xEF\xBB\xBF"; + final public const string UTF8_BOM = "\xEF\xBB\xBF"; /** * @var resource|null diff --git a/src/DateTime/DateIntervalUtils.php b/src/DateTime/DateIntervalUtils.php index e458988..9f9f0cb 100644 --- a/src/DateTime/DateIntervalUtils.php +++ b/src/DateTime/DateIntervalUtils.php @@ -17,7 +17,7 @@ class DateIntervalUtils { - private const SUPPORTED_HUMAN_COMPONENTS = [ + private const array SUPPORTED_HUMAN_COMPONENTS = [ 'y' => 'year', 'm' => 'month', 'd' => 'day', diff --git a/src/DeploymentConfig/DeploymentConfig.php b/src/DeploymentConfig/DeploymentConfig.php index eb381ef..2e9e72f 100644 --- a/src/DeploymentConfig/DeploymentConfig.php +++ b/src/DeploymentConfig/DeploymentConfig.php @@ -8,13 +8,13 @@ final class DeploymentConfig { - public const ANY = '*'; - public const STANDALONE = 'standalone'; - public const DEV = 'dev'; - public const CI = 'ci'; - public const PRODUCTION = 'production'; - public const QA = 'qa'; - public const LOADTEST = 'loadtest'; + public const string ANY = '*'; + public const string STANDALONE = 'standalone'; + public const string DEV = 'dev'; + public const string CI = 'ci'; + public const string PRODUCTION = 'production'; + public const string QA = 'qa'; + public const string LOADTEST = 'loadtest'; private string $config_dir = '/etc/ingenerator/conf'; diff --git a/src/Logging/DeviceIdentifier.php b/src/Logging/DeviceIdentifier.php index 38859c2..6653003 100644 --- a/src/Logging/DeviceIdentifier.php +++ b/src/Logging/DeviceIdentifier.php @@ -81,9 +81,9 @@ public static function get(): string return $i->getValue(); } - public const CLI_ID = 'cli-------------------'; - public const COOKIE_LIFETIME = 'P5Y'; - public const VALID_REGEX = '/^[a-zA-Z0-9\-_=]{22}$/'; + public const string CLI_ID = 'cli-------------------'; + public const string COOKIE_LIFETIME = 'P5Y'; + public const string VALID_REGEX = '/^[a-zA-Z0-9\-_=]{22}$/'; private ?string $id = null; diff --git a/test/unit/Object/ConstantDirectoryTest.php b/test/unit/Object/ConstantDirectoryTest.php index b8e9e00..3e9969c 100644 --- a/test/unit/Object/ConstantDirectoryTest.php +++ b/test/unit/Object/ConstantDirectoryTest.php @@ -20,10 +20,10 @@ */ final class ConstantDirectoryTest extends TestCase { - public const TEST_ONE = 'any'; - public const TEST_TWO = 'two'; - public const TESTTHREE = 'three'; - public const ANYTHING = 'another'; + public const string TEST_ONE = 'any'; + public const string TEST_TWO = 'two'; + public const string TESTTHREE = 'three'; + public const string ANYTHING = 'another'; public function testItIsInitialisable(): void { From 48a7b942f18f132e7c7f581071a965650de39065 Mon Sep 17 00:00:00 2001 From: craig410 Date: Wed, 28 Oct 2026 10:49:59 +0000 Subject: [PATCH 5/7] Remove unnecessary parenthesis --- .php-cs-fixer.dist.php | 4 ++-- src/Logging/DeviceIdentifier.php | 2 +- test/unit/DateTime/Clock/StoppedMockClockTest.php | 2 +- test/unit/DateTime/DateIntervalUtilsTest.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 3994a6a..8e5c4fb 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -4,11 +4,11 @@ use PhpCsFixer\Config; use PhpCsFixer\Runner\Parallel\ParallelConfigFactory; -$finder = (new Finder()) +$finder = new Finder() ->in(__DIR__) ; -return (new Config()) +return new Config() ->setParallelConfig(ParallelConfigFactory::detect()) ->setRules([ '@Symfony' => true, diff --git a/src/Logging/DeviceIdentifier.php b/src/Logging/DeviceIdentifier.php index 6653003..66bf0e7 100644 --- a/src/Logging/DeviceIdentifier.php +++ b/src/Logging/DeviceIdentifier.php @@ -130,7 +130,7 @@ public function init(): void private function assignNewIdAndSetCookies(): void { $this->id = Base64Url::encode(random_bytes(16)); - $expire = (new DateTimeImmutable())->add(new DateInterval(self::COOKIE_LIFETIME)); + $expire = new DateTimeImmutable()->add(new DateInterval(self::COOKIE_LIFETIME)); // The cookies are set with httponly=false to allow access from JS (though generally it is // preferred to render it server-side and inject into javascript through data attributes / diff --git a/test/unit/DateTime/Clock/StoppedMockClockTest.php b/test/unit/DateTime/Clock/StoppedMockClockTest.php index ea229e5..fcae0f2 100644 --- a/test/unit/DateTime/Clock/StoppedMockClockTest.php +++ b/test/unit/DateTime/Clock/StoppedMockClockTest.php @@ -72,7 +72,7 @@ public function testItIsInitialisableAtFixedMicrotime(): void public function testItIsInitialisableAtADateIntervalInThePast(): void { $clock = StoppedMockClock::atTimeAgo('P3D'); - $ago = (new DateTimeImmutable())->sub(new DateInterval('P3D')); + $ago = new DateTimeImmutable()->sub(new DateInterval('P3D')); $this->assertEqualsWithDelta($ago, $clock->getDateTime(), 1, 'Time is at correct interval'); } diff --git a/test/unit/DateTime/DateIntervalUtilsTest.php b/test/unit/DateTime/DateIntervalUtilsTest.php index 869fbd4..c2a4111 100644 --- a/test/unit/DateTime/DateIntervalUtilsTest.php +++ b/test/unit/DateTime/DateIntervalUtilsTest.php @@ -35,7 +35,7 @@ public function testItCanParseToHumanString(string $interval_string, string $exp public static function provider_unsupported_human_intervals(): Iterator { - $diff = fn (string $dt1, string $dt2) => (new DateTimeImmutable($dt1))->diff(new DateTimeImmutable($dt2)); + $diff = fn (string $dt1, string $dt2) => new DateTimeImmutable($dt1)->diff(new DateTimeImmutable($dt2)); yield 'with micros e.g. from a diff' => [$diff('now', 'now')]; yield 'negative' => [$diff('2022-01-01 10:03:03', '2021-03-02 10:02:03')]; } From 854db7ae2a922049a3e77a77a32206cadd5f003c Mon Sep 17 00:00:00 2001 From: craig410 Date: Wed, 28 Oct 2026 10:50:25 +0000 Subject: [PATCH 6/7] Add explicit CSV escape character args --- src/CSV/CSVWriter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CSV/CSVWriter.php b/src/CSV/CSVWriter.php index a275669..beadec2 100644 --- a/src/CSV/CSVWriter.php +++ b/src/CSV/CSVWriter.php @@ -89,7 +89,7 @@ public function write(array $row): void if (!$this->isResourceOpen()) { throw new LogicException('Cannot write to a closed file'); } - fputcsv($this->resource, $row, eol: $this->options['eol']); + fputcsv($this->resource, $row, escape: '\\', eol: $this->options['eol']); } /** @@ -122,7 +122,7 @@ private function writeHeaders(array $keys): void if (!$this->isResourceOpen()) { throw new LogicException('Cannot write to a closed file'); } - fputcsv($this->resource, $keys, eol: $this->options['eol']); + fputcsv($this->resource, $keys, escape: '\\', eol: $this->options['eol']); } else { array_walk( $keys, From 838d3e8236515e9eb792be66b00eb66bda6cd49c Mon Sep 17 00:00:00 2001 From: craig410 Date: Wed, 28 Oct 2026 10:50:57 +0000 Subject: [PATCH 7/7] Replace foreach with array_any --- src/Logging/ExternalCallSiteFinder.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Logging/ExternalCallSiteFinder.php b/src/Logging/ExternalCallSiteFinder.php index 1320331..ec2034d 100644 --- a/src/Logging/ExternalCallSiteFinder.php +++ b/src/Logging/ExternalCallSiteFinder.php @@ -111,12 +111,6 @@ public function findExternalCall(array $trace, array $ignore_classes): array /** @param list $ignore_classes */ protected function isIgnoredClass(string $class, array $ignore_classes): bool { - foreach ($ignore_classes as $ignored_class) { - if (is_a($ignored_class, $class, true)) { - return true; - } - } - - return false; + return array_any($ignore_classes, fn (string $ignored_class): bool => is_a($ignored_class, $class, true)); } }