summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--core/modules/package_manager/src/ExecutableFinder.php22
-rw-r--r--core/modules/package_manager/tests/src/Unit/ExecutableFinderTest.php38
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'));
+ }
+
}