You are here

class ComposerJsonSource in PatchInfo 8.2

Gathers patch information from composer.json files.

This patch source will read patches from your composer.json files and any external patch files specified in your composer.json files. For Drupal Core, it will check for a composer.json in your Drupal root directory or in the core folder.

It is assumed, that 'cweagans/composer-patches' is used for patch management with Composer.

Presently, the source plugin will skip any patches for modules outside the 'drupal/' namespace.

Plugin annotation


@PatchInfoSource(
  id = "patchinfo_composer",
  label = @Translation("composer.json", context = "PatchInfoSource"),
)

Hierarchy

Expanded class hierarchy of ComposerJsonSource

See also

https://github.com/cweagans/composer-patches

File

modules/patchinfo_source_composer/src/Plugin/PatchInfo/Source/ComposerJsonSource.php, line 32

Namespace

Drupal\patchinfo_source_composer\Plugin\patchinfo\source
View source
class ComposerJsonSource extends PatchInfoSourceBase {

  /**
   * The file system service.
   *
   * @var \Drupal\Core\File\FileSystemInterface
   */
  protected $fileSystem;

  /**
   * Constructs a PatchInfoSourceBase object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
   *   The logger factory.
   * @param \Drupal\Core\File\FileSystemInterface $file_system
   *   The file system service.
   */
  public function __construct(array $configuration, string $plugin_id, $plugin_definition, LoggerChannelFactoryInterface $logger_factory, FileSystemInterface $file_system) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $logger_factory, $file_system);
    $this->fileSystem = $file_system;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static($configuration, $plugin_id, $plugin_definition, $container
      ->get('logger.factory'), $container
      ->get('file_system'));
  }

  /**
   * {@inheritdoc}
   */
  public function getPatches(array $info, Extension $file, string $type) {
    $return = [];
    if (!in_array($type, [
      'module',
      'theme',
    ])) {
      return $return;
    }
    $patches = $this
      ->parseComposerFile($file
      ->getPath());
    $return = array_merge_recursive($return, $patches);
    if ($file
      ->getName() == 'system') {

      // Check for patches in /composer.json, ../composer.json and
      // /core/composer.json.
      $core_base_path = str_replace('/modules/system', '', $file
        ->getPath());
      $patches = $this
        ->parseComposerFile($core_base_path);
      $return = array_merge_recursive($return, $patches);
      $patches = $this
        ->parseComposerFile('.');
      $return = array_merge_recursive($return, $patches);
      $patches = $this
        ->parseComposerFile('..');
      $return = array_merge_recursive($return, $patches);
    }
    return $return;
  }

  /**
   * Parses composer.json within a given path for patches.
   *
   * @param string $path
   *   A path from the local filesystem from where to fetch composer.json.
   *
   * @return array
   *   An array of patch information keyed by composer project name. The patch
   *   information is an array with an info key and a source key. The info key
   *   is a string with the url of the patch file followed by a space and the
   *   patch description. The source key is the path to the composer.json file
   *   that contained the patch information or link to the external patch file.
   */
  protected function parseComposerFile(string $path) {
    $return = [];

    // Try to prevent triggering open basedir restrictions.
    if (stream_resolve_include_path($path . DIRECTORY_SEPARATOR . 'composer.json') === FALSE) {
      return $return;
    }
    $path = $this->fileSystem
      ->realpath($path);
    $config = $this
      ->getDecodedJson($path, 'composer.json');
    $patches = [];
    if (!empty($config['extra']['patches'])) {
      $patches = $config['extra']['patches'];
    }
    elseif (!empty($config['extra']['patches-file'])) {
      $patchfile = $this
        ->getDecodedJson($path, $config['extra']['patches-file']);
      if (!empty($patchfile['patches'])) {
        $patches = $patchfile['patches'];
      }
    }
    foreach ($patches as $project => $project_patches) {
      if (strpos($project, 'drupal/') !== 0) {

        // Only handle Drupal projects.
        continue;
      }

      // Generate Drupal project name from composer project name.
      $project = str_replace('drupal/', '', $project);
      if ($project == 'core' || $project == 'drupal') {
        $project = 'system';
      }
      foreach ($project_patches as $description => $url) {
        $info = $url . ' ' . $description;
        $return[$project][] = [
          'info' => trim($info),
          'source' => $path . '/composer.json',
        ];
      }
    }
    return $return;
  }

  /**
   * Gets decoded JSON from a JSON file.
   *
   * @param string $path
   *   Path to JSON file.
   * @param string $file
   *   JSON file name.
   *
   * @return mixed
   *   Parsed JSON.
   */
  protected function getDecodedJson(string $path, string $file) {
    $return = [];
    if (!file_exists($path . '/' . $file)) {
      return $return;
    }
    if (!is_readable($path . '/' . $file)) {
      $this->loggerFactory
        ->get('patchinfo_source_composer')
        ->warning($this
        ->t('Can not read @path/@file. Check your file permissions.', [
        '@path' => $path,
        '@file' => $file,
      ]));
      return $return;
    }
    $content = file_get_contents($path . '/' . $file);
    if ($content === FALSE) {
      $this->loggerFactory
        ->get('patchinfo_source_composer')
        ->warning($this
        ->t('Can not get contents from @path/@file.', [
        '@path' => $path,
        '@file' => $file,
      ]));
      return $return;
    }
    $config = json_decode($content, TRUE);
    if ($config === NULL) {
      $this->loggerFactory
        ->get('patchinfo_source_composer')
        ->warning($this
        ->t('Unable to parse @path/@file. Check your JSON syntax.', [
        '@path' => $path,
        '@file' => $file,
      ]));
      return $return;
    }
    return $config;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ComposerJsonSource::$fileSystem protected property The file system service.
ComposerJsonSource::create public static function Creates an instance of the plugin. Overrides PatchInfoSourceBase::create
ComposerJsonSource::getDecodedJson protected function Gets decoded JSON from a JSON file.
ComposerJsonSource::getPatches public function Gets patch information for a module from a patch source. Overrides PatchInfoSourceBase::getPatches
ComposerJsonSource::parseComposerFile protected function Parses composer.json within a given path for patches.
ComposerJsonSource::__construct public function Constructs a PatchInfoSourceBase object. Overrides PatchInfoSourceBase::__construct
PatchInfoSourceBase::$loggerFactory protected property The logger factory.
PatchInfoSourceBase::getLabel public function Gets the plugin label. Overrides PatchInfoSourceInterface::getLabel
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.