From 80f5bced38c87f0b5c6ebf46745a87d578c3b228 Mon Sep 17 00:00:00 2001 From: mpyw Date: Sat, 13 Jun 2026 12:55:48 +0900 Subject: [PATCH 1/2] Constrain to sebastian/comparator <8.2 and support 8.0/8.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sebastian/comparator 8.2.0 fixed the serialization problem this package works around (sebastianbergmann/comparator#158): ComparisonFailure gained __serialize()/__unserialize() so it survives process-isolation transport even when the stack trace holds non-serializable objects (e.g. a PDO argument). comparator 8.0/8.1 still lack that fix. - composer.json: extend `sebastian/comparator` to `... || >=8.0 <8.2`. This is the precise boundary — every version *without* the upstream fix. 8.2+ is excluded so this package never overrides the upstream-fixed class (it replaces ComparisonFailure via exclude-from-classmap). - files/ComparisonFailure.php: sebastian/diff >=8 (pulled by comparator ^8) added an `emitNoLineEndEofWarning` ctor arg defaulting to true, which injects "\ No newline at end of file" markers into the diff. Detect that arg via reflection and pass false; older diff releases keep the existing 1-argument construction. Verified locally: tests pass on comparator 7.1.8 (diff 7.0) and 8.1.4 (diff 8.3). - README: document the upstream fix and the <8.2 boundary. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 7 ++++++- composer.json | 14 ++++++++++++-- files/ComparisonFailure.php | 20 ++++++++++++++++++-- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5def956..0ce9b8a 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,16 @@ Fixes `assertSame()`/`assertEquals()` serialization errors running in separate processes. +> [!IMPORTANT] +> **`sebastian/comparator` `8.2.0` fixes this upstream**, so this patch is only needed below that version. +> See [sebastianbergmann/comparator#158](https://github.com/sebastianbergmann/comparator/issues/158): as of `8.2.0`, `ComparisonFailure` implements `__serialize()`/`__unserialize()` and can be serialized across process-isolation boundaries even when its stack trace references non-serializable objects (e.g. a `PDO` passed as a method argument) — exactly the case this package works around. +> This package is therefore constrained to `sebastian/comparator` `<8.2`. On `comparator` `>=8.2` you do **not** need it (and shouldn't install it: it would override the upstream-fixed class). + ## Requirements - php: `>=5.3.3` - [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit): `>=4.8.0` -- [sebastianbergmann/comparator](https://github.com/sebastianbergmann/comparator): `^1.0 || ^2.0 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0` +- [sebastianbergmann/comparator](https://github.com/sebastianbergmann/comparator): `^1.0 || ^2.0 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0 || >=8.0 <8.2` ## Installing diff --git a/composer.json b/composer.json index ebdc4ef..ee9b16f 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,17 @@ "email": "ryosuke_i_628@yahoo.co.jp" } ], - "keywords": ["phpunit", "isolation", "serialization", "separate", "process", "processes", "patch", "fix", "bug"], + "keywords": [ + "phpunit", + "isolation", + "serialization", + "separate", + "process", + "processes", + "patch", + "fix", + "bug" + ], "autoload": { "files": [ "./files/ComparisonFailure.php" @@ -28,7 +38,7 @@ }, "require": { "php": ">=5.3.3", - "sebastian/comparator": "^1.0 || ^2.0 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" + "sebastian/comparator": "^1.0 || ^2.0 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0 || >=8.0 <8.2" }, "require-dev": { "phpunit/phpunit": ">=4.8" diff --git a/files/ComparisonFailure.php b/files/ComparisonFailure.php index 3c5b97c..ac1adc2 100644 --- a/files/ComparisonFailure.php +++ b/files/ComparisonFailure.php @@ -110,10 +110,26 @@ public function getDiff() } $header = "\n--- Expected\n+++ Actual\n"; + $builder = $header; if (\class_exists('SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder')) { - $header = new UnifiedDiffOutputBuilder($header); + // sebastian/diff >=8 (pulled in by sebastian/comparator ^8) adds an + // `emitNoLineEndEofWarning` constructor argument that defaults to true, + // which injects `\ No newline at end of file` markers into the diff. + // Disable it when available to keep the legacy output format; older + // diff releases lack the argument, so fall back to the 1-argument form. + $emitsNoLineEndEofWarning = false; + $constructor = new \ReflectionMethod('SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder', '__construct'); + foreach ($constructor->getParameters() as $parameter) { + if ($parameter->getName() === 'emitNoLineEndEofWarning') { + $emitsNoLineEndEofWarning = true; + break; + } + } + $builder = $emitsNoLineEndEofWarning + ? new UnifiedDiffOutputBuilder($header, false, 3, false) + : new UnifiedDiffOutputBuilder($header); } - $differ = new Differ($header); + $differ = new Differ($builder); return $differ->diff($this->expectedAsString, $this->actualAsString); } From 7df02777f7f296ec5442a4c781f592efd47648f1 Mon Sep 17 00:00:00 2001 From: mpyw Date: Sat, 13 Jun 2026 13:28:17 +0900 Subject: [PATCH 2/2] ci: explicitly test sebastian/comparator 8.0 and 8.1 The default `composer require phpunit/phpunit` only ever resolves the highest comparator allowed (8.1 on PHP 8.4/8.5), so comparator 8.0 was never exercised. Add matrix entries that pin `sebastian/comparator` to `8.0.*` and `8.1.*` (PHP 8.4/8.5; comparator 8 requires PHP >= 8.4) and a guarded step that pins the version before installing PHPUnit, so the `>=8.0 <8.2` support range is actually covered by CI. Also surface sebastian/diff in the installed-versions printout. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/test.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 24e45e3..ba83636 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,6 +17,11 @@ jobs: - { php: 5.6, flags: --prefer-lowest } - { php: '7.0', flags: --prefer-lowest } - { php: 7.1, flags: --prefer-lowest } + # Explicitly exercise the sebastian/comparator 8.0 / 8.1 range — the + # versions that still lack the #158 serialization fix and therefore + # still need this patch (requires PHP >= 8.4). + - { php: 8.4, comparator: 8.0.* } + - { php: 8.5, comparator: 8.1.* } steps: - uses: actions/checkout@v3 @@ -26,7 +31,11 @@ jobs: with: php-version: ${{ matrix.php }} - - run: composer require phpunit/phpunit --dev ${{ matrix.flags }} - - run: composer show | grep -E '^(phpunit/phpunit|sebastian/comparator)' + - name: Pin sebastian/comparator + if: ${{ matrix.comparator }} + run: composer require "sebastian/comparator:${{ matrix.comparator }}" --no-update + + - run: composer require phpunit/phpunit --dev ${{ matrix.flags }} ${{ matrix.comparator && '-W' || '' }} + - run: composer show | grep -E '^(phpunit/phpunit|sebastian/comparator|sebastian/diff)' - run: 'vendor/bin/phpunit --migrate-configuration || :' - run: vendor/bin/phpunit