View source
<?php
namespace Drupal\automatic_updates\Services;
use Composer\Semver\VersionParser;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Extension\ExtensionList;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\TransferException;
use Psr\Log\LoggerInterface;
class AutomaticUpdatesPsa implements AutomaticUpdatesPsaInterface {
use StringTranslationTrait;
use DependencySerializationTrait;
protected $config;
protected $httpClient;
protected $cache;
protected $time;
protected $module;
protected $profile;
protected $theme;
protected $logger;
public function __construct(ConfigFactoryInterface $config_factory, CacheBackendInterface $cache, TimeInterface $time, Client $client, ExtensionList $module, ExtensionList $profile, ExtensionList $theme, LoggerInterface $logger) {
$this->config = $config_factory
->get('automatic_updates.settings');
$this->cache = $cache;
$this->time = $time;
$this->httpClient = $client;
$this->module = $module;
$this->profile = $profile;
$this->theme = $theme;
$this->logger = $logger;
}
public function getPublicServiceMessages() {
$messages = [];
if (!$this->config
->get('enable_psa')) {
return $messages;
}
if ($cache = $this->cache
->get('automatic_updates_psa')) {
$response = $cache->data;
}
else {
$psa_endpoint = $this->config
->get('psa_endpoint');
try {
$response = $this->httpClient
->get($psa_endpoint)
->getBody()
->getContents();
$this->cache
->set('automatic_updates_psa', $response, $this->time
->getCurrentTime() + $this->config
->get('check_frequency'));
} catch (TransferException $exception) {
$this->logger
->error($exception
->getMessage());
return [
$this
->t('Drupal PSA endpoint :url is unreachable.', [
':url' => $psa_endpoint,
]),
];
}
}
try {
$json_payload = json_decode($response, FALSE);
if ($json_payload !== NULL) {
foreach ($json_payload as $json) {
if ($json->is_psa && ($json->type === 'core' || $this
->isValidExtension($json->type, $json->project))) {
$messages[] = $this
->message($json->title, $json->link);
}
elseif ($json->type === 'core') {
$this
->parseConstraints($messages, $json, \Drupal::VERSION);
}
elseif ($this
->isValidExtension($json->type, $json->project)) {
$this
->contribParser($messages, $json);
}
}
}
else {
$this->logger
->error('Drupal PSA JSON is malformed: @response', [
'@response' => $response,
]);
$messages[] = $this
->t('Drupal PSA JSON is malformed.');
}
} catch (\UnexpectedValueException $exception) {
$this->logger
->error($exception
->getMessage());
$messages[] = $this
->t('Drupal PSA endpoint service is malformed.');
}
return $messages;
}
protected function isValidExtension($extension_type, $project_name) {
if (!property_exists($this, $extension_type)) {
$this->logger
->error('Extension list of type "%extension" does not exist.', [
'%extension' => $extension_type,
]);
return FALSE;
}
return $this->{$extension_type}
->exists($project_name) && !empty($this->{$extension_type}
->getAllAvailableInfo()[$project_name]['version']);
}
protected function contribParser(array &$messages, $json) {
$extension_version = $this->{$json->type}
->getAllAvailableInfo()[$json->project]['version'];
$json->insecure = array_filter(array_map(static function ($version) {
$version_array = explode('-', $version, 2);
if ($version_array && $version_array[0] === \Drupal::CORE_COMPATIBILITY) {
return isset($version_array[1]) ? $version_array[1] : NULL;
}
if (count($version_array) === 1) {
return $version_array[0];
}
if (count($version_array) === 2 && $version_array[1] === 'dev') {
return $version;
}
}, $json->insecure));
$version_array = explode('-', $extension_version, 2);
$extension_version = isset($version_array[1]) && $version_array[1] !== 'dev' ? $version_array[1] : $extension_version;
$this
->parseConstraints($messages, $json, $extension_version);
}
protected function parseConstraints(array &$messages, $json, $current_version) {
$version_string = implode('||', $json->insecure);
if (empty($version_string)) {
return;
}
$parser = new VersionParser();
$psa_constraint = $parser
->parseConstraints($version_string);
$contrib_constraint = $parser
->parseConstraints($current_version);
if ($psa_constraint
->matches($contrib_constraint)) {
$messages[] = $this
->message($json->title, $json->link);
}
}
protected function message($title, $link) {
return new FormattableMarkup('<a href=":url">:message</a>', [
':message' => $title,
':url' => $link,
]);
}
}