Description
Symfony version(s) affected
7.2
Description
FYI I'm filing this just to document the problem and the solution I found. If you're a maintainer, no need to read it all, no need to respond.
I just upgraded from PHPUnit 9.5 to 11.5 (by changing the SYMFONY_PHPUNIT_VERSION
) and now I get a new error:
PHP Fatal error: Uncaught Error: Class "PHPUnit\TextUI\Application" not found in .../vendor/bin/.phpunit/phpunit-11.5-0/phpunit:104
Stack trace:
#0 .../vendor/symfony/phpunit-bridge/bin/simple-phpunit.php(473): include()
#1 .../vendor/symfony/phpunit-bridge/bin/simple-phpunit(13): require('...')
#2 .../vendor/bin/simple-phpunit(119): include('...')
#3 {main}
thrown in .../vendor/bin/.phpunit/phpunit-11.5-0/phpunit on line 104
How to reproduce
<server name="SYMFONY_PHPUNIT_VERSION" value="11.5"/>
(or12.1
, doesn't matter) inphpunit.xml.dist
.- Not have
phpunit/phpunit
as a (dev) dependency in the outercomposer.json
. - Run
vendor/bin/simple-phpunit
Possible Solution
I believe it has to do with this change by @xabbuh. It removed the file_put_contents('phpunit', ...)
line (and surrounding lines) if PHPUnit >= 11. This line replaced the PHPUnit main script with the contents between EOPHP
there. The main purpose main purpose of using that patched script was to use Symfony\Bridge\PhpUnit\TextUI\Command::main();
instead of the (new PHPUnit\TextUI\Application)->run($_SERVER['argv'])
from the standard PHPUnit main script. The documented reason was to inject logic for Symfone's test listener feature, which had since been removed, hence it was no longer necessary to inject that alternative main script either.
However, there's another behavioral difference:
The old/patched script always loaded this:
define('PHPUNIT_COMPOSER_INSTALL', __DIR__.'/vendor/autoload.php');
require PHPUNIT_COMPOSER_INSTALL;
whereas since PHPUnit 8, the official script does this:
if (isset($GLOBALS['_composer_autoload_path'])) {
define('PHPUNIT_COMPOSER_INSTALL', $GLOBALS['_composer_autoload_path']);
unset($GLOBALS['_composer_autoload_path']);
} else {
foreach (array(__DIR__ . '/../../autoload.php', __DIR__ . '/../vendor/autoload.php', __DIR__ . '/vendor/autoload.php') as $file) {
if (file_exists($file)) {
define('PHPUNIT_COMPOSER_INSTALL', $file);
break;
}
}
unset($file);
}
require PHPUNIT_COMPOSER_INSTALL;
That is: If an outer Composer autoload is detected, that is used instead of the inner one. But the inner one is where phpunit-bridge intalls the phpunit/phpunit
package.
In the past, it wasn't necessary to have phpunit/phpunit
as a dependency on the outer composer.json
, but now it is. That's fine, as some Symfony documentation asks to install symfony/test-pack
(which brings in phpunit/phpunit
) and some other Symfony documentation asks to install phpunit/phpunit
itself. Doing so fixes the problem.
Maybe this should be documented as a breaking change for Symfony 7.2.
Additional Context
No response