class ExecutableFinder

An executable finder which looks for executable paths in configuration.

@internal This is an internal part of Package Manager and may be changed or removed at any time without warning. External code should not interact with this class.

Hierarchy

  • class \Drupal\package_manager\ExecutableFinder implements \PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface, \Psr\Log\LoggerAwareInterface uses \Psr\Log\LoggerAwareTrait

Expanded class hierarchy of ExecutableFinder

2 files declare their use of ExecutableFinder
ExecutableFinderTest.php in core/modules/package_manager/tests/src/Unit/ExecutableFinderTest.php
ServicesTest.php in core/modules/package_manager/tests/src/Kernel/ServicesTest.php

File

core/modules/package_manager/src/ExecutableFinder.php, line 25

Namespace

Drupal\package_manager
View source
final class ExecutableFinder implements ExecutableFinderInterface, LoggerAwareInterface {
  use LoggerAwareTrait;
  
  /**
   * The path where Composer is installed in the project, or FALSE if it's not.
   */
  private string|false|null $composerPackagePath = NULL;
  
  /**
   * The path of the Composer binary, or NULL if it can't be found.
   */
  private ?string $composerBinaryPath = NULL;
  public function __construct(private readonly ExecutableFinderInterface $decorated, private readonly FileSystemInterface $fileSystem, private readonly ConfigFactoryInterface $configFactory) {
    $this->composerPackagePath = InstalledVersions::isInstalled('composer/composer') ? InstalledVersions::getInstallPath('composer/composer') : FALSE;
  }
  
  /**
   * {@inheritdoc}
   */
  public function find(string $name) : string {
    $legacy_executables = $this->configFactory
      ->get('package_manager.settings')
      ->get('executables');
    if ($name === 'rsync') {
      try {
        return Settings::get('package_manager_rsync_path', $this->decorated
          ->find($name));
      } catch (LogicException $e) {
        if (isset($legacy_executables[$name])) {
          @trigger_error("Storing the path to rsync in configuration is deprecated in drupal:11.2.4 and not supported in drupal:12.0.0. Move it to the <code>package_manager_rsync_path</code> setting instead. See https://www.drupal.org/node/3540264", E_USER_DEPRECATED);
          return $legacy_executables[$name];
        }
        throw $e;
      }
    }
    elseif ($name === 'composer') {
      $path = $this->getLocalComposerPath();
      if ($path && file_exists($path)) {
        return $path;
      }
      // If the regular executable finder can't find Composer, and it's not
      // overridden by a setting, fall back to the configured path to Composer
      // (if available), which is no longer supported.
      try {
        return Settings::get('package_manager_composer_path', $this->decorated
          ->find($name));
      } catch (LogicException $e) {
        if (isset($legacy_executables[$name])) {
          @trigger_error("Storing the path to Composer in configuration is deprecated in drupal:11.2.4 and not supported in drupal:12.0.0. Add composer/composer directly to your project's dependencies instead. See https://www.drupal.org/node/3540264", E_USER_DEPRECATED);
          return $legacy_executables[$name];
        }
        throw $e;
      }
    }
    return $this->decorated
      ->find($name);
  }
  
  /**
   * Tries to find the Composer binary installed in the project.
   *
   * @return string|null
   *   The path of the `composer` binary installed in the project's vendor
   *   dependencies, or NULL if it is not installed or cannot be found.
   */
  private function getLocalComposerPath() : ?string {
    // Composer is not installed in the project, so there's nothing to do.
    if ($this->composerPackagePath === FALSE) {
      return NULL;
    }
    // This is a bit expensive to compute, so statically cache it.
    if ($this->composerBinaryPath) {
      return $this->composerBinaryPath;
    }
    $composer_json = file_get_contents($this->composerPackagePath . '/composer.json');
    $composer_json = Json::decode($composer_json);
    foreach ($composer_json['bin'] ?? [] as $bin) {
      if (str_ends_with($bin, '/composer')) {
        $bin = $this->composerPackagePath . '/' . $bin;
        // For extra security, try to disable the binary's execute permission.
        // If that fails, it's worth warning about but is not an actual problem.
        if (is_executable($bin) && !$this->fileSystem
          ->chmod($bin, 0644)) {
          $this->logger?->warning('Composer was found at %path, but could not be made read-only.', [
            '%path' => $bin,
          ]);
        }
        return $this->composerBinaryPath = $bin;
      }
    }
    return NULL;
  }

}

Members

Title Sort descending Modifiers Object type Summary
ExecutableFinder::$composerBinaryPath private property The path of the Composer binary, or NULL if it can&#039;t be found.
ExecutableFinder::$composerPackagePath private property The path where Composer is installed in the project, or FALSE if it&#039;s not.
ExecutableFinder::find public function
ExecutableFinder::getLocalComposerPath private function Tries to find the Composer binary installed in the project.
ExecutableFinder::__construct public function

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.