update.inc

  1. 8.0.x commands/core/drupal/update.inc
  2. 6.x commands/core/drupal/update.inc
  3. 7.x commands/core/drupal/update.inc
  4. 4.x commands/core/drupal/update.inc
  5. 5.x commands/core/drupal/update.inc
  6. master commands/core/drupal/update.inc

Update.php for provisioned sites. This file is a derivative of the standard drupal update.php, which has been modified to allow being run from the command line.

Functions

Namesort descending Description
drush_get_update_list
drush_update_batch Start the database update batch process.
drush_update_do_one Perform one update and store the results which will later be displayed on the finished page.
drush_update_entity_definitions Apply entity schema updates.
drush_update_finished Process and display any returned update output.
entity_updates_main Apply pending entity schema updates.
updatedb_status Return a 2 item array with
update_main
_update_batch_command

File

commands/core/drupal/update.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Update.php for provisioned sites.
  5. * This file is a derivative of the standard drupal update.php,
  6. * which has been modified to allow being run from the command
  7. * line.
  8. */
  9. use Drush\Log\LogLevel;
  10. /**
  11. * Drupal's update.inc has functions that are in previous update_X.inc files
  12. * for example, update_check_incompatibility() which can prove useful when
  13. * enabling modules.
  14. */
  15. require_once DRUSH_DRUPAL_CORE . '/includes/update.inc';
  16. use Drupal\Core\Utility\Error;
  17. use Drupal\Core\Entity\EntityStorageException;
  18. /**
  19. * Perform one update and store the results which will later be displayed on
  20. * the finished page.
  21. *
  22. * An update function can force the current and all later updates for this
  23. * module to abort by returning a $ret array with an element like:
  24. * $ret['#abort'] = array('success' => FALSE, 'query' => 'What went wrong');
  25. * The schema version will not be updated in this case, and all the
  26. * aborted updates will continue to appear on update.php as updates that
  27. * have not yet been run.
  28. *
  29. * @param $module
  30. * The module whose update will be run.
  31. * @param $number
  32. * The update number to run.
  33. * @param $context
  34. * The batch context array
  35. */
  36. function drush_update_do_one($module, $number, $dependency_map, &$context) {
  37. $function = $module . '_update_' . $number;
  38. // If this update was aborted in a previous step, or has a dependency that
  39. // was aborted in a previous step, go no further.
  40. if (!empty($context['results']['#abort']) && array_intersect($context['results']['#abort'], array_merge($dependency_map, array($function)))) {
  41. return;
  42. }
  43. $context['log'] = FALSE;
  44. \Drupal::moduleHandler()->loadInclude($module, 'install');
  45. $ret = array();
  46. if (function_exists($function)) {
  47. try {
  48. if ($context['log']) {
  49. Database::startLog($function);
  50. }
  51. drush_log("Executing " . $function);
  52. $ret['results']['query'] = $function($context['sandbox']);
  53. $ret['results']['success'] = TRUE;
  54. }
  55. // @TODO We may want to do different error handling for different exception
  56. // types, but for now we'll just print the message.
  57. catch (Exception $e) {
  58. $ret['#abort'] = array('success' => FALSE, 'query' => $e->getMessage());
  59. drush_set_error('DRUPAL_EXCEPTION', $e->getMessage());
  60. }
  61. if ($context['log']) {
  62. $ret['queries'] = Database::getLog($function);
  63. }
  64. }
  65. else {
  66. $ret['#abort'] = array('success' => FALSE);
  67. drush_set_error('DRUSH_UPDATE_FUNCTION_NOT_FOUND', dt('Update function @function not found', array('@function' => $function)));
  68. }
  69. if (isset($context['sandbox']['#finished'])) {
  70. $context['finished'] = $context['sandbox']['#finished'];
  71. unset($context['sandbox']['#finished']);
  72. }
  73. if (!isset($context['results'][$module])) {
  74. $context['results'][$module] = array();
  75. }
  76. if (!isset($context['results'][$module][$number])) {
  77. $context['results'][$module][$number] = array();
  78. }
  79. $context['results'][$module][$number] = array_merge($context['results'][$module][$number], $ret);
  80. if (!empty($ret['#abort'])) {
  81. // Record this function in the list of updates that were aborted.
  82. $context['results']['#abort'][] = $function;
  83. }
  84. // Record the schema update if it was completed successfully.
  85. if ($context['finished'] == 1 && empty($ret['#abort'])) {
  86. drupal_set_installed_schema_version($module, $number);
  87. }
  88. $context['message'] = 'Performing ' . $function;
  89. }
  90. function update_main() {
  91. // In D8, we expect to be in full bootstrap.
  92. drush_bootstrap_to_phase(DRUSH_BOOTSTRAP_DRUPAL_FULL);
  93. require_once DRUPAL_ROOT . '/core/includes/install.inc';
  94. require_once DRUPAL_ROOT . '/core/includes/update.inc';
  95. drupal_load_updates();
  96. update_fix_compatibility();
  97. // Pending hook_update_N() implementations.
  98. $pending = update_get_update_list();
  99. // Pending hook_post_update_X() implementations.
  100. $post_updates = \Drupal::service('update.post_update_registry')->getPendingUpdateInformation();
  101. $start = array();
  102. $change_summary = [];
  103. if (drush_get_option('entity-updates', FALSE)) {
  104. $change_summary = \Drupal::entityDefinitionUpdateManager()->getChangeSummary();
  105. }
  106. // Print a list of pending updates for this module and get confirmation.
  107. if (count($pending) || $change_summary || count($post_updates)) {
  108. drush_print(dt('The following updates are pending:'));
  109. drush_print();
  110. foreach ($change_summary as $entity_type_id => $changes) {
  111. drush_print($entity_type_id . ' entity type : ');
  112. foreach ($changes as $change) {
  113. drush_print(strip_tags($change), 2);
  114. }
  115. }
  116. foreach (array('update', 'post_update') as $update_type) {
  117. $updates = $update_type == 'update' ? $pending : $post_updates;
  118. foreach ($updates as $module => $updates) {
  119. if (isset($updates['start'])) {
  120. drush_print($module . ' module : ');
  121. if (!empty($updates['pending'])) {
  122. $start += [$module => array()];
  123. $start[$module] = array_merge($start[$module], $updates['pending']);
  124. foreach ($updates['pending'] as $update) {
  125. drush_print(strip_tags($update), 2);
  126. }
  127. }
  128. drush_print();
  129. }
  130. }
  131. }
  132. if (!drush_confirm(dt('Do you wish to run all pending updates?'))) {
  133. return drush_user_abort();
  134. }
  135. drush_update_batch($start);
  136. }
  137. else {
  138. drush_log(dt("No database updates required"), LogLevel::SUCCESS);
  139. }
  140. }
  141. function _update_batch_command($id) {
  142. // In D8, we expect to be in full bootstrap.
  143. drush_bootstrap_to_phase(DRUSH_BOOTSTRAP_DRUPAL_FULL);
  144. drush_batch_command($id);
  145. }
  146. /**
  147. * Start the database update batch process.
  148. */
  149. function drush_update_batch() {
  150. $start = drush_get_update_list();
  151. // Resolve any update dependencies to determine the actual updates that will
  152. // be run and the order they will be run in.
  153. $updates = update_resolve_dependencies($start);
  154. // Store the dependencies for each update function in an array which the
  155. // batch API can pass in to the batch operation each time it is called. (We
  156. // do not store the entire update dependency array here because it is
  157. // potentially very large.)
  158. $dependency_map = array();
  159. foreach ($updates as $function => $update) {
  160. $dependency_map[$function] = !empty($update['reverse_paths']) ? array_keys($update['reverse_paths']) : array();
  161. }
  162. $operations = array();
  163. foreach ($updates as $update) {
  164. if ($update['allowed']) {
  165. // Set the installed version of each module so updates will start at the
  166. // correct place. (The updates are already sorted, so we can simply base
  167. // this on the first one we come across in the above foreach loop.)
  168. if (isset($start[$update['module']])) {
  169. drupal_set_installed_schema_version($update['module'], $update['number'] - 1);
  170. unset($start[$update['module']]);
  171. }
  172. // Add this update function to the batch.
  173. $function = $update['module'] . '_update_' . $update['number'];
  174. $operations[] = array('drush_update_do_one', array($update['module'], $update['number'], $dependency_map[$function]));
  175. }
  176. }
  177. // Apply post update hooks.
  178. $post_updates = \Drupal::service('update.post_update_registry')->getPendingUpdateFunctions();
  179. if ($post_updates) {
  180. $operations[] = ['drush_drupal_cache_clear_all', []];
  181. foreach ($post_updates as $function) {
  182. $operations[] = ['update_invoke_post_update', [$function]];
  183. }
  184. }
  185. // Lastly, perform entity definition updates, which will update storage
  186. // schema if needed. If module update functions need to work with specific
  187. // entity schema they should call the entity update service for the specific
  188. // update themselves.
  189. // @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface::applyEntityUpdate()
  190. // @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface::applyFieldUpdate()
  191. if (drush_get_option('entity-updates', FALSE) && \Drupal::entityDefinitionUpdateManager()->needsUpdates()) {
  192. $operations[] = array('drush_update_entity_definitions', array());
  193. }
  194. $batch['operations'] = $operations;
  195. $batch += array(
  196. 'title' => 'Updating',
  197. 'init_message' => 'Starting updates',
  198. 'error_message' => 'An unrecoverable error has occurred. You can find the error message below. It is advised to copy it to the clipboard for reference.',
  199. 'finished' => 'drush_update_finished',
  200. 'file' => 'includes/update.inc',
  201. );
  202. batch_set($batch);
  203. \Drupal::service('state')->set('system.maintenance_mode', TRUE);
  204. drush_backend_batch_process('updatedb-batch-process');
  205. \Drupal::service('state')->set('system.maintenance_mode', FALSE);
  206. }
  207. /**
  208. * Apply entity schema updates.
  209. */
  210. function drush_update_entity_definitions(&$context) {
  211. try {
  212. \Drupal::entityDefinitionUpdateManager()->applyUpdates();
  213. }
  214. catch (EntityStorageException $e) {
  215. watchdog_exception('update', $e);
  216. $variables = Error::decodeException($e);
  217. unset($variables['backtrace']);
  218. // The exception message is run through
  219. // \Drupal\Component\Utility\SafeMarkup::checkPlain() by
  220. // \Drupal\Core\Utility\Error::decodeException().
  221. $ret['#abort'] = array('success' => FALSE, 'query' => t('%type: !message in %function (line %line of %file).', $variables));
  222. $context['results']['core']['update_entity_definitions'] = $ret;
  223. $context['results']['#abort'][] = 'update_entity_definitions';
  224. }
  225. }
  226. // Copy of protected \Drupal\system\Controller\DbUpdateController::getModuleUpdates.
  227. function drush_get_update_list() {
  228. $return = array();
  229. $updates = update_get_update_list();
  230. foreach ($updates as $module => $update) {
  231. $return[$module] = $update['start'];
  232. }
  233. return $return;
  234. }
  235. /**
  236. * Process and display any returned update output.
  237. *
  238. * @see \Drupal\system\Controller\DbUpdateController::batchFinished()
  239. * @see \Drupal\system\Controller\DbUpdateController::results()
  240. */
  241. function drush_update_finished($success, $results, $operations) {
  242. if (!drush_get_option('cache-clear', TRUE)) {
  243. drush_log(dt("Skipping cache-clear operation due to --cache-clear=0 option."), LogLevel::WARNING);
  244. }
  245. else {
  246. drupal_flush_all_caches();
  247. }
  248. foreach ($results as $module => $updates) {
  249. if ($module != '#abort') {
  250. foreach ($updates as $number => $queries) {
  251. foreach ($queries as $query) {
  252. // If there is no message for this update, don't show anything.
  253. if (empty($query['query'])) {
  254. continue;
  255. }
  256. if ($query['success']) {
  257. drush_log(strip_tags($query['query']));
  258. }
  259. else {
  260. drush_set_error(dt('Failed: ') . strip_tags($query['query']));
  261. }
  262. }
  263. }
  264. }
  265. }
  266. }
  267. /**
  268. * Return a 2 item array with
  269. * - an array where each item is a 3 item associative array describing a pending update.
  270. * - an array listing the first update to run, keyed by module.
  271. */
  272. function updatedb_status() {
  273. $pending = update_get_update_list();
  274. $return = array();
  275. // Ensure system module's updates run first.
  276. $start['system'] = array();
  277. foreach (\Drupal::entityDefinitionUpdateManager()->getChangeSummary() as $entity_type_id => $changes) {
  278. foreach ($changes as $change) {
  279. $return[] = array(
  280. 'module' => dt('@type entity type', array('@type' => $entity_type_id)), 'update_id' => '', 'description' => strip_tags($change));
  281. }
  282. }
  283. // Print a list of pending updates for this module and get confirmation.
  284. foreach ($pending as $module => $updates) {
  285. if (isset($updates['start'])) {
  286. foreach ($updates['pending'] as $update_id => $description) {
  287. // Strip cruft from front.
  288. $description = str_replace($update_id . ' - ', '', $description);
  289. $return[] = array('module' => ucfirst($module), 'update_id' => $update_id, 'description' => $description);
  290. }
  291. if (isset($updates['start'])) {
  292. $start[$module] = $updates['start'];
  293. }
  294. }
  295. }
  296. return array($return, $start);
  297. }
  298. /**
  299. * Apply pending entity schema updates.
  300. */
  301. function entity_updates_main() {
  302. $change_summary = \Drupal::entityDefinitionUpdateManager()->getChangeSummary();
  303. if (!empty($change_summary)) {
  304. drush_print(dt('The following updates are pending:'));
  305. drush_print();
  306. foreach ($change_summary as $entity_type_id => $changes) {
  307. drush_print($entity_type_id . ' entity type : ');
  308. foreach ($changes as $change) {
  309. drush_print(strip_tags($change), 2);
  310. }
  311. }
  312. if (!drush_confirm(dt('Do you wish to run all pending updates?'))) {
  313. return drush_user_abort();
  314. }
  315. $operations[] = array('drush_update_entity_definitions', array());
  316. $batch['operations'] = $operations;
  317. $batch += array(
  318. 'title' => 'Updating',
  319. 'init_message' => 'Starting updates',
  320. 'error_message' => 'An unrecoverable error has occurred. You can find the error message below. It is advised to copy it to the clipboard for reference.',
  321. 'finished' => 'drush_update_finished',
  322. 'file' => 'includes/update.inc',
  323. );
  324. batch_set($batch);
  325. \Drupal::service('state')->set('system.maintenance_mode', TRUE);
  326. drush_backend_batch_process('updatedb-batch-process');
  327. \Drupal::service('state')->set('system.maintenance_mode', FALSE);
  328. }
  329. else {
  330. drush_log(dt("No entity schema updates required"), LogLevel::SUCCESS);
  331. }
  332. }