PageRenderTime 37ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/core/modules/update/lib/Drupal/update/UpdateProcessor.php

https://bitbucket.org/aswinvk28/smartpan-stock-drupal
PHP | 272 lines | 130 code | 30 blank | 112 comment | 16 complexity | 31367e5588db729ad54342c1b480eebe MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. /**
  3. * @file
  4. * Contains \Drupal\update\UpdateProcessor.
  5. */
  6. namespace Drupal\update;
  7. use Drupal\Component\Utility\Crypt;
  8. use Drupal\Core\Config\ConfigFactoryInterface;
  9. use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
  10. use Drupal\Core\KeyValueStore\StateInterface;
  11. use Drupal\Core\PrivateKey;
  12. use Drupal\Core\Queue\QueueFactory;
  13. /**
  14. * Process project update information.
  15. */
  16. class UpdateProcessor implements UpdateProcessorInterface {
  17. /**
  18. * The update settings
  19. *
  20. * @var \Drupal\Core\Config\Config
  21. */
  22. protected $updateSettings;
  23. /**
  24. * The UpdateFetcher service.
  25. *
  26. * @var \Drupal\update\UpdateFetcherInterface
  27. */
  28. protected $updateFetcher;
  29. /**
  30. * The update fetch queue.
  31. *
  32. * @var \Drupal\Core\Queue\QueueInterface
  33. */
  34. protected $fetchQueue;
  35. /**
  36. * Update key/value store
  37. *
  38. * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
  39. */
  40. protected $tempStore;
  41. /**
  42. * Update Fetch Task Store
  43. *
  44. * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
  45. */
  46. protected $fetchTaskStore;
  47. /**
  48. * Update available releases store
  49. *
  50. * @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
  51. */
  52. protected $availableReleasesTempStore;
  53. /**
  54. * Array of release history URLs that we have failed to fetch
  55. *
  56. * @var array
  57. */
  58. protected $failed;
  59. /**
  60. * The state service.
  61. *
  62. * @var \Drupal\Core\KeyValueStore\StateInterface
  63. */
  64. protected $stateStore;
  65. /**
  66. * The private key.
  67. *
  68. * @var \Drupal\Core\PrivateKey
  69. */
  70. protected $privateKey;
  71. /**
  72. * Constructs a UpdateProcessor.
  73. *
  74. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
  75. * The config factory.
  76. * @param \Drupal\Core\Queue\QueueFactory $queue_factory
  77. * The queue factory
  78. * @param \Drupal\update\UpdateFetcherInterface $update_fetcher
  79. * The update fetcher service
  80. * @param \Drupal\Core\KeyValueStore\StateInterface $state_store
  81. * The state service.
  82. * @param \Drupal\Core\PrivateKey $private_key
  83. * The private key factory service.
  84. * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory
  85. * The key/value factory.
  86. * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_expirable_factory
  87. * The expirable key/value factory.
  88. */
  89. 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) {
  90. $this->updateFetcher = $update_fetcher;
  91. $this->updateSettings = $config_factory->get('update.settings');
  92. $this->fetchQueue = $queue_factory->get('update_fetch_tasks');
  93. $this->tempStore = $key_value_expirable_factory->get('update');
  94. $this->fetchTaskStore = $key_value_factory->get('update_fetch_task');
  95. $this->availableReleasesTempStore = $key_value_expirable_factory->get('update_available_releases');
  96. $this->stateStore = $state_store;
  97. $this->privateKey = $private_key;
  98. $this->fetchTasks = array();
  99. $this->failed = array();
  100. }
  101. /**
  102. * {@inheritdoc}
  103. */
  104. public function createFetchTask($project) {
  105. if (empty($this->fetchTasks)) {
  106. $this->fetchTasks = $this->fetchTaskStore->getAll();
  107. }
  108. if (empty($this->fetchTasks[$project['name']])) {
  109. $this->fetchQueue->createItem($project);
  110. $this->fetchTaskStore->set($project['name'], $project);
  111. $this->fetchTasks[$project['name']] = REQUEST_TIME;
  112. }
  113. }
  114. /**
  115. * {@inheritdoc}
  116. */
  117. public function fetchData() {
  118. $end = time() + $this->updateSettings->get('fetch.timeout');
  119. while (time() < $end && ($item = $this->fetchQueue->claimItem())) {
  120. $this->processFetchTask($item->data);
  121. $this->fetchQueue->deleteItem($item);
  122. }
  123. }
  124. /**
  125. * {@inheritdoc}
  126. */
  127. public function processFetchTask($project) {
  128. global $base_url;
  129. // This can be in the middle of a long-running batch, so REQUEST_TIME won't
  130. // necessarily be valid.
  131. $request_time_difference = time() - REQUEST_TIME;
  132. if (empty($this->failed)) {
  133. // If we have valid data about release history XML servers that we have
  134. // failed to fetch from on previous attempts, load that.
  135. $this->failed = $this->tempStore->get('fetch_failures');
  136. }
  137. $max_fetch_attempts = $this->updateSettings->get('fetch.max_attempts');
  138. $success = FALSE;
  139. $available = array();
  140. $site_key = Crypt::hmacBase64($base_url, $this->privateKey->get());
  141. $fetch_url_base = $this->updateFetcher->getFetchBaseUrl($project);
  142. $project_name = $project['name'];
  143. if (empty($this->failed[$fetch_url_base]) || $this->failed[$fetch_url_base] < $max_fetch_attempts) {
  144. $data = $this->updateFetcher->fetchProjectData($project, $site_key);
  145. }
  146. if (!empty($data)) {
  147. $available = $this->parseXml($data);
  148. // @todo: Purge release data we don't need (http://drupal.org/node/238950).
  149. if (!empty($available)) {
  150. // Only if we fetched and parsed something sane do we return success.
  151. $success = TRUE;
  152. }
  153. }
  154. else {
  155. $available['project_status'] = 'not-fetched';
  156. if (empty($this->failed[$fetch_url_base])) {
  157. $this->failed[$fetch_url_base] = 1;
  158. }
  159. else {
  160. $this->failed[$fetch_url_base]++;
  161. }
  162. }
  163. $frequency = $this->updateSettings->get('check.interval_days');
  164. $available['last_fetch'] = REQUEST_TIME + $request_time_difference;
  165. $this->availableReleasesTempStore->setWithExpire($project_name, $available, $request_time_difference + (60 * 60 * 24 * $frequency));
  166. // Stash the $this->failed data back in the DB for the next 5 minutes.
  167. $this->tempStore->setWithExpire('fetch_failures', $this->failed, $request_time_difference + (60 * 5));
  168. // Whether this worked or not, we did just (try to) check for updates.
  169. $this->stateStore->set('update.last_check', REQUEST_TIME + $request_time_difference);
  170. // Now that we processed the fetch task for this project, clear out the
  171. // record for this task so we're willing to fetch again.
  172. $this->fetchTaskStore->delete($project_name);
  173. return $success;
  174. }
  175. /**
  176. * Parses the XML of the Drupal release history info files.
  177. *
  178. * @param string $raw_xml
  179. * A raw XML string of available release data for a given project.
  180. *
  181. * @return array
  182. * Array of parsed data about releases for a given project, or NULL if there
  183. * was an error parsing the string.
  184. */
  185. protected function parseXml($raw_xml) {
  186. try {
  187. $xml = new \SimpleXMLElement($raw_xml);
  188. }
  189. catch (\Exception $e) {
  190. // SimpleXMLElement::__construct produces an E_WARNING error message for
  191. // each error found in the XML data and throws an exception if errors
  192. // were detected. Catch any exception and return failure (NULL).
  193. return NULL;
  194. }
  195. // If there is no valid project data, the XML is invalid, so return failure.
  196. if (!isset($xml->short_name)) {
  197. return NULL;
  198. }
  199. $data = array();
  200. foreach ($xml as $k => $v) {
  201. $data[$k] = (string) $v;
  202. }
  203. $data['releases'] = array();
  204. if (isset($xml->releases)) {
  205. foreach ($xml->releases->children() as $release) {
  206. $version = (string) $release->version;
  207. $data['releases'][$version] = array();
  208. foreach ($release->children() as $k => $v) {
  209. $data['releases'][$version][$k] = (string) $v;
  210. }
  211. $data['releases'][$version]['terms'] = array();
  212. if ($release->terms) {
  213. foreach ($release->terms->children() as $term) {
  214. if (!isset($data['releases'][$version]['terms'][(string) $term->name])) {
  215. $data['releases'][$version]['terms'][(string) $term->name] = array();
  216. }
  217. $data['releases'][$version]['terms'][(string) $term->name][] = (string) $term->value;
  218. }
  219. }
  220. }
  221. }
  222. return $data;
  223. }
  224. /**
  225. * {@inheritdoc}
  226. */
  227. public function numberOfQueueItems() {
  228. return $this->fetchQueue->numberOfItems();
  229. }
  230. /**
  231. * {@inheritdoc}
  232. */
  233. public function claimQueueItem() {
  234. return $this->fetchQueue->claimItem();
  235. }
  236. /**
  237. * {@inheritdoc}
  238. */
  239. public function deleteQueueItem($item) {
  240. return $this->fetchQueue->deleteItem($item);
  241. }
  242. }