deprecation_status-8.x-3.x-dev/src/DataSource.php
src/DataSource.php
<?php
namespace Drupal\deprecation_status;
use Drupal\Core\File\FileSystemInterface;
use GuzzleHttp\Exception\RequestException;
class DataSource {
/**
* List of file names for all dataset files.
*
* @type array
*/
public static $datafiles = [
'core_deprecation_count.csv',
'error_counts_200.csv',
'error_counts.csv',
'error_types_200.csv',
'error_types.csv',
'errors_detail.csv',
'metadata.csv',
'next_steps_general_200.csv',
'next_steps_general_segments.csv',
'next_steps_general.csv',
'next_steps_help.csv',
'next_steps_specific_200.csv',
'next_steps_specific_segments.csv',
'next_steps_specific.csv',
'projects_detail.csv',
];
/**
* Get full path of data file based on filename.
*
* @param string $filename
* Filename. For example 'metadata.csv'.
*
* @return string
* Full path to $filename.
*/
public static function getFullPath($filename) {
// Attempt to use an up to date copy of the data from locally saved files.
/* @var \Drupal\file\FileInterface[] $files */
$files = \Drupal::entityTypeManager()
->getStorage('file')
->loadByProperties(['uri' => 'private://deprecation_status_' . $filename]);
if (count($files)) {
$file = array_shift($files);
return $file->getFileUri();
}
// If that fails, we still have shipped files locally.
else {
return \Drupal::service('extension.list.module')->getPath('deprecation_status') . '/data/' . $filename;
}
}
/**
* Get metadata about current dataset.
*
* @return array
* Two element indexed array containing the dataset ID and the dataset date.
*/
public static function getMetaData() {
$metadata = file_get_contents(static::getFullPath('metadata.csv'));
return explode(';', trim($metadata));
}
/**
* Get metadata about shipped dataset.
*
* As opposed to getMetaData() this will always use the local shipped file.
*
* @return array
* Two element indexed array containing the dataset ID and the dataset date.
*/
public static function getShippedMetaData() {
$path = \Drupal::service('extension.list.module')->getPath('deprecation_status') . '/data/metadata.csv';
$metadata = file_get_contents($path);
return explode(';', trim($metadata));
}
/**
* Download and update all local dataset files.
*
* @return string|boolean
* TRUE if all successful. Error string otherwise.
*/
public static function updateDataFiles() {
$datastore = [];
$client = \Drupal::httpClient();
try {
// Try downloading each dataset file. Store them in memory as a protection
// against half updates. Only save locally once all files downloaded
// successfully.
foreach(static::$datafiles as $filename) {
$response = $client->get('https://git.drupalcode.org/project/deprecation_status/raw/8.x-5.x/data/' . $filename . '?' . time());
$data = (string) $response->getBody();
if (!empty($data)) {
$datastore[$filename] = $data;
}
else {
return 'Missing data in ' . $filename . '.';
}
}
}
catch (RequestException $e) {
return 'HTTP error while downloading online dataset.';
}
// Save updated files locally as private files.
foreach($datastore as $filename => $data) {
if (!\Drupal::service('file.repository')->writeData($data, 'private://deprecation_status_' . $filename, FileSystemInterface::EXISTS_REPLACE)) {
return 'Error while saving updated ' . $filename . ' locally.';
}
}
return TRUE;
}
/**
* Reset all local dataset files to local copies.
*
* @return string|boolean
* TRUE if all successful. Error string otherwise.
*/
public static function resetDataFiles() {
$count = 0;
foreach(static::$datafiles as $filename) {
/* @var \Drupal\file\FileInterface[] $files */
$files = \Drupal::entityTypeManager()
->getStorage('file')
->loadByProperties(['uri' => 'private://deprecation_status_' . $filename]);
foreach($files as $file) {
$file->delete();
$count++;
}
}
return $count;
}
/**
* Format an error message for display.
*
* @param string $error
* String error message
*
* @return string
* The HTML formatted error string.
*/
public static function formatError($error) {
// Link deprecated function names assuming they are core functions.
$api_version = '9.4.x';
$api_link = 'https://api.drupal.org/api/drupal/' . $api_version . '/search/';
$formatted_error = preg_replace('!deprecated function ([^(]+)\(\)!', 'deprecated function <a target="_blank" href="' . $api_link . '\1">\1()</a>', $error);
// Replace deprecated class links.
if (preg_match_all('!([cC]lass|trait|[uU]se) (Drupal\\\\[a-z_0-9A-Z\\\\]+)( |\.|$|:)!', $formatted_error, $found)) {
foreach($found[2] as $found_item) {
if (preg_match('!Drupal\\\\([a-z_0-9A-Z]+)\\\\(.+)$!', $found_item, $namespace)) {
$path_parts = explode('\\', $namespace[2]);
$class = array_pop($path_parts);
if (in_array($namespace[1], ['Component', 'Core'])) {
$class_file = 'core!lib!Drupal!' . $namespace[1];
}
elseif (in_array($namespace[1], ['KernelTests', 'FunctionalTests', 'FunctionalJavascriptTests'])) {
$class_file = 'core!tests!Drupal!' . $namespace[1];
}
elseif ($namespace[1] == 'Tests') {
// If the test was in a module, use that path.
if (preg_match('!^([a-z0-9_]+)\\\\!', $namespace[2], $module)) {
// Remove module name from array.
array_shift($path_parts);
$class_file = 'core!modules!' . $module[1] . '!tests!src';
}
else {
// If the test was global, use that.
$class_file = 'core!tests!Drupal!' . $namespace[1];
}
}
else {
$class_file = 'core!modules!' . $namespace[1] . '!src';
}
if (count($path_parts)) {
$class_file .= '!' . join('!', $path_parts);
}
$class_file .= '!' . $class . '.php';
$type = 'class';
if (strpos($class_file, 'Interface')) {
$type = 'interface';
}
elseif (strpos($class_file, 'Trait')) {
$type = 'trait';
}
$api_link = 'https://api.drupal.org/api/drupal/' . $class_file . '/' . $type . '/' . $class . '/' . $api_version;
$formatted_error = str_replace($found_item, '<a target="_blank" href="' . $api_link . '">' . $found_item . '</a>', $formatted_error);
}
}
}
// Convert remaining links.
$formatted_error = preg_replace('!(https://(www\.)?drupal\.org/[a-zA-Z/0-9]+)(\.|,| |$)!', '<a target="_blank" href="\1">\1</a>\3', $formatted_error);
// Allow error messages to wrap.
$formatted_error = str_replace('\\', '​\\​', $formatted_error);
return $formatted_error;
}
/**
* Returns render structure for dataset/date.
*
* @return array
* Render array structure with dataset date information.
*/
public static function getDataInfo() {
list($data_id, $data_date) = self::getMetaData();
return [
'#type' => 'markup',
'#markup' => '<p align="center"><br/>Results are from dataset #' . $data_id . ' from ' . $data_date . '. Report is ran by the Drupal Association regularly.</p>',
'#weight' => 2000,
];
}
}
