View source
<?php
namespace Drupal\upgrade_status;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Component\Serialization\Yaml;
use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
use Drupal\Core\Extension\Extension;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
use Drupal\Core\Template\TwigEnvironment;
use DrupalFinder\DrupalFinder;
use GuzzleHttp\Client;
use Psr\Log\LoggerInterface;
use Twig\Util\DeprecationCollector;
use Twig\Util\TemplateDirIterator;
final class DeprecationAnalyzer {
protected $scanResultStorage;
protected $logger;
protected $phpstanNeonPath;
protected $vendorPath;
protected $binPath;
protected $temporaryDirectory;
protected $httpClient;
protected $fileSystem;
protected $twigEnvironment;
protected $libraryDeprecationAnalyzer;
protected $themeFunctionDeprecationAnalyzer;
protected $time;
protected $finder;
protected $environmentInitialized = FALSE;
public function __construct(KeyValueFactoryInterface $key_value_factory, LoggerInterface $logger, Client $http_client, FileSystemInterface $file_system, TwigEnvironment $twig_environment, LibraryDeprecationAnalyzer $library_deprecation_analyzer, ThemeFunctionDeprecationAnalyzer $theme_function_deprecation_analyzer, TimeInterface $time) {
$this->scanResultStorage = $key_value_factory
->get('upgrade_status_scan_results');
$this->logger = $logger;
$this->httpClient = $http_client;
$this->fileSystem = $file_system;
$this->twigEnvironment = $twig_environment;
$this->libraryDeprecationAnalyzer = $library_deprecation_analyzer;
$this->themeFunctionDeprecationAnalyzer = $theme_function_deprecation_analyzer;
$this->time = $time;
}
public function initEnvironment() {
if (!empty($this->environmentInitialized)) {
return;
}
$this->finder = new DrupalFinder();
$this->finder
->locateRoot(DRUPAL_ROOT);
$this->vendorPath = $this->finder
->getVendorDir();
$this->binPath = $this
->findBinPath();
if (function_exists('file_directory_temp')) {
$system_temporary = file_directory_temp();
}
else {
$system_temporary = $this->fileSystem
->getTempDirectory();
}
$this->temporaryDirectory = $system_temporary . '/upgrade_status';
if (!file_exists($this->temporaryDirectory)) {
$this
->prepareTempDirectory();
}
$this->phpstanNeonPath = $this->temporaryDirectory . '/deprecation_testing.neon';
$this
->createModifiedNeonFile();
$this->environmentInitialized = TRUE;
}
protected function findBinPath() {
$composer_name = trim(getenv('COMPOSER')) ?: 'composer.json';
$composer_json_path = $this->finder
->getComposerRoot() . '/' . $composer_name;
if ($composer_json_path && file_exists($composer_json_path)) {
$json = json_decode(file_get_contents($composer_json_path), TRUE);
if (is_null($json) || !is_array($json)) {
throw new \Exception('Unable to decode composer information from ' . $composer_json_path . '.');
}
}
else {
throw new \Exception('The composer.json file was not found at ' . $composer_json_path . '.');
}
if (isset($json['config']['bin-dir'])) {
$binPath = $this->finder
->getComposerRoot() . '/' . $json['config']['bin-dir'];
if (file_exists($binPath . '/phpstan')) {
return $binPath;
}
else {
throw new \Exception('The PHPStan binary was not found in the bin-dir specified by ' . $composer_json_path . '. Attempted: ' . $binPath . '/phpstan.');
}
}
if (isset($json['config']['vendor-dir'])) {
$binPath = $this->finder
->getComposerRoot() . '/' . $json['config']['vendor-dir'] . '/bin';
if (file_exists($binPath . '/phpstan')) {
return $binPath;
}
else {
throw new \Exception('The PHPStan binary was not found in the vendor-dir specified by ' . $composer_json_path . '. Attempted: ' . $binPath . '/phpstan.');
}
}
$binPath = $this->finder
->getComposerRoot() . '/vendor/bin';
if (file_exists($binPath . '/phpstan')) {
return $binPath;
}
throw new \Exception('The PHPStan binary was not found in the default vendor directory based on the location of ' . $composer_json_path . '. You may need to configure a vendor-dir in composer.json. See https://getcomposer.org/doc/06-config.md#vendor-dir. Attempted: ' . $binPath . '/phpstan.');
}
public function analyze(Extension $extension) {
try {
$this
->initEnvironment();
} catch (\Exception $e) {
return;
}
$project_dir = DRUPAL_ROOT . '/' . $extension
->getPath();
$this->logger
->notice('Processing %path.', [
'%path' => $project_dir,
]);
$output = [];
$error_filename = $this->temporaryDirectory . '/phpstan_error_output';
$command = $this->binPath . '/phpstan analyse --memory-limit=-1 --error-format=json -c ' . $this->phpstanNeonPath . ' ' . $project_dir . ' 2> ' . $error_filename;
exec($command, $output);
$json = json_decode(implode('', $output), TRUE);
if (!isset($json['files']) || !is_array($json['files'])) {
$stdout = trim(implode('', $output)) ?: 'Empty.';
$stderr = trim(file_get_contents($error_filename)) ?: 'Empty.';
$formatted_error = "<h6>PHPStan command failed:</h6> <p>" . $command . "</p> <h6>Command output:</h6> <p>" . $stdout . "</p> <h6>Command error:</h6> <p>" . $stderr . '</p>';
$this->logger
->error('%phpstan_fail', [
'%phpstan_fail' => strip_tags($formatted_error),
]);
$json = [
'files' => [
'PHPStan failed' => [
'messages' => [
[
'message' => $formatted_error,
'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']++;
}
$result['data']['totals']['upgrade_status_split']['declared_ready'] = TRUE;
$info_files = $this
->getSubExtensionInfoFiles($project_dir);
foreach ($info_files as $info_file) {
try {
$info = Yaml::decode(file_get_contents($info_file)) ?: [];
if (!empty($info['package']) && $info['package'] == 'Testing' && !strpos($info_file, '/upgrade_status_test')) {
continue;
}
$error_path = str_replace(DRUPAL_ROOT . '/', '', $info_file);
if ($info['type'] === 'theme') {
if (!isset($info['base theme'])) {
$result['data']['files'][$error_path]['messages'][] = [
'message' => "The now required 'base theme' key is missing. See https://www.drupal.org/node/3066038.",
'line' => 0,
];
$result['data']['totals']['errors']++;
$result['data']['totals']['file_errors']++;
}
}
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 (!ProjectCollector::isCompatibleWithNextMajorDrupal($info['core_version_requirement'])) {
$result['data']['files'][$error_path]['messages'][] = [
'message' => "Value of core_version_requirement: {$info['core_version_requirement']} is not compatible with the next major version of Drupal core. 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;
}
if (!empty($info['lifecycle'])) {
$link = !empty($info['lifecycle_link']) ? $info['lifecycle_link'] : 'https://www.drupal.org/node/3215042';
if ($info['lifecycle'] == 'deprecated') {
$result['data']['files'][$error_path]['messages'][] = [
'message' => "This extension is deprecated. Don't use it. See {$link}.",
'line' => 0,
];
$result['data']['totals']['errors']++;
$result['data']['totals']['file_errors']++;
$result['data']['totals']['upgrade_status_split']['declared_ready'] = FALSE;
}
elseif ($info['lifecycle'] == 'obsolete') {
$result['data']['files'][$error_path]['messages'][] = [
'message' => "This extension is obsolete. Obsolete extensions are usually uninstalled automatically when not needed anymore. You only need to do something about this if the uninstallation was unsuccesful. See {$link}.",
'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;
}
}
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 in general, 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'}) && !projectCollector::isCompatibleWithNextMajorDrupal($composer_json->require->{'drupal/core'})) {
$result['data']['files'][$extension
->getPath() . '/composer.json']['messages'][] = [
'message' => "The drupal/core requirement is not compatible with the next major version of Drupal. Either remove it or update it to be compatible. 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;
}
elseif (projectCollector::getDrupalCoreMajorVersion() > 8 && !empty($composer_json->require->{'php'} && !projectCollector::isCompatibleWithPHP8($composer_json->require->{'php'}))) {
$result['data']['files'][$extension
->getPath() . '/composer.json']['messages'][] = [
'message' => "The PHP requirement is not compatible with PHP 8. Once the codebase is actually compatible, either remove this limitation or update it to be compatible.",
'line' => 0,
];
$result['data']['totals']['errors']++;
$result['data']['totals']['file_errors']++;
$result['data']['totals']['upgrade_status_split']['declared_ready'] = FALSE;
}
}
$result['data']['totals']['upgrade_status_next'] = ProjectCollector::NEXT_RELAX;
foreach ($result['data']['files'] as $path => &$errors) {
foreach ($errors['messages'] as &$error) {
[
$message,
$category,
] = $this
->categorizeMessage($error['message'], $extension);
$error['message'] = $message;
$error['upgrade_status_category'] = $category;
if ($category == 'rector') {
$result['data']['totals']['upgrade_status_next'] = ProjectCollector::NEXT_RECTOR;
}
elseif ($result['data']['totals']['upgrade_status_next'] == ProjectCollector::NEXT_RELAX) {
$result['data']['totals']['upgrade_status_next'] = ProjectCollector::NEXT_MANUAL;
}
@$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']++;
}
}
}
if (!empty($extension->info['project'])) {
try {
$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']);
}
}
} catch (\Exception $e) {
$this->logger
->error($e
->getMessage());
}
}
$this->scanResultStorage
->set($extension
->getName(), $result);
}
protected function analyzeTwigTemplates($directory) {
$iterator = new TemplateDirIterator(new TwigRecursiveIterator($directory));
return (new DeprecationCollector($this->twigEnvironment))
->collect($iterator);
}
protected function prepareTempDirectory() {
$success = $this->fileSystem
->prepareDirectory($this->temporaryDirectory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
if (!$success) {
throw new \Exception('Unable to create temporary directory for Upgrade Status at ' . $this->temporaryDirectory);
}
$phpstan_cache_directory = $this->temporaryDirectory . '/phpstan';
$success = $this->fileSystem
->prepareDirectory($phpstan_cache_directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
if (!$success) {
throw new \Exception('Unable to create temporary directory for PHPStan at ' . $phpstan_cache_directory);
}
}
protected function createModifiedNeonFile() {
$module_path = DRUPAL_ROOT . '/' . drupal_get_path('module', 'upgrade_status');
$config = file_get_contents($module_path . '/deprecation_testing_template.neon');
$config = str_replace('parameters:', "parameters:\n\ttmpDir: '" . $this->temporaryDirectory . '/phpstan' . "'\n" . "\tdrupal:\n\t\tdrupal_root: '" . DRUPAL_ROOT . "'", $config);
if (!class_exists('PHPStan\\ExtensionInstaller\\GeneratedConfig')) {
$extension_neon = $this->vendorPath . '/mglaman/phpstan-drupal/extension.neon';
$rules_neon = $this->vendorPath . '/phpstan/phpstan-deprecation-rules/rules.neon';
if (!file_exists($extension_neon) || !file_exists($rules_neon)) {
throw new \Exception('Vendor source files were not found. You may need to configure a vendor-dir in composer.json. See https://getcomposer.org/doc/06-config.md#vendor-dir. Missing ' . $extension_neon . ' and ' . $rules_neon . '.');
}
$config .= "\nincludes:\n\t- '" . $extension_neon . "'\n\t- '" . $rules_neon . "'\n";
}
$success = file_put_contents($this->phpstanNeonPath, $config);
if (!$success) {
throw new \Exception('Unable to write configuration for PHPStan to ' . $this->phpstanNeonPath . '.');
}
}
protected function categorizeMessage(string $error, Extension $extension) {
$error = preg_replace('!\\s+!', ' ', trim($error));
$error = preg_replace('!:\\s+(in|as of)!', '. Deprecated \\1', $error);
$error = preg_replace('!(u|U)se \\\\Drupal!', '\\1se Drupal', $error);
$version = '';
if (preg_match('!\\\\(Web|)TestBase. Deprecated in [Dd]rupal[ :]8\\.8\\.0 !', $error)) {
$version = '8.6.0';
$error .= " Replacement available from drupal:8.6.0.";
}
elseif (preg_match('!Deprecated (in|as of) [Dd]rupal[ :](\\d+\\.\\d)!', $error, $version_found)) {
$version = $version_found[2];
}
$category = 'uncategorized';
if (!empty($version)) {
if (!empty($extension->info['project'])) {
if (version_compare($version, ProjectCollector::getOldestSupportedMinor()) <= 0) {
$category = 'old';
}
else {
$category = 'later';
}
}
else {
if (version_compare($version, \Drupal::VERSION) <= 0) {
$category = 'safe';
}
else {
$category = 'later';
}
}
}
if ($this
->isRectorCovered($error)) {
$category = 'rector';
}
if (preg_match('!(will be|is) removed (before|from) [Dd]rupal[ :](\\d+)\\.!', $error, $version_removed)) {
if ($version_removed[3] > ProjectCollector::getDrupalCoreMajorVersion() + 1) {
$category = 'ignore';
}
}
return [
$error,
$category,
];
}
protected function isRectorCovered($string) {
$rector_covered = [
'Call to deprecated function drupal_set_message(). Deprecated in drupal:8.5.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\Messenger\\MessengerInterface::addMessage() instead.',
'Call to deprecated method entityManager() of class Drupal. Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use Drupal::entityTypeManager() instead in most cases. If the needed method is not on \\Drupal\\Core\\Entity\\EntityTypeManagerInterface, see the deprecated \\Drupal\\Core\\Entity\\EntityManager to find the correct interface or service.',
'Call to deprecated method entityManager() of class Drupal\\Core\\Controller\\ControllerBase. Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Most of the time static::entityTypeManager() is supposed to be used instead.',
'Call to deprecated function db_insert(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call insert() on it. For example,',
'Call to deprecated function db_select(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call select() on it. For example,',
'Call to deprecated function db_query(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call query() on it. For example,',
'Call to deprecated function file_prepare_directory(). Deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\File\\FileSystemInterface::prepareDirectory().',
'Call to deprecated method getMock() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.5.0 and is removed from drupal:9.0.0. Use Drupal\\Tests\\PhpunitCompatibilityTrait::createMock() instead.',
'Call to deprecated method getMock() of class Drupal\\KernelTests\\KernelTestBase. Deprecated in drupal:8.5.0 and is removed from drupal:9.0.0. Use Drupal\\Tests\\PhpunitCompatibilityTrait::createMock() instead.',
'Call to deprecated method getMock() of class Drupal\\Tests\\UnitTestCase. Deprecated in drupal:8.5.0 and is removed from drupal:9.0.0. Use Drupal\\Tests\\PhpunitCompatibilityTrait::createMock() instead.',
'Call to deprecated method url() of class Drupal. Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead create a \\Drupal\\Core\\Url object directly, for example using Url::fromRoute().',
'Call to deprecated function format_date(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use Drupal::service(\'date.formatter\')->format().',
'Call to deprecated method strtolower() of class Drupal\\Component\\Utility\\Unicode. Deprecated in drupal:8.6.0 and is removed from drupal:9.0.0. Use mb_strtolower() instead.',
'Call to deprecated constant FILE_CREATE_DIRECTORY: Deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\File\\FileSystemInterface::CREATE_DIRECTORY.',
'Call to deprecated constant FILE_EXISTS_REPLACE: Deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\File\\FileSystemInterface::EXISTS_REPLACE.',
'Call to deprecated method l() of class Drupal. Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\Link::fromTextAndUrl() instead.',
'Call to deprecated function drupal_render(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use the',
'Call to deprecated function drupal_render_root(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\Render\\RendererInterface::renderRoot() instead.',
'Call to deprecated function file_unmanaged_save_data(). Deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\File\\FileSystemInterface::saveData().',
'Call to deprecated constant FILE_MODIFY_PERMISSIONS: Deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\File\\FileSystemInterface::MODIFY_PERMISSIONS.',
'Call to deprecated function db_delete(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call delete() on it. For example,',
'Call to deprecated function entity_get_form_display(). Deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use EntityDisplayRepositoryInterface::getFormDisplay() instead.',
'Call to deprecated function entity_get_display(). Deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use EntityDisplayRepositoryInterface::getViewDisplay() instead.',
'Call to deprecated constant REQUEST_TIME: Deprecated in drupal:8.3.0 and is removed from drupal:10.0.0. Use Drupal::time()->getRequestTime();',
'Call to deprecated method urlInfo() of class Drupal\\Core\\Entity\\EntityInterface. Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\Entity\\EntityInterface::toUrl() instead.',
'Call to deprecated function file_scan_directory(). Deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\File\\FileSystemInterface::scanDirectory() instead.',
'Call to deprecated function file_default_scheme(). Deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use Drupal::config(\'system.file\')->get(\'default_scheme\') instead.',
'Call to deprecated function db_update(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call update() on it. For example,',
'Call to deprecated method strtolower() of class Drupal\\Component\\Utility\\Unicode. Deprecated in drupal:8.6.0 and is removed from drupal:9.0.0. Use mb_strtolower() instead.',
'Call to deprecated method strlen() of class Drupal\\Component\\Utility\\Unicode. Deprecated in drupal:8.6.0 and is removed from drupal:9.0.0. Use mb_strlen() instead.',
'Call to deprecated method substr() of class Drupal\\Component\\Utility\\Unicode. Deprecated in drupal:8.6.0 and is removed from drupal:9.0.0. Use mb_substr() instead.',
'Call to deprecated method link() of class Drupal\\Core\\Entity\\EntityInterface. Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\EntityInterface::toLink()->toString() instead.',
'Call to deprecated function entity_load(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use the entity type storage\'s load() method.',
'Call to deprecated function node_load(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use Drupal\\node\\Entity\\Node::load().',
'Call to deprecated function file_load(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use Drupal\\file\\Entity\\File::load().',
'Call to deprecated function user_load(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use Drupal\\user\\Entity\\User::load().',
'Call to deprecated function file_directory_temp(). Deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\File\\FileSystemInterface::getTempDirectory() instead.',
'Call to deprecated function file_directory_os_temp(). Deprecated in drupal:8.3.0 and is removed from drupal:9.0.0. Use Drupal\\Component\\FileSystem\\FileSystem::getOsTemporaryDirectory().',
'Call to deprecated function drupal_realpath(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\File\\FileSystem::realpath().',
'Call to deprecated function file_uri_target(). Deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\StreamWrapper\\StreamWrapperManagerInterface::getTarget() instead.',
'Call to deprecated method format() of class Drupal\\Component\\Utility\\SafeMarkup. Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use Drupal\\Component\\Render\\FormattableMarkup.',
'Call to deprecated constant FILE_EXISTS_RENAME: Deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use Drupal\\Core\\File\\FileSystemInterface::EXISTS_RENAME.',
'Call to deprecated function entity_create(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use The method overriding Entity::create() for the entity type, e.g. \\Drupal\\node\\Entity\\Node::create() if the entity type is known. If the entity type is variable, use the entity storage\'s create() method to construct a new entity:',
'Call to deprecated constant DATETIME_STORAGE_TIMEZONE: Deprecated in drupal:8.5.0 and is removed from drupal:9.0.0. Use Drupal\\datetime\\Plugin\\Field\\FieldType\\DateTimeItemInterface::STORAGE_TIMEZONE instead.',
'Call to deprecated constant DATETIME_DATETIME_STORAGE_FORMAT: Deprecated in drupal:8.5.0 and is removed from drupal:9.0.0. Use Drupal\\datetime\\Plugin\\Field\\FieldType\\DateTimeItemInterface::DATETIME_STORAGE_FORMAT instead.',
'Call to deprecated constant DATETIME_DATE_STORAGE_FORMAT: Deprecated in drupal:8.5.0 and is removed from drupal:9.0.0. Use Drupal\\datetime\\Plugin\\Field\\FieldType\\DateTimeItemInterface::DATE_STORAGE_FORMAT instead.',
'Call to deprecated method getLowercaseLabel() of class Drupal\\Core\\Entity\\EntityTypeInterface. Deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, you should call getSingularLabel(). See https://www.drupal.org/node/3075567',
'Call to deprecated function entity_delete_multiple(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use the entity storage\'s \\Drupal\\Core\\Entity\\EntityStorageInterface::delete() method to delete multiple entities:',
'Call to deprecated function entity_view(). Deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use the entity view builder\'s view() method for creating a render array:',
'Call to deprecated method drupalPostForm() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use $this->submitForm() instead.',
'Call to deprecated method assertText() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use - $this->assertSession()->responseContains() for non-HTML responses, like XML or Json. - $this->assertSession()->pageTextContains() for HTML responses. Unlike the deprecated assertText(), the passed text should be HTML decoded, exactly as a human sees it in the browser.',
'Call to deprecated method assertEqual() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. Use $this->assertEquals() instead.',
'Call to deprecated method assertEqual() of class Drupal\\KernelTests\\KernelTestBase. Deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. Use $this->assertEquals() instead.',
'Call to deprecated method assertIdentical() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. Use $this->assertSame() instead.',
'Call to deprecated method assertIdentical() of class Drupal\\KernelTests\\KernelTestBase. Deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. Use $this->assertSame() instead.',
'Call to deprecated method assertResponse() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->statusCodeEquals() instead.',
'Call to deprecated method assertRaw() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->responseContains() instead.',
'Call to deprecated method assertFieldByName() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->fieldExists() or $this->assertSession()->buttonExists() or $this->assertSession()->fieldValueEquals() instead.',
'Call to deprecated method buildXPathQuery() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->buildXPathQuery() instead.',
'Call to deprecated method assertHeader() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.3.0 and is removed from drupal:10.0.0. Use $this->assertSession()->responseHeaderEquals() instead.',
'Call to deprecated method assertNoCacheTag() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.4.0 and is removed from drupal:10.0.0. Use $this->assertSession()->responseHeaderNotContains() instead.',
'Call to deprecated method assertCacheTag() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->responseHeaderContains() instead.',
'Call to deprecated method assertNoPattern() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.4.0 and is removed from drupal:10.0.0. Use $this->assertSession()->responseNotMatches() instead.',
'Call to deprecated method assertPattern() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->responseMatches() instead.',
'Call to deprecated method assertEscaped() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->assertEscaped() instead.',
'Call to deprecated method assertNoEscaped() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->assertNoEscaped() instead.',
'Call to deprecated method assertNotEqual() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. Use $this->assertNotEquals() instead.',
'Call to deprecated method assertNotEqual() of class Drupal\\KernelTests\\KernelTestBase. Deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. Use $this->assertNotEquals() instead.',
'Call to deprecated method assertNotIdentical() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. Use $this->assertNotSame() instead.',
'Call to deprecated method assertNotIdentical() of class Drupal\\KernelTests\\KernelTestBase. Deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. Use $this->assertNotSame() instead.',
'Call to deprecated method assertIdenticalObject() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. Use $this->assertEquals() instead.',
'Call to deprecated method assertIdenticalObject() of class Drupal\\KernelTests\\KernelTestBase. Deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. Use $this->assertEquals() instead.',
'Call to deprecated method assert() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. Use $this->assertTrue() instead.',
'Call to deprecated method assert() of class Drupal\\KernelTests\\KernelTestBase. Deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. Use $this->assertTrue() instead.',
'Call to deprecated method assertElementNotPresent() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->elementNotExists() instead.',
'Call to deprecated method assertElementPresent() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->elementExists() instead.',
'Call to deprecated method assertNoText() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use - $this->assertSession()->responseNotContains() for non-HTML responses, like XML or Json. - $this->assertSession()->pageTextNotContains() for HTML responses. Unlike the deprecated assertNoText(), the passed text should be HTML decoded, exactly as a human sees it in the browser.',
'Call to deprecated method assertNoRaw() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->responseNotContains() instead.',
'Call to deprecated method assertTitle() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->titleEquals() instead.',
'Call to deprecated method assertNoLink() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->linkNotExists() instead.',
'Call to deprecated method assertLink() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->linkExists() instead.',
'Call to deprecated method assertLinkByHref() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->linkByHrefExists() instead.',
'Call to deprecated method assertNoLinkByHref() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->linkByHrefNotExists() instead.',
'Call to deprecated method pass() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. PHPUnit interrupts a test as soon as a test assertion fails, so there is usually no need to call this method. If a test\'s logic relies on this method, refactor the test.',
'Call to deprecated method pass() of class Drupal\\KernelTests\\KernelTestBase. Deprecated in drupal:8.0.0 and is removed from drupal:10.0.0. PHPUnit interrupts a test as soon as a test assertion fails, so there is usually no need to call this method. If a test\'s logic relies on this method, refactor the test.',
'Call to deprecated method assertNoUniqueText() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Instead, use $this->getSession()->pageTextMatchesCount() if you know the cardinality in advance, or $this->getSession()->getPage()->getText() and substr_count().',
'Call to deprecated method assertUniqueText() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->getSession()->pageTextContainsOnce() or $this->getSession()->pageTextMatchesCount() instead.',
'Call to deprecated method assertNoFieldByName() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->fieldNotExists() or $this->assertSession()->buttonNotExists() or $this->assertSession()->fieldValueNotEquals() instead.',
'Call to deprecated method assertFieldChecked() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->checkboxChecked() instead.',
'Call to deprecated method assertNoFieldChecked() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->checkboxNotChecked() instead.',
'Call to deprecated method assertNoOption() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->optionNotExists() instead.',
'Call to deprecated method assertOptionByText() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.4.0 and is removed from drupal:10.0.0. Use $this->assertSession()->optionExists() instead.',
'Call to deprecated method assertOption() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->optionExists() instead.',
'Call to deprecated method assertUrl() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->addressEquals() instead.',
'Call to deprecated method constructFieldXpath() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.5.0 and is removed from drupal:10.0.0. Use $this->getSession()->getPage()->findField() instead.',
'Call to deprecated method getRawContent() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->getSession()->getPage()->getContent() instead.',
'Call to deprecated method assertFieldById() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->fieldExists() or $this->assertSession()->buttonExists() or $this->assertSession()->fieldValueEquals() instead.',
'Call to deprecated method assertField() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->fieldExists() or $this->assertSession()->buttonExists() instead.',
'Call to deprecated method assertNoFieldById() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->fieldNotExists() or $this->assertSession()->buttonNotExists() or $this->assertSession()->fieldValueNotEquals() instead.',
'Call to deprecated method assertNoField() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->fieldNotExists() or $this->assertSession()->buttonNotExists() instead.',
'Call to deprecated method assertOptionSelected() of class Drupal\\Tests\\BrowserTestBase. Deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->optionExists() instead and check the "selected" attribute yourself.',
];
return in_array($string, $rector_covered) || strpos($string, 'Call to deprecated method l() of class Drupal') === 0;
}
private function getSubExtensionInfoFiles(string $path) {
$files = [];
foreach (glob($path . '/*.info.yml') as $file) {
$parts = explode('.', basename($file));
if (count($parts) == 3) {
$files[] = $file;
}
}
foreach (glob($path . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
$files = array_merge($files, $this
->getSubExtensionInfoFiles($dir));
}
return $files;
}
}