composer-installer: symfony/finder incompatibility

Problem/Motivation

My CI jobs are occasionally failing with the below error

Expected behaviour

No fatal error

Actual behaviour

Fatal error: Uncaught Error: Cannot call constructor in /builds/dist/vendor/symfony/finder/Comparator/NumberComparator.php:76
Stack trace:
#0 phar:///usr/bin/composer/vendor/symfony/finder/Finder.php(230): Symfony\Component\Finder\Comparator\NumberComparator->__construct('<= 3')
#1 /builds/dist/vendor/dealerdirect/phpcodesniffer-composer-installer/src/Plugin.php(449): Symfony\Component\Finder\Finder->depth('<= 3')
#2 /builds/dist/vendor/dealerdirect/phpcodesniffer-composer-installer/src/Plugin.php(194): Dealerdirect\Composer\Plugin\Installers\PHPCodeSniffer\Plugin->updateInstalledPaths()
#3 [internal function]: Dealerdirect\Composer\Plugin\Installers\PHPCodeSniffer\Plugin->onDependenciesChangedEvent(Object(Composer\Script\Event))
#4 phar:///usr/bin/composer/src/Composer/EventDispatcher/EventDispatcher.php(191): call_user_func(Array, Object(Composer\Script\Event))
#5 phar:///usr/bin/composer/src/Composer/EventDispatcher/EventDispatcher.php(118): Composer\EventDisp in /builds/dist/vendor/symfony/finder/Comparator/NumberComparator.php on line 76

Steps to reproduce

Unclear, happens randomly. Composer version is fixed in a Docker image.

Proposed changes

The issue seems to be with the auto loader and a mismatch in symfony/finder versions. I depend on v5.4.0 while composer.phar uses v2.8 (I think). The Cannot call constructor error is because v5.4.0 calls parent::__construct() and a __construct() doesn’t exist in the parent class (due to the version mismatch).

Environment

Question Answer
OS Debian 11 (bullseye)
PHP version 7.4.27
Composer version 2.2.1
PHP_CodeSniffer version 3.6.2
Dealerdirect PHPCS plugin version 0.7.1
Install type curl -L -o /usr/bin/composer https://getcomposer.org/composer-2.phar && chmod +x /usr/bin/composer

Tested against master branch?

  • I have verified the issue still exists in the master branch.

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 1
  • Comments: 18 (9 by maintainers)

Most upvoted comments

For anyone else that become stuck on this issue & need a solution here and now. We decided just to script our way out of it using the following bash script:

echo "Installing dependencies..."
while ! composer install --prefer-dist
do
  ((c++)) && ((c==10)) && break
  echo "Installing dependencies retry in 1 second..."
  sleep 1
done

In our case it automatically resolves itself after attempting the second time. Leading us to think it’s a matter of system constraints at that point in time provoking it. We’ve had 0 issues after using the above bash in our pipelines since adding it.

One thing I’ve found that may help reproduce this is when resources on the machine running the CI job are exhausted. After we switched from raw EC2 instances running our CI jobs with 1 job per machine to Kubernetes with 2 EC2 instances running concurrent jobs - then we started seeing it almost every time.

So I have a sinking feeling that it may be more easily reproducible in an environment that is under stress. Could maybe be possible to provoke through a docker container restricted to a few CPU/RAM resources?

I’m reopening the issue for further investigation, though I’m not holding out much hope that we’ll be able to find a stable solution. All the same, between the combined brainpower in this thread who knows what we can come up with 😉

The “no” answer is that we could possibly use a try {} catch {} in one or more select places to prevent the fatal error and throw an informative error message instead.

We might need to take a look at other/more places a try/catch might be needed to guard against external code. However, I think if such things are added, they should be tested at the unit level, as that would also make it trivial to reproduce since the exception would then be thrown from a mock instead of the actual code.

Unless / Until this problem becomes more prevalent, I think it should not receive much priority (other than us thinking about the try/catch in general for hardening this project regarding external code).

Either way, this write up will hopefully help the next person running into the issue understand what’s going on

Amen Indubitably. 🙏

Hello, having the same (rare) issue on a Laravel 8 app and Composer 2.1.6.

Fixed by removing slevomat/coding-standard dependency which became useless after moving our phpcs to PSR12.

I think we determined in https://github.com/composer/composer/issues/10413 that it’s a composer issue.