diff options
-rw-r--r-- | core/modules/package_manager/src/ExecutableFinder.php | 22 | ||||
-rw-r--r-- | core/modules/package_manager/tests/src/Unit/ExecutableFinderTest.php | 38 |
2 files changed, 58 insertions, 2 deletions
diff --git a/core/modules/package_manager/src/ExecutableFinder.php b/core/modules/package_manager/src/ExecutableFinder.php index d8a2f1c21d5..a26de302187 100644 --- a/core/modules/package_manager/src/ExecutableFinder.php +++ b/core/modules/package_manager/src/ExecutableFinder.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Drupal\package_manager; +use Composer\InstalledVersions; use Drupal\Core\Config\ConfigFactoryInterface; use PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface; @@ -17,10 +18,19 @@ use PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface; */ final class ExecutableFinder implements ExecutableFinderInterface { + /** + * The path where Composer is installed in the project, or FALSE if it's not. + */ + private string|false|null $composerPath = NULL; + public function __construct( private readonly ExecutableFinderInterface $decorated, private readonly ConfigFactoryInterface $configFactory, - ) {} + ) { + $this->composerPath = InstalledVersions::isInstalled('composer/composer') + ? InstalledVersions::getInstallPath('composer/composer') . '/bin/composer' + : FALSE; + } /** * {@inheritdoc} @@ -29,7 +39,15 @@ final class ExecutableFinder implements ExecutableFinderInterface { $executables = $this->configFactory->get('package_manager.settings') ->get('executables'); - return $executables[$name] ?? $this->decorated->find($name); + if (isset($executables[$name])) { + return $executables[$name]; + } + + // If we're looking for Composer, use the project's local copy if available. + if ($name === 'composer' && $this->composerPath && file_exists($this->composerPath)) { + return $this->composerPath; + } + return $this->decorated->find($name); } } diff --git a/core/modules/package_manager/tests/src/Unit/ExecutableFinderTest.php b/core/modules/package_manager/tests/src/Unit/ExecutableFinderTest.php index 811d7547e04..69becb1fd8d 100644 --- a/core/modules/package_manager/tests/src/Unit/ExecutableFinderTest.php +++ b/core/modules/package_manager/tests/src/Unit/ExecutableFinderTest.php @@ -6,6 +6,7 @@ namespace Drupal\Tests\package_manager\Unit; use Drupal\package_manager\ExecutableFinder; use Drupal\Tests\UnitTestCase; +use org\bovigo\vfs\vfsStream; use PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface; /** @@ -36,4 +37,41 @@ class ExecutableFinderTest extends UnitTestCase { $finder->find('rsync'); } + /** + * Tests that the executable finder tries to use a local copy of Composer. + */ + public function testComposerInstalledInProject(): void { + vfsStream::setup('root', NULL, [ + 'composer-path' => [ + 'bin' => [], + ], + ]); + $composer_path = 'vfs://root/composer-path/bin/composer'; + touch($composer_path); + $this->assertFileExists($composer_path); + + $decorated = $this->prophesize(ExecutableFinderInterface::class); + $decorated->find('composer')->willReturn('the real Composer'); + + $finder = new ExecutableFinder( + $decorated->reveal(), + $this->getConfigFactoryStub([ + 'package_manager.settings' => [ + 'executables' => [], + ], + ]), + ); + $reflector = new \ReflectionProperty($finder, 'composerPath'); + $reflector->setValue($finder, $composer_path); + $this->assertSame($composer_path, $finder->find('composer')); + + // If the executable disappears, or Composer isn't locally installed, the + // decorated executable finder should be called. + unlink($composer_path); + $this->assertSame('the real Composer', $finder->find('composer')); + + $reflector->setValue($finder, FALSE); + $this->assertSame('the real Composer', $finder->find('composer')); + } + } |