You are here

protected function FeaturesCommands::componentFilter in Features 8.3

Same name and namespace in other branches
  1. 8.4 src/Commands/FeaturesCommands.php \Drupal\features\Commands\FeaturesCommands::componentFilter()

Filters components by patterns.

2 calls to FeaturesCommands::componentFilter()
FeaturesCommands::add in src/Commands/FeaturesCommands.php
Add a config item to a feature package.
FeaturesCommands::components in src/Commands/FeaturesCommands.php
List features components.

File

src/Commands/FeaturesCommands.php, line 853

Class

FeaturesCommands
Drush commands for Features.

Namespace

Drupal\features\Commands

Code

protected function componentFilter($all_components, $patterns = [], $options = []) {
  $options += [
    'exported' => TRUE,
    'not exported' => TRUE,
    'provided by' => FALSE,
  ];
  $pool = [];

  // Maps exported components to feature modules.
  $components_map = $this
    ->componentMap();

  // First filter on exported state.
  foreach ($all_components as $source => $components) {
    foreach ($components as $name => $title) {
      $exported = count($components_map[$source][$name]) > 0;
      if ($exported) {
        if ($options['exported']) {
          $pool[$source][$name] = $title;
        }
      }
      else {
        if ($options['not exported']) {
          $pool[$source][$name] = $title;
        }
      }
    }
  }
  $state_string = '';
  if (!$options['exported']) {
    $state_string = 'unexported';
  }
  elseif (!$options['not exported']) {
    $state_string = 'exported';
  }
  $selected = [];
  foreach ($patterns as $pattern) {

    // Rewrite * to %. Let users use both as wildcard.
    $pattern = strtr($pattern, [
      '*' => '%',
    ]);
    $sources = [];
    list($source_pattern, $component_pattern) = explode(':', $pattern, 2);

    // If source is empty, use a pattern.
    if ($source_pattern == '') {
      $source_pattern = '%';
    }
    if ($component_pattern == '') {
      $component_pattern = '%';
    }
    $preg_source_pattern = strtr(preg_quote($source_pattern, '/'), [
      '%' => '.*',
    ]);
    $preg_component_pattern = strtr(preg_quote($component_pattern, '/'), [
      '%' => '.*',
    ]);

    // If it isn't a pattern, but a simple string, we don't anchor the
    // pattern. This allows for abbreviating. Otherwise, we do, as this seems
    // more natural for patterns.
    if (strpos($source_pattern, '%') !== FALSE) {
      $preg_source_pattern = '^' . $preg_source_pattern . '$';
    }
    if (strpos($component_pattern, '%') !== FALSE) {
      $preg_component_pattern = '^' . $preg_component_pattern . '$';
    }
    $matches = [];

    // Find the sources.
    $all_sources = array_keys($pool);
    $matches = preg_grep('/' . $preg_source_pattern . '/', $all_sources);
    if (count($matches) > 0) {

      // If we have multiple matches and the source string wasn't a
      // pattern, check if one of the matches is equal to the pattern, and
      // use that, or error out.
      if (count($matches) > 1 and $preg_source_pattern[0] != '^') {
        if (in_array($source_pattern, $matches)) {
          $matches = [
            $source_pattern,
          ];
        }
        else {
          throw new \Exception(dt('Ambiguous source "@source", matches @matches', [
            '@source' => $source_pattern,
            '@matches' => implode(', ', $matches),
          ]));
        }
      }

      // Loose the indexes preg_grep preserved.
      $sources = array_values($matches);
    }
    else {
      throw new \Exception(dt('No @state sources match "@source"', [
        '@state' => $state_string,
        '@source' => $source_pattern,
      ]));
    }

    // Now find the components.
    foreach ($sources as $source) {

      // Find the components.
      $all_components = array_keys($pool[$source]);

      // See if there's any matches.
      $matches = preg_grep('/' . $preg_component_pattern . '/', $all_components);
      if (count($matches) > 0) {

        // If we have multiple matches and the components string wasn't a
        // pattern, check if one of the matches is equal to the pattern, and
        // use that, or error out.
        if (count($matches) > 1 and $preg_component_pattern[0] != '^') {
          if (in_array($component_pattern, $matches)) {
            $matches = [
              $component_pattern,
            ];
          }
          else {
            throw new \Exception(dt('Ambiguous component "@component", matches @matches', [
              '@component' => $component_pattern,
              '@matches' => implode(', ', $matches),
            ]));
          }
        }
        if (!is_array($selected[$source])) {
          $selected[$source] = [];
        }
        $selected[$source] += array_intersect_key($pool[$source], array_flip($matches));
      }
      else {

        // No matches. If the source was a pattern, just carry on, else
        // error out. Allows for patterns like ":*field*".
        if ($preg_source_pattern[0] != '^') {
          throw new \Exception(dt('No @state @source components match "@component"', [
            '@state' => $state_string,
            '@component' => $component_pattern,
            '@source' => $source,
          ]));
        }
      }
    }
  }

  // Lastly, provide feature module information on the selected components, if
  // requested.
  $provided_by = [];
  if ($options['provided by'] && $options['exported']) {
    foreach ($selected as $source => $components) {
      foreach ($components as $name => $title) {
        $exported = count($components_map[$source][$name]) > 0;
        if ($exported) {
          $provided_by[$source . ':' . $name] = implode(', ', $components_map[$source][$name]);
        }
      }
    }
  }
  return [
    'components' => $selected,
    'sources' => $provided_by,
  ];
}