You are here

public function ProjectCollector::collectProjects in Upgrade Status 8.3

Same name and namespace in other branches
  1. 8 src/ProjectCollector.php \Drupal\upgrade_status\ProjectCollector::collectProjects()
  2. 8.2 src/ProjectCollector.php \Drupal\upgrade_status\ProjectCollector::collectProjects()

Collect projects of installed modules grouped by custom and contrib.

Return value

\Drupal\Core\Extension\Extension[] An array keyed by project names. Extensions selected as projects without a defined project name get one based on their topmost parent extension and only that topmost extension gets included in the list.

1 call to ProjectCollector::collectProjects()
ProjectCollector::loadProject in src/ProjectCollector.php
Returns a single extension based on type and machine name.

File

src/ProjectCollector.php, line 182

Class

ProjectCollector
Collects projects and their associated metadata collated for Upgrade Status.

Namespace

Drupal\upgrade_status

Code

public function collectProjects() {
  $projects = [];
  $modules = $this->moduleExtensionList
    ->getList();
  $themes = $this->themeExtensionList
    ->getList();
  $profiles = $this->profileExtensionList
    ->getList();
  $extensions = array_merge($modules, $themes, $profiles);
  unset($modules, $themes, $profiles);
  $update_check_for_uninstalled = $this->configFactory
    ->get('update.settings')
    ->get('check.disabled_extensions');

  /** @var \Drupal\Core\Extension\Extension $extension */
  foreach ($extensions as $key => $extension) {
    if ($extension->origin === 'core') {

      // Ignore core extensions for the sake of upgrade status.
      continue;
    }

    // If the project is already specified in this extension, use that.
    $project = isset($extension->info['project']) ? $extension->info['project'] : '';
    if (isset($projects[$project])) {

      // If we already have a representative of this project in the list,
      // don't add this extension.
      // @todo Make sure to use the extension with the shortest file path.
      // If the existing project was already Drupal 9 compatible, consider
      // this subcomponent as well. If this component was enabled, it would
      // affect how we consider the Drupal 9 compatibility.
      if (!empty($projects[$project]->info['upgrade_status_next_major_compatible']) && !empty($extension->status)) {

        // Overwrite compatibility. If this is still compatible, it will
        // keep being TRUE, otherwise FALSE.
        $projects[$project]->info['upgrade_status_next_major_compatible'] = isset($extension->info['core_version_requirement']) && self::isCompatibleWithNextMajorDrupal($extension->info['core_version_requirement']);
      }
      continue;
    }
    if (strpos($key, 'upgrade_status') === 0 && !drupal_valid_test_ua()) {

      // Don't add the Upgrade Status modules to the list if not in tests.
      // Upgrade status is a temporary site component and does have
      // intentional deprecated API use for the sake of testing. Avoid
      // distracting site owners with this.
      continue;
    }

    // Attempt to identify if the project was contrib based on the directory
    // structure it is in. Extension placement is not a mandatory requirement
    // and theoretically this could lead to false positives, but if
    // composer_deploy or git_deploy is not available (and/or did not
    // identify the project for us), this is all we can do. Ignore our test
    // modules for this scenario.
    if (empty($project)) {
      $type = self::TYPE_CUSTOM;
      if (strpos($extension
        ->getPath(), '/contrib/') && strpos($key, 'upgrade_status_test_') !== 0) {
        $type = self::TYPE_CONTRIB;
      }
    }
    elseif ($project === 'drupal') {
      $type = self::TYPE_CUSTOM;
    }
    else {
      $type = self::TYPE_CONTRIB;
    }

    // Add additional information to the extension info for our tracking.
    // Keep this on a cloned extension object so we are not polluting runtime
    // extension information elsewhere.
    $extdata = clone $extension;
    $extdata->info['upgrade_status_type'] = $type;
    $extdata->info['upgrade_status_next_major_compatible'] = isset($extdata->info['core_version_requirement']) && self::isCompatibleWithNextMajorDrupal($extdata->info['core_version_requirement']);

    // Save this as a possible project to consider.
    $projects[$key] = $extdata;
  }

  // Collate extensions to projects, removing sub-extensions.
  $projects = $this
    ->collateExtensionsIntoProjects($projects);

  // After the collation is done, assign project names based on the topmost
  // extension. While this is not always right for drupal.org projects, this
  // is the best guess we have.
  foreach ($projects as $name => $extension) {
    if (!isset($extension->info['project'])) {
      $projects[$name]->info['project'] = $name;
    }

    // Add available update information to contrib projects found.
    if ($extension->info['upgrade_status_type'] == self::TYPE_CONTRIB) {

      // Look up by drupal.org project info not $name because the two may be different.
      $project_update = $this->availableUpdates
        ->get($extension->info['project']);
      if (!isset($project_update['releases']) || is_null($project_update['releases'])) {

        // Releases were either not checked or not available.
        $projects[$name]->info['upgrade_status_update'] = $update_check_for_uninstalled ? self::UPDATE_NOT_AVAILABLE : self::UPDATE_NOT_CHECKED;
      }
      else {

        // Add Drupal 9 compatibility info from the update's data.
        $latest_release = reset($project_update['releases']);
        $projects[$name]->info['upgrade_status_update_compatible'] = FALSE;
        if (!empty($latest_release['core_compatibility']) && self::isCompatibleWithNextMajorDrupal($latest_release['core_compatibility'])) {
          $projects[$name]->info['upgrade_status_update_compatible'] = TRUE;
        }

        // Denormalize update info into the extension info for our own use.
        if ($extension->info['version'] !== $latest_release['version']) {
          $projects[$name]->info['upgrade_status_update'] = self::UPDATE_AVAILABLE;
          $link = $project_update['link'] . '/releases/' . $latest_release['version'];
          $projects[$name]->info['upgrade_status_update_link'] = $link;
          $projects[$name]->info['upgrade_status_update_version'] = $latest_release['version'];
        }
        else {

          // If the current version is already the latest, store that.
          $projects[$name]->info['upgrade_status_update'] = self::UPDATE_ALREADY_INSTALLED;
        }
      }
    }

    // Get scan results if there was any.
    $scan_result = $this
      ->getResults($name);

    // Pick a suggested next step for this project.
    if ($extension->info['upgrade_status_next_major_compatible'] && $extension->info['upgrade_status_type'] == self::TYPE_CONTRIB) {

      // If the project was contrib and already Drupal 9 compatible, relax.
      $extension->info['upgrade_status_next'] = self::NEXT_RELAX;
    }
    elseif (empty($extension->status)) {

      // Uninstalled modules should be removed.
      $extension->info['upgrade_status_next'] = self::NEXT_REMOVE;
    }
    elseif (isset($extension->info['upgrade_status_update']) && $extension->info['upgrade_status_update'] == self::UPDATE_AVAILABLE) {

      // If there was a Drupal 9 compatible update or even a yet incompatible
      // update to this project, the best course of action is to update to
      // that, since that should move closer to Drupal 9 compatibility.
      $extension->info['upgrade_status_next'] = self::NEXT_UPDATE;
    }
    elseif ($extension->info['upgrade_status_type'] == self::TYPE_CONTRIB) {

      // For installed contributed modules that do not have compatile updates, collaborate.
      $extension->info['upgrade_status_next'] = self::NEXT_COLLABORATE;
    }
    else {

      // If there was no scanning result yet, next step is to scan this project.
      if (empty($scan_result) || empty($scan_result['data']['totals']['upgrade_status_next'])) {
        $extension->info['upgrade_status_next'] = self::NEXT_SCAN;
      }
      else {
        $extension->info['upgrade_status_next'] = $scan_result['data']['totals']['upgrade_status_next'];
      }
    }
  }
  return $projects;
}