public function DeprecationAnalyzer::analyze in Upgrade Status 8.2
Same name and namespace in other branches
- 8.3 src/DeprecationAnalyzer.php \Drupal\upgrade_status\DeprecationAnalyzer::analyze()
Analyze the codebase of an extension including all its sub-components.
Parameters
\Drupal\Core\Extension\Extension $extension: The extension to analyze.
Return value
null Errors are logged to the logger, data is stored to keyvalue storage.
File
- src/
DeprecationAnalyzer.php, line 256
Class
Namespace
Drupal\upgrade_statusCode
public function analyze(Extension $extension) {
try {
$this
->initEnvironment();
} catch (\Exception $e) {
// Should not get here as integrations are expected to invoke
// initEnvironment() first by itself to ensure the environment
// is going to work when needed (and inform users about any
// issues). That said, if they did not do that and there was
// no issue with the environment, then they are lucky.
return;
}
$project_dir = DRUPAL_ROOT . '/' . $extension
->getPath();
$this->logger
->notice('Processing %path.', [
'%path' => $project_dir,
]);
$output = [];
exec($this->binPath . '/phpstan analyse --error-format=json -c ' . $this->phpstanNeonPath . ' ' . $project_dir, $output);
$json = json_decode(implode('', $output), TRUE);
if (!isset($json['files']) || !is_array($json['files'])) {
$this->logger
->error('PHPStan failed: %results', [
'%results' => print_r($output, TRUE),
]);
$json = [
'files' => [
'PHPStan failed' => 'PHP API deprecations cannot be checked. Reason: ' . print_r($output, TRUE),
'line' => 0,
],
'totals' => [
'errors' => 1,
'file_errors' => 1,
],
];
}
$result = [
'date' => $this->time
->getRequestTime(),
'data' => $json,
];
$twig_deprecations = $this
->analyzeTwigTemplates($extension
->getPath());
foreach ($twig_deprecations as $twig_deprecation) {
preg_match('/\\s([a-zA-Z0-9\\_\\-\\/]+.html\\.twig)\\s/', $twig_deprecation, $file_matches);
preg_match('/\\s(\\d).?$/', $twig_deprecation, $line_matches);
$twig_deprecation = preg_replace('! in (.+)\\.twig at line \\d+\\.!', '.', $twig_deprecation);
$twig_deprecation .= ' See https://drupal.org/node/3071078.';
$result['data']['files'][$file_matches[1]]['messages'] = [
[
'message' => $twig_deprecation,
'line' => $line_matches[1] ?: 0,
],
];
$result['data']['totals']['errors']++;
$result['data']['totals']['file_errors']++;
}
$deprecation_messages = $this->libraryDeprecationAnalyzer
->analyze($extension);
foreach ($deprecation_messages as $deprecation_message) {
$result['data']['files'][$deprecation_message
->getFile()]['messages'][] = [
'message' => $deprecation_message
->getMessage(),
'line' => $deprecation_message
->getLine(),
];
$result['data']['totals']['errors']++;
$result['data']['totals']['file_errors']++;
}
$theme_function_deprecations = $this->themeFunctionDeprecationAnalyzer
->analyze($extension);
foreach ($theme_function_deprecations as $deprecation_message) {
$result['data']['files'][$deprecation_message
->getFile()]['messages'][] = [
'message' => $deprecation_message
->getMessage(),
'line' => $deprecation_message
->getLine(),
];
$result['data']['totals']['errors']++;
$result['data']['totals']['file_errors']++;
}
// Assume this project is Drupal 9 ready unless proven otherwise.
$result['data']['totals']['upgrade_status_split']['declared_ready'] = TRUE;
$info_files = $this
->getSubExtensionInfoFiles($project_dir);
foreach ($info_files as $info_file) {
try {
// Manually add on info file incompatibility to results. Reding
// .info.yml files directly, not from extension discovery because that
// is cached.
$info = Yaml::decode(file_get_contents($info_file)) ?: [];
if (!empty($info['package']) && $info['package'] == 'Testing' && !strpos($info_file, '/upgrade_status_test')) {
// If this info file was for a testing project other than our own
// testing projects, ignore it.
continue;
}
$error_path = str_replace(DRUPAL_ROOT . '/', '', $info_file);
if (!isset($info['core_version_requirement'])) {
$result['data']['files'][$error_path]['messages'][] = [
'message' => "Add core_version_requirement: ^8 || ^9 to designate that the module is compatible with Drupal 9. See https://drupal.org/node/3070687.",
'line' => 0,
];
$result['data']['totals']['errors']++;
$result['data']['totals']['file_errors']++;
$result['data']['totals']['upgrade_status_split']['declared_ready'] = FALSE;
}
elseif (!Semver::satisfies('9.0.0', $info['core_version_requirement'])) {
$result['data']['files'][$error_path]['messages'][] = [
'message' => "Value of core_version_requirement: {$info['core_version_requirement']} is not compatible with Drupal 9.0.0. See https://drupal.org/node/3070687.",
'line' => 0,
];
$result['data']['totals']['errors']++;
$result['data']['totals']['file_errors']++;
$result['data']['totals']['upgrade_status_split']['declared_ready'] = FALSE;
}
} catch (InvalidDataTypeException $e) {
$result['data']['files'][$error_path]['messages'][] = [
'message' => 'Parse error. ' . $e
->getMessage(),
'line' => 0,
];
$result['data']['totals']['errors']++;
$result['data']['totals']['file_errors']++;
$result['data']['totals']['upgrade_status_split']['declared_ready'] = FALSE;
}
}
// Manually add on composer.json file incompatibility to results.
if (file_exists($project_dir . '/composer.json')) {
$composer_json = json_decode(file_get_contents($project_dir . '/composer.json'));
if (empty($composer_json) || !is_object($composer_json)) {
$result['data']['files'][$extension
->getPath() . '/composer.json']['messages'][] = [
'message' => "Parse error in composer.json. Having a composer.json is not a requirement for Drupal 9 compatibility but if there is one, it should be valid. See https://drupal.org/node/2514612.",
'line' => 0,
];
$result['data']['totals']['errors']++;
$result['data']['totals']['file_errors']++;
$result['data']['totals']['upgrade_status_split']['declared_ready'] = FALSE;
}
elseif (!empty($composer_json->require->{'drupal/core'}) && !Semver::satisfies('9.0.0', $composer_json->require->{'drupal/core'})) {
$result['data']['files'][$extension
->getPath() . '/composer.json']['messages'][] = [
'message' => "The drupal/core requirement is not Drupal 9 compatible. Either remove it or update it to be compatible with Drupal 9. See https://drupal.org/node/2514612#s-drupal-9-compatibility.",
'line' => 0,
];
$result['data']['totals']['errors']++;
$result['data']['totals']['file_errors']++;
$result['data']['totals']['upgrade_status_split']['declared_ready'] = FALSE;
}
}
foreach ($result['data']['files'] as $path => &$errors) {
if (!empty($errors['messages'])) {
foreach ($errors['messages'] as &$error) {
// Overwrite message with processed text. Save category.
[
$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',
'rector',
])) {
@$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'])) {
try {
/** @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
}
}
} catch (\Exception $e) {
$this->logger
->error($e
->getMessage());
}
}
// Store the analysis results in our storage bin.
$this->scanResultStorage
->set($extension
->getName(), json_encode($result));
}