Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
273
core/modules/update/src/UpdateProcessor.php
Normal file
273
core/modules/update/src/UpdateProcessor.php
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\update\UpdateProcessor.
|
||||
*/
|
||||
|
||||
namespace Drupal\update;
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
use Drupal\Core\PrivateKey;
|
||||
use Drupal\Core\Queue\QueueFactory;
|
||||
|
||||
/**
|
||||
* Process project update information.
|
||||
*/
|
||||
class UpdateProcessor implements UpdateProcessorInterface {
|
||||
|
||||
/**
|
||||
* The update settings
|
||||
*
|
||||
* @var \Drupal\Core\Config\Config
|
||||
*/
|
||||
protected $updateSettings;
|
||||
|
||||
/**
|
||||
* The UpdateFetcher service.
|
||||
*
|
||||
* @var \Drupal\update\UpdateFetcherInterface
|
||||
*/
|
||||
protected $updateFetcher;
|
||||
|
||||
/**
|
||||
* The update fetch queue.
|
||||
*
|
||||
* @var \Drupal\Core\Queue\QueueInterface
|
||||
*/
|
||||
protected $fetchQueue;
|
||||
|
||||
/**
|
||||
* Update key/value store
|
||||
*
|
||||
* @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
|
||||
*/
|
||||
protected $tempStore;
|
||||
|
||||
/**
|
||||
* Update Fetch Task Store
|
||||
*
|
||||
* @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
|
||||
*/
|
||||
protected $fetchTaskStore;
|
||||
|
||||
/**
|
||||
* Update available releases store
|
||||
*
|
||||
* @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
|
||||
*/
|
||||
protected $availableReleasesTempStore;
|
||||
|
||||
/**
|
||||
* Array of release history URLs that we have failed to fetch
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $failed;
|
||||
|
||||
/**
|
||||
* The state service.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $stateStore;
|
||||
|
||||
/**
|
||||
* The private key.
|
||||
*
|
||||
* @var \Drupal\Core\PrivateKey
|
||||
*/
|
||||
protected $privateKey;
|
||||
|
||||
/**
|
||||
* Constructs a UpdateProcessor.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
* @param \Drupal\Core\Queue\QueueFactory $queue_factory
|
||||
* The queue factory
|
||||
* @param \Drupal\update\UpdateFetcherInterface $update_fetcher
|
||||
* The update fetcher service
|
||||
* @param \Drupal\Core\State\StateInterface $state_store
|
||||
* The state service.
|
||||
* @param \Drupal\Core\PrivateKey $private_key
|
||||
* The private key factory service.
|
||||
* @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory
|
||||
* The key/value factory.
|
||||
* @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_expirable_factory
|
||||
* The expirable key/value factory.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, QueueFactory $queue_factory, UpdateFetcherInterface $update_fetcher, StateInterface $state_store, PrivateKey $private_key, KeyValueFactoryInterface $key_value_factory, KeyValueFactoryInterface $key_value_expirable_factory) {
|
||||
$this->updateFetcher = $update_fetcher;
|
||||
$this->updateSettings = $config_factory->get('update.settings');
|
||||
$this->fetchQueue = $queue_factory->get('update_fetch_tasks');
|
||||
$this->tempStore = $key_value_expirable_factory->get('update');
|
||||
$this->fetchTaskStore = $key_value_factory->get('update_fetch_task');
|
||||
$this->availableReleasesTempStore = $key_value_expirable_factory->get('update_available_releases');
|
||||
$this->stateStore = $state_store;
|
||||
$this->privateKey = $private_key;
|
||||
$this->fetchTasks = array();
|
||||
$this->failed = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createFetchTask($project) {
|
||||
if (empty($this->fetchTasks)) {
|
||||
$this->fetchTasks = $this->fetchTaskStore->getAll();
|
||||
}
|
||||
if (empty($this->fetchTasks[$project['name']])) {
|
||||
$this->fetchQueue->createItem($project);
|
||||
$this->fetchTaskStore->set($project['name'], $project);
|
||||
$this->fetchTasks[$project['name']] = REQUEST_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchData() {
|
||||
$end = time() + $this->updateSettings->get('fetch.timeout');
|
||||
while (time() < $end && ($item = $this->fetchQueue->claimItem())) {
|
||||
$this->processFetchTask($item->data);
|
||||
$this->fetchQueue->deleteItem($item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processFetchTask($project) {
|
||||
global $base_url;
|
||||
|
||||
// This can be in the middle of a long-running batch, so REQUEST_TIME won't
|
||||
// necessarily be valid.
|
||||
$request_time_difference = time() - REQUEST_TIME;
|
||||
if (empty($this->failed)) {
|
||||
// If we have valid data about release history XML servers that we have
|
||||
// failed to fetch from on previous attempts, load that.
|
||||
$this->failed = $this->tempStore->get('fetch_failures');
|
||||
}
|
||||
|
||||
$max_fetch_attempts = $this->updateSettings->get('fetch.max_attempts');
|
||||
|
||||
$success = FALSE;
|
||||
$available = array();
|
||||
$site_key = Crypt::hmacBase64($base_url, $this->privateKey->get());
|
||||
$fetch_url_base = $this->updateFetcher->getFetchBaseUrl($project);
|
||||
$project_name = $project['name'];
|
||||
|
||||
if (empty($this->failed[$fetch_url_base]) || $this->failed[$fetch_url_base] < $max_fetch_attempts) {
|
||||
$data = $this->updateFetcher->fetchProjectData($project, $site_key);
|
||||
}
|
||||
if (!empty($data)) {
|
||||
$available = $this->parseXml($data);
|
||||
// @todo: Purge release data we don't need. See
|
||||
// https://www.drupal.org/node/238950.
|
||||
if (!empty($available)) {
|
||||
// Only if we fetched and parsed something sane do we return success.
|
||||
$success = TRUE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$available['project_status'] = 'not-fetched';
|
||||
if (empty($this->failed[$fetch_url_base])) {
|
||||
$this->failed[$fetch_url_base] = 1;
|
||||
}
|
||||
else {
|
||||
$this->failed[$fetch_url_base]++;
|
||||
}
|
||||
}
|
||||
|
||||
$frequency = $this->updateSettings->get('check.interval_days');
|
||||
$available['last_fetch'] = REQUEST_TIME + $request_time_difference;
|
||||
$this->availableReleasesTempStore->setWithExpire($project_name, $available, $request_time_difference + (60 * 60 * 24 * $frequency));
|
||||
|
||||
// Stash the $this->failed data back in the DB for the next 5 minutes.
|
||||
$this->tempStore->setWithExpire('fetch_failures', $this->failed, $request_time_difference + (60 * 5));
|
||||
|
||||
// Whether this worked or not, we did just (try to) check for updates.
|
||||
$this->stateStore->set('update.last_check', REQUEST_TIME + $request_time_difference);
|
||||
|
||||
// Now that we processed the fetch task for this project, clear out the
|
||||
// record for this task so we're willing to fetch again.
|
||||
$this->fetchTaskStore->delete($project_name);
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the XML of the Drupal release history info files.
|
||||
*
|
||||
* @param string $raw_xml
|
||||
* A raw XML string of available release data for a given project.
|
||||
*
|
||||
* @return array
|
||||
* Array of parsed data about releases for a given project, or NULL if there
|
||||
* was an error parsing the string.
|
||||
*/
|
||||
protected function parseXml($raw_xml) {
|
||||
try {
|
||||
$xml = new \SimpleXMLElement($raw_xml);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// SimpleXMLElement::__construct produces an E_WARNING error message for
|
||||
// each error found in the XML data and throws an exception if errors
|
||||
// were detected. Catch any exception and return failure (NULL).
|
||||
return NULL;
|
||||
}
|
||||
// If there is no valid project data, the XML is invalid, so return failure.
|
||||
if (!isset($xml->short_name)) {
|
||||
return NULL;
|
||||
}
|
||||
$data = array();
|
||||
foreach ($xml as $k => $v) {
|
||||
$data[$k] = (string) $v;
|
||||
}
|
||||
$data['releases'] = array();
|
||||
if (isset($xml->releases)) {
|
||||
foreach ($xml->releases->children() as $release) {
|
||||
$version = (string) $release->version;
|
||||
$data['releases'][$version] = array();
|
||||
foreach ($release->children() as $k => $v) {
|
||||
$data['releases'][$version][$k] = (string) $v;
|
||||
}
|
||||
$data['releases'][$version]['terms'] = array();
|
||||
if ($release->terms) {
|
||||
foreach ($release->terms->children() as $term) {
|
||||
if (!isset($data['releases'][$version]['terms'][(string) $term->name])) {
|
||||
$data['releases'][$version]['terms'][(string) $term->name] = array();
|
||||
}
|
||||
$data['releases'][$version]['terms'][(string) $term->name][] = (string) $term->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function numberOfQueueItems() {
|
||||
return $this->fetchQueue->numberOfItems();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function claimQueueItem() {
|
||||
return $this->fetchQueue->claimItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteQueueItem($item) {
|
||||
return $this->fetchQueue->deleteItem($item);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in a new issue