You are here

public function DeprecationAnalyser::analyse in Upgrade Status 8

Analyse the codebase of an extension including all its sub-components.

Parameters

\Drupal\Core\Extension\Extension $extension: The extension to analyse.

Return value

null Errors are logged to the logger, data is stored to keyvalue storage.

Overrides DeprecationAnalyserInterface::analyse

File

src/DeprecationAnalyser.php, line 156

Class

DeprecationAnalyser

Namespace

Drupal\upgrade_status

Code

public function analyse(Extension $extension) {

  // Prepare for possible fatal errors while autoloading or due to issues with
  // dependencies.
  drupal_register_shutdown_function([
    $this,
    'logFatalError',
  ], $extension);

  // Set the autoloader for PHPStan.
  if (!isset($GLOBALS['autoloaderInWorkingDirectory'])) {
    $GLOBALS['autoloaderInWorkingDirectory'] = DRUPAL_ROOT . '/autoload.php';
  }
  $project_dir = DRUPAL_ROOT . '/' . $extension->subpath;
  $paths = $this
    ->getDirContents($project_dir);
  foreach ($paths as $key => $file_path) {
    if (substr($file_path, -3) !== 'php' && substr($file_path, -7) !== '.module' && substr($file_path, -8) !== '.install' && substr($file_path, -3) !== 'inc') {
      unset($paths[$key]);
    }
  }
  $this->logger
    ->notice($this
    ->t("Extension @project_machine_name contains @number files to process.", [
    '@project_machine_name' => $extension
      ->getName(),
    '@number' => count($paths),
  ]));
  $result = [];
  $result['date'] = REQUEST_TIME;
  $result['data'] = [
    'totals' => [
      'errors' => 0,
      'file_errors' => 0,
    ],
    'files' => [],
  ];

  // Manually add on info file incompatibility to phpstan results.
  $info = $extension->info;
  if (!isset($info['core_version_requirement'])) {
    $result['data']['files'][$extension
      ->getFilename()]['messages'] = [
      [
        'message' => 'Add <code>core_version_requirement: ^8 || ^9</code> to ' . $extension
          ->getFilename() . ' to designate that the module is compatible with Drupal 9. See https://www.drupal.org/node/3070687.',
        'line' => 0,
      ],
    ];
    $result['data']['totals']['errors']++;
    $result['data']['totals']['file_errors']++;
  }
  elseif (!Semver::satisfies('9.0.0', $info['core_version_requirement'])) {
    $result['data']['files'][$extension
      ->getFilename()]['messages'] = [
      [
        'message' => "The current value  <code>core_version_requirement: {$info['core_version_requirement']}</code> in {$extension->getFilename()} is not compatible with Drupal 9.0.0. See https://www.drupal.org/node/3070687.",
        'line' => 0,
      ],
    ];
    $result['data']['totals']['errors']++;
    $result['data']['totals']['file_errors']++;
  }
  if (!empty($paths)) {
    $num_of_files = $this->config
      ->get('paths_per_scan') ?: 30;

    // @todo: refactor and validate.
    for ($offset = 0; $offset <= count($paths); $offset += $num_of_files) {
      $files = array_slice($paths, $offset, $num_of_files);
      if (!empty($files)) {
        $raw_errors = $this
          ->runPhpStan($files);
        $errors = json_decode($raw_errors, TRUE);
        if (!is_array($errors)) {
          continue;
        }
        $result['data']['totals']['errors'] += $errors['totals']['errors'];
        $result['data']['totals']['file_errors'] += $errors['totals']['file_errors'];
        $result['data']['files'] = array_merge($result['data']['files'], $errors['files']);
      }
    }
  }
  foreach ($result['data']['files'] as $path => &$errors) {
    if (!empty($errors['messages'])) {
      foreach ($errors['messages'] as &$error) {

        // Overwrite message with processed text. Save category.
        list($message, $category) = $this
          ->categorizeMessage($error['message'], $extension);
        $error['message'] = $message;
        $error['upgrade_status_category'] = $category;

        // Sum up the error based on the category it ended up in. Split the
        // categories into two high level buckets needing attention now or
        // later for Drupal 9 compatibility. Ignore Drupal 10 here.
        @$result['data']['totals']['upgrade_status_category'][$category]++;
        if (in_array($category, [
          'safe',
          'old',
        ])) {
          @$result['data']['totals']['upgrade_status_split']['error']++;
        }
        elseif (in_array($category, [
          'later',
          'uncategorized',
        ])) {
          @$result['data']['totals']['upgrade_status_split']['warning']++;
        }
      }
    }
  }

  // For contributed projects, attempt to grab Drupal 9 plan information.
  if (!empty($extension->info['project'])) {

    /** @var \Psr\Http\Message\ResponseInterface $response */
    $response = $this->httpClient
      ->request('GET', 'https://www.drupal.org/api-d7/node.json?field_project_machine_name=' . $extension
      ->getName());
    if ($response
      ->getStatusCode()) {
      $data = json_decode($response
        ->getBody(), TRUE);
      if (!empty($data['list'][0]['field_next_major_version_info']['value'])) {
        $result['plans'] = str_replace('href="/', 'href="https://drupal.org/', $data['list'][0]['field_next_major_version_info']['value']);

        // @todo implement "replaced by" collection once drupal.org exposes
        // that in an accessible way
        // @todo once/if drupal.org deprecation testing is in place, grab
        // the status from there so we know if it improves by updating
      }
    }
  }

  // Store the analysis results in our storage bin.
  $this->scanResultStorage
    ->set($extension
    ->getName(), json_encode($result));
}