# Changes in PHPUnit 12.5

All notable changes of the PHPUnit 12.5 release series are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.

## [12.5.32] - 2026-07-25

### Fixed

* [#6833](https://github.com/sebastianbergmann/phpunit/issues/6833): `assertArrayHasKey()` does not accept `ArrayAccess` implementations with a specific value type when test code is analysed with PHPStan at level 9
* [#6854](https://github.com/sebastianbergmann/phpunit/issues/6854): Deprecation triggered in first-party code is wrongly classified as indirect when the first-party code is called from third-party code

## [12.5.31] - 2026-07-06

### Changed

* [#6797](https://github.com/sebastianbergmann/phpunit/issues/6797): Adapt code generated for test double of interface with constructor for PHP 8.6

## [12.5.30] - 2026-06-15

### Fixed

* [#6743](https://github.com/sebastianbergmann/phpunit/issues/6743): Improve error message for invalid version constraint in attribute
* Class, method, and data-set names in TestDox HTML report are not escaped

## [12.5.29] - 2026-06-04

### Fixed

* [#6683](https://github.com/sebastianbergmann/phpunit/issues/6683): `assertNotEquals()` failure message says "is equal to" instead of "is not equal to" when comparing arrays or objects
* [#6700](https://github.com/sebastianbergmann/phpunit/issues/6700): `expectOutputString()` and `expectOutputRegex()` silently replace themselves and each other

## [12.5.28] - 2026-05-27

### Fixed

* [#6681](https://github.com/sebastianbergmann/phpunit/pull/6681): XML assertions such as `assertXmlStringEqualsXmlString()` regressed into treating comments as significant

## [12.5.27] - 2026-05-25

### Fixed

* [#6673](https://github.com/sebastianbergmann/phpunit/issues/6673): Empty PHP settings from the parent process override per-test `-d` settings forwarded to child processes (breaks PCOV coverage)

## [12.5.26] - 2026-05-21

### Fixed

* PHP setting values containing `=` need to be quoted before forwarding via `-d`

## [12.5.25] - 2026-05-13

### Changed

* A `Test` or `Tests` prefix is no longer stripped from class names when they are processed for TestDox output

### Fixed

* [#6605](https://github.com/sebastianbergmann/phpunit/issues/6605): Data set names and provider values containing Unicode bidirectional control characters distort terminal output
* [#6610](https://github.com/sebastianbergmann/phpunit/issues/6610): Per-testsuite bootstrap script not loaded in process isolation
* TestDox output collapsed separate test classes into a single group when their prettified class names matched

## [12.5.24] - 2026-05-01

### Added

* [#6596](https://github.com/sebastianbergmann/phpunit/issues/6596): Backport `TestCase::invokeTestMethod()` method for customizing test method invocation from PHPUnit 13

### Fixed

* [#6595](https://github.com/sebastianbergmann/phpunit/issues/6595): Crash when before-class or after-class method fails with assertion failure
* [#6599](https://github.com/sebastianbergmann/phpunit/issues/6599): TeamCity logger does not wrap failures in before-test methods with `testStarted` and `testFinished`
* [#6601](https://github.com/sebastianbergmann/phpunit/issues/6601): Anonymous classes are not rejected with a clear error when creating a test double
* `MockBuilder::setMockClassName()` and `TestStubBuilder::setStubClassName()` now reject values that are not valid unqualified PHP class identifiers, throwing the new `InvalidClassNameException`
* The regular expression used by `Generator::ensureValidMethods()` to validate method names passed to `MockBuilder::onlyMethods()` and `addMethods()` was not anchored, so any string containing a valid identifier substring (including strings with parentheses, braces, comments, or newlines) was accepted

## [12.5.23] - 2026-04-18

### Changed

* Pass `LIBXML_NONET` when parsing/validating XML configuration files to make explicit that no network I/O is performed
* Verify the result file written by an isolated child process with a random nonce before deserializing it

## [12.5.22] - 2026-04-17

### Fixed

* [#6590](https://github.com/sebastianbergmann/phpunit/issues/6590): Silent failure when configuration file is invalid
* [#6592](https://github.com/sebastianbergmann/phpunit/pull/6592): INI metacharacters `;` and `"` are not preserved when forwarding settings to child processes

## [12.5.21] - 2026-04-16

### Fixed

* [#5860](https://github.com/sebastianbergmann/phpunit/issues/5860): PHP CLI `-d` settings are not forwarded to child processes for process isolation
* [#6451](https://github.com/sebastianbergmann/phpunit/issues/6451): Incomplete version in `RequiresPhp` (e.g. `<=8.5`) is compared against full PHP version, causing unexpected skips

## [12.5.20] - 2026-04-15

### Fixed

* [#5993](https://github.com/sebastianbergmann/phpunit/issues/5993): `DefaultJobRunner` deadlocks on child processes that write large amounts of stderr output
* [#6465](https://github.com/sebastianbergmann/phpunit/issues/6465): SAPI-populated `$_SERVER` entries leak from parent into child process
* [#6587](https://github.com/sebastianbergmann/phpunit/issues/6587): `failOnEmptyTestSuite="false"` in `phpunit.xml` is ignored when `--group`/`--filter`/`--testsuite` matches no tests
* [#6588](https://github.com/sebastianbergmann/phpunit/issues/6588): Order of issue baseline entries is not canonicalized

## [12.5.19] - 2026-04-13

### Fixed

* Regression in XML configuration migration introduced in PHPUnit 12.5.8

## [12.5.18] - 2026-04-13

### Fixed

* [#4571](https://github.com/sebastianbergmann/phpunit/issues/4571): No warning when `--random-order-seed` is used when test execution order is not random
* [#4975](https://github.com/sebastianbergmann/phpunit/issues/4975): `--filter` does not work when filter string starts with `#`
* [#5354](https://github.com/sebastianbergmann/phpunit/issues/5354): JUnit XML logger does not handle `TestSuiteSkipped` event
* [#6276](https://github.com/sebastianbergmann/phpunit/issues/6276): Exit with non-zero exit code when explicit test selection (`--filter`, `--group`, `--testsuite`) yields no tests
* [#6583](https://github.com/sebastianbergmann/phpunit/issues/6583): Failing output expectation skips `tearDown()` and handler restoration, causing subsequent tests to be marked as risky

## [12.5.17] - 2026-04-08

### Changed
 
* [#4793](https://github.com/sebastianbergmann/phpunit/issues/4793): Exit with non-zero exit code when `exit` was called from some test

### Fixed

* [#5881](https://github.com/sebastianbergmann/phpunit/issues/5881): `colors="true"` in XML configuration file does not unconditionally enable colored output
* [#6019](https://github.com/sebastianbergmann/phpunit/issues/6019): `--migrate-configuration` does not update schema location when XML content already validates against current schema
* [#6372](https://github.com/sebastianbergmann/phpunit/issues/6372): Assertion failure inside `willReturnCallback()` is silently swallowed when code under test catches exceptions
* [#6464](https://github.com/sebastianbergmann/phpunit/issues/6464): Process isolation template unconditionally calls `set_include_path()`
* [#6571](https://github.com/sebastianbergmann/phpunit/issues/6571): Static analysis errors for `TestDoubleBuilder` method chaining

## [12.5.16] - 2026-04-03

### Added

* [#6547](https://github.com/sebastianbergmann/phpunit/pull/6547): Support for `%r...%r` in `EXPECTF` section

### Fixed

* [#6025](https://github.com/sebastianbergmann/phpunit/issues/6025): `FILE_EXTERNAL` breaks `__DIR__`
* [#6351](https://github.com/sebastianbergmann/phpunit/issues/6351): No warning when the same test runner extension is configured more than once
* [#6433](https://github.com/sebastianbergmann/phpunit/issues/6433): Logic in `TestSuiteLoader` is brittle and causes "Class FooTest not found" even for valid tests in valid filenames
* [#6463](https://github.com/sebastianbergmann/phpunit/issues/6463): Process Isolation fails on non-serializable globals and quietly ignore closures

## [12.5.15] - 2026-03-31

### Changed

* [#4440](https://github.com/sebastianbergmann/phpunit/issues/4440): Improve error when configured code coverage file list is empty
* [#6549](https://github.com/sebastianbergmann/phpunit/pull/6549): Allow to stub both hooks of non-virtual properties

### Fixed

* [#6529](https://github.com/sebastianbergmann/phpunit/pull/6529): Git "detached HEAD state" in Open Test Reporting (OTR) XML logger not handled properly
* [#6545](https://github.com/sebastianbergmann/phpunit/issues/6545): Stubbing a class with set property hook leaves property uninitialized by default
* The `RegularExpression` and `StringMatchesFormatDescription` did not handle `preg_match()` errors such as `Compilation failed: regular expression is too large`

## [12.5.14] - 2026-02-18

### Fixed

* [#6521](https://github.com/sebastianbergmann/phpunit/issues/6521): Performance regression in PHPUnit 11.5.54, PHPUnit 12.5.13, and PHPUnit 13.0.4

## [12.5.13] - 2026-02-18

### Fixed

* [#6489](https://github.com/sebastianbergmann/phpunit/pull/6489): Classification of self/direct/indirect deprecation triggers is not aligned with Symfony's bridge for PHPUnit

## [12.5.12] - 2026-02-16

### Fixed

* [#6511](https://github.com/sebastianbergmann/phpunit/issues/6511): TestDox variables out of order with named arguments
* [#6514](https://github.com/sebastianbergmann/phpunit/issues/6514): `<ini />` can silently fail

## [12.5.11] - 2026-02-10

### Deprecated

* [#6510](https://github.com/sebastianbergmann/phpunit/issues/6510): Deprecate using `with*()` on test stubs

### Fixed

* [#6503](https://github.com/sebastianbergmann/phpunit/issues/6503): Temporary file used by `SourceMapper` may be deleted prematurely when multiple PHPUnit processes run in parallel
* [#6509](https://github.com/sebastianbergmann/phpunit/issues/6509): "No expectations were configured for the mock object ..." notice is emitted when `with()` is used without `expects()`

## [12.5.10] - 2026-02-08

### Fixed

* [#6495](https://github.com/sebastianbergmann/phpunit/pull/6495): Source map for issue trigger identification is regenerated in process isolation child processes

## [12.5.9] - 2026-02-05

### Added

* [#6488](https://github.com/sebastianbergmann/phpunit/issues/6488): Allow disabling issue trigger identification for improved performance

### Fixed

* [#6486](https://github.com/sebastianbergmann/phpunit/issues/6486): Incorrect file name reported for errors for test methods declared in traits
* [#6490](https://github.com/sebastianbergmann/phpunit/pull/6490): Incorrect test count when tests are skipped in before-class method

## [12.5.8] - 2026-01-27

### Changed

* To prevent Poisoned Pipeline Execution (PPE) attacks using prepared `.coverage` files in pull requests, a PHPT test will no longer be run if the temporary file for writing code coverage information already exists before the test runs

## [12.5.7] - 2026-01-24

### Fixed

* [#6362](https://github.com/sebastianbergmann/phpunit/issues/6362): Manually instantiated test doubles are broken since PHPUnit 11.2
* [#6470](https://github.com/sebastianbergmann/phpunit/issues/6470): Infinite recursion in `Count::getCountOf()` for unusal implementations of `Iterator` or `IteratorAggregate`

## [12.5.6] - 2026-01-16

### Changed

* Reverted a change that caused a [build failure](https://github.com/php/php-src/actions/runs/21052584327/job/60542023395#step:14:3729) for the [PHP project's nightly community job](https://phpunit.expert/articles/how-php-and-its-ecosystem-test-each-other.html?ref=github)

## [12.5.5] - 2026-01-15

### Changed

* `PHPUnit\Framework\MockObject` exceptions are now subtypes of `PHPUnit\Exception`

### Deprecated

* [#6461](https://github.com/sebastianbergmann/phpunit/issues/6461): `any()` matcher (soft deprecation)

### Fixed

* [#6470](https://github.com/sebastianbergmann/phpunit/issues/6470): Mocking a class with a property hook setter accepting more types than the property results in a fatal error

## [12.5.4] - 2025-12-15

### Changed

* The `#[AllowMockObjectsWithoutExpectations]` attribute can now be used on the method level

### Fixed

* [#6446](https://github.com/sebastianbergmann/phpunit/issues/6446): Test runner crashes with `Timer::start() has to be called before Timer::stop()`

## [12.5.3] - 2025-12-11

### Changed

* The message emitted when a test method creates a mock object but does not configure any expectations for it has been improved

## [12.5.2] - 2025-12-08

### Added

* Attribute `#[AllowMockObjectsWithoutExpectations]` for excluding tests from the check that emits the notice for test methods that create a mock object but do not configure an expectation for it

## [12.5.1] - 2025-12-06

### Added

* `TestCase::getStubBuilder()` (analogous to `TestCase::getMockBuilder()`) for creating (partial) test stubs using a fluent API

## [12.5.0] - 2025-12-05

### Added

* [#6376](https://github.com/sebastianbergmann/phpunit/issues/6376): `--all` CLI option to ignore test selection configured in XML configuration file
* [#6422](https://github.com/sebastianbergmann/phpunit/issues/6422): Make `<source>` element in XML code coverage report optional

### Changed

* [#6380](https://github.com/sebastianbergmann/phpunit/pull/6380): Allow `Throwable` in `expectExceptionObject()`
* A PHPUnit notice is now emitted for test methods that create a mock object but do not configure an expectation for it

[12.5.32]: https://github.com/sebastianbergmann/phpunit/compare/12.5.31...12.5.32
[12.5.31]: https://github.com/sebastianbergmann/phpunit/compare/12.5.30...12.5.31
[12.5.30]: https://github.com/sebastianbergmann/phpunit/compare/12.5.29...12.5.30
[12.5.29]: https://github.com/sebastianbergmann/phpunit/compare/12.5.28...12.5.29
[12.5.28]: https://github.com/sebastianbergmann/phpunit/compare/12.5.27...12.5.28
[12.5.27]: https://github.com/sebastianbergmann/phpunit/compare/12.5.26...12.5.27
[12.5.26]: https://github.com/sebastianbergmann/phpunit/compare/12.5.25...12.5.26
[12.5.25]: https://github.com/sebastianbergmann/phpunit/compare/12.5.24...12.5.25
[12.5.24]: https://github.com/sebastianbergmann/phpunit/compare/12.5.23...12.5.24
[12.5.23]: https://github.com/sebastianbergmann/phpunit/compare/12.5.22...12.5.23
[12.5.22]: https://github.com/sebastianbergmann/phpunit/compare/12.5.21...12.5.22
[12.5.21]: https://github.com/sebastianbergmann/phpunit/compare/12.5.20...12.5.21
[12.5.20]: https://github.com/sebastianbergmann/phpunit/compare/12.5.19...12.5.20
[12.5.19]: https://github.com/sebastianbergmann/phpunit/compare/12.5.18...12.5.19
[12.5.18]: https://github.com/sebastianbergmann/phpunit/compare/12.5.17...12.5.18
[12.5.17]: https://github.com/sebastianbergmann/phpunit/compare/12.5.16...12.5.17
[12.5.16]: https://github.com/sebastianbergmann/phpunit/compare/12.5.15...12.5.16
[12.5.15]: https://github.com/sebastianbergmann/phpunit/compare/12.5.14...12.5.15
[12.5.14]: https://github.com/sebastianbergmann/phpunit/compare/12.5.13...12.5.14
[12.5.13]: https://github.com/sebastianbergmann/phpunit/compare/12.5.12...12.5.13
[12.5.12]: https://github.com/sebastianbergmann/phpunit/compare/12.5.11...12.5.12
[12.5.11]: https://github.com/sebastianbergmann/phpunit/compare/12.5.10...12.5.11
[12.5.10]: https://github.com/sebastianbergmann/phpunit/compare/12.5.9...12.5.10
[12.5.9]: https://github.com/sebastianbergmann/phpunit/compare/12.5.8...12.5.9
[12.5.8]: https://github.com/sebastianbergmann/phpunit/compare/12.5.7...12.5.8
[12.5.7]: https://github.com/sebastianbergmann/phpunit/compare/12.5.6...12.5.7
[12.5.6]: https://github.com/sebastianbergmann/phpunit/compare/12.5.5...12.5.6
[12.5.5]: https://github.com/sebastianbergmann/phpunit/compare/12.5.4...12.5.5
[12.5.4]: https://github.com/sebastianbergmann/phpunit/compare/12.5.3...12.5.4
[12.5.3]: https://github.com/sebastianbergmann/phpunit/compare/12.5.2...12.5.3
[12.5.2]: https://github.com/sebastianbergmann/phpunit/compare/12.5.1...12.5.2
[12.5.1]: https://github.com/sebastianbergmann/phpunit/compare/12.5.0...12.5.1
[12.5.0]: https://github.com/sebastianbergmann/phpunit/compare/12.4.5...12.5.0
