c48dfd438e127b77e273b68b688f525f243f22c9
[yaffs-website] / vendor / drush / drush / commands / core / drupal / update.inc
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
10 use Drush\Log\LogLevel;
11
12 /**
13  * Drupal's update.inc has functions that are in previous update_X.inc files
14  * for example, update_check_incompatibility() which can prove useful when
15  * enabling modules.
16  */
17 require_once DRUSH_DRUPAL_CORE . '/includes/update.inc';
18
19 use Drupal\Core\Utility\Error;
20 use Drupal\Core\Entity\EntityStorageException;
21 /**
22  * Perform one update and store the results which will later be displayed on
23  * the finished page.
24  *
25  * An update function can force the current and all later updates for this
26  * module to abort by returning a $ret array with an element like:
27  * $ret['#abort'] = array('success' => FALSE, 'query' => 'What went wrong');
28  * The schema version will not be updated in this case, and all the
29  * aborted updates will continue to appear on update.php as updates that
30  * have not yet been run.
31  *
32  * @param $module
33  *   The module whose update will be run.
34  * @param $number
35  *   The update number to run.
36  * @param $context
37  *   The batch context array
38  */
39 function drush_update_do_one($module, $number, $dependency_map,  &$context) {
40   $function = $module . '_update_' . $number;
41
42   // If this update was aborted in a previous step, or has a dependency that
43   // was aborted in a previous step, go no further.
44   if (!empty($context['results']['#abort']) && array_intersect($context['results']['#abort'], array_merge($dependency_map, array($function)))) {
45     return;
46   }
47
48   $context['log'] = FALSE;
49
50   \Drupal::moduleHandler()->loadInclude($module, 'install');
51
52   $ret = array();
53   if (function_exists($function)) {
54     try {
55       if ($context['log']) {
56         Database::startLog($function);
57       }
58
59       drush_log("Executing " . $function);
60       $ret['results']['query'] = $function($context['sandbox']);
61       $ret['results']['success'] = TRUE;
62     }
63     // @TODO We may want to do different error handling for different exception
64     // types, but for now we'll just print the message.
65     catch (Exception $e) {
66       $ret['#abort'] = array('success' => FALSE, 'query' => $e->getMessage());
67       drush_set_error('DRUPAL_EXCEPTION', $e->getMessage());
68     }
69
70     if ($context['log']) {
71       $ret['queries'] = Database::getLog($function);
72     }
73   }
74   else {
75     $ret['#abort'] = array('success' => FALSE);
76     drush_set_error('DRUSH_UPDATE_FUNCTION_NOT_FOUND', dt('Update function @function not found', array('@function' => $function)));
77   }
78
79   if (isset($context['sandbox']['#finished'])) {
80     $context['finished'] = $context['sandbox']['#finished'];
81     unset($context['sandbox']['#finished']);
82   }
83
84   if (!isset($context['results'][$module])) {
85     $context['results'][$module] = array();
86   }
87   if (!isset($context['results'][$module][$number])) {
88     $context['results'][$module][$number] = array();
89   }
90   $context['results'][$module][$number] = array_merge($context['results'][$module][$number], $ret);
91
92   if (!empty($ret['#abort'])) {
93     // Record this function in the list of updates that were aborted.
94     $context['results']['#abort'][] = $function;
95   }
96
97   // Record the schema update if it was completed successfully.
98   if ($context['finished'] == 1 && empty($ret['#abort'])) {
99     drupal_set_installed_schema_version($module, $number);
100   }
101
102   $context['message'] = 'Performing ' . $function;
103 }
104
105 /**
106  * Clears caches and rebuilds the container.
107  *
108  * This is called in between regular updates and post updates. Do not use
109  * drush_drupal_cache_clear_all() as the cache clearing and container rebuild
110  * must happen in the same process that the updates are run in.
111  *
112  * Drupal core's update.php uses drupal_flush_all_caches() directly without
113  * explicitly rebuilding the container as the container is rebuilt on the next
114  * HTTP request of the batch.
115  *
116  * @see drush_drupal_cache_clear_all()
117  * @see \Drupal\system\Controller\DbUpdateController::triggerBatch()
118  */
119 function drush_update_cache_rebuild() {
120   drupal_flush_all_caches();
121   \Drupal::service('kernel')->rebuildContainer();
122 }
123
124 function update_main() {
125   // In D8, we expect to be in full bootstrap.
126   drush_bootstrap_to_phase(DRUSH_BOOTSTRAP_DRUPAL_FULL);
127
128   require_once DRUPAL_ROOT . '/core/includes/install.inc';
129   require_once DRUPAL_ROOT . '/core/includes/update.inc';
130   drupal_load_updates();
131   update_fix_compatibility();
132
133   // Check requirements before updating.
134   if (!drush_update_check_requirements()) {
135     if (!drush_confirm(dt('Requirements check reports errors. Do you wish to continue?'))) {
136         return drush_user_abort();
137     }
138   }
139
140   // Pending hook_update_N() implementations.
141   $pending = update_get_update_list();
142
143   // Pending hook_post_update_X() implementations.
144   $post_updates = \Drupal::service('update.post_update_registry')->getPendingUpdateInformation();
145
146   $start = array();
147
148   $change_summary = [];
149   if (drush_get_option('entity-updates', FALSE)) {
150     $change_summary = \Drupal::entityDefinitionUpdateManager()->getChangeSummary();
151   }
152
153   // Print a list of pending updates for this module and get confirmation.
154   if (count($pending) || count($change_summary) || count($post_updates)) {
155     drush_print(dt('The following updates are pending:'));
156     drush_print();
157
158     foreach ($change_summary as $entity_type_id => $changes) {
159       drush_print($entity_type_id . ' entity type : ');
160       foreach ($changes as $change) {
161         drush_print(strip_tags($change), 2);
162       }
163     }
164
165     foreach (array('update', 'post_update') as $update_type) {
166       $updates = $update_type == 'update' ? $pending : $post_updates;
167       foreach ($updates as $module => $updates) {
168         if (isset($updates['start'])) {
169           drush_print($module . ' module : ');
170           if (!empty($updates['pending'])) {
171             $start += [$module => array()];
172
173             $start[$module] = array_merge($start[$module], $updates['pending']);
174             foreach ($updates['pending'] as $update) {
175               drush_print(strip_tags($update), 2);
176             }
177           }
178           drush_print();
179         }
180       }
181     }
182
183     if (!drush_confirm(dt('Do you wish to run all pending updates?'))) {
184       return drush_user_abort();
185     }
186
187     drush_update_batch($start);
188   }
189   else {
190     drush_log(dt("No database updates required"), LogLevel::SUCCESS);
191   }
192
193   return count($pending) + count($change_summary) + count($post_updates);
194 }
195
196 /**
197  * Check update requirements and report any errors.
198  */
199 function drush_update_check_requirements() {
200   $continue = TRUE;
201
202   \Drupal::moduleHandler()->resetImplementations();
203   $requirements = update_check_requirements();
204   $severity = drupal_requirements_severity($requirements);
205
206   // If there are issues, report them.
207   if ($severity != REQUIREMENT_OK) {
208     if ($severity === REQUIREMENT_ERROR) {
209       $continue = FALSE;
210     }
211     foreach ($requirements as $requirement) {
212       if (isset($requirement['severity']) && $requirement['severity'] != REQUIREMENT_OK) {
213         $message = isset($requirement['description']) ? $requirement['description'] : '';
214         if (isset($requirement['value']) && $requirement['value']) {
215           $message .= ' (Currently using '. $requirement['title'] .' '. $requirement['value'] .')';
216         }
217         $log_level = $requirement['severity'] === REQUIREMENT_ERROR ? LogLevel::ERROR : LogLevel::WARNING;
218         drush_log($message, $log_level);
219       }
220     }
221   }
222
223   return $continue;
224 }
225
226 function _update_batch_command($id) {
227   // In D8, we expect to be in full bootstrap.
228   drush_bootstrap_to_phase(DRUSH_BOOTSTRAP_DRUPAL_FULL);
229
230   drush_batch_command($id);
231 }
232
233 /**
234  * Start the database update batch process.
235  */
236 function drush_update_batch() {
237   $start = drush_get_update_list();
238   // Resolve any update dependencies to determine the actual updates that will
239   // be run and the order they will be run in.
240   $updates = update_resolve_dependencies($start);
241
242   // Store the dependencies for each update function in an array which the
243   // batch API can pass in to the batch operation each time it is called. (We
244   // do not store the entire update dependency array here because it is
245   // potentially very large.)
246   $dependency_map = array();
247   foreach ($updates as $function => $update) {
248     $dependency_map[$function] = !empty($update['reverse_paths']) ? array_keys($update['reverse_paths']) : array();
249   }
250
251   $operations = array();
252
253   foreach ($updates as $update) {
254     if ($update['allowed']) {
255       // Set the installed version of each module so updates will start at the
256       // correct place. (The updates are already sorted, so we can simply base
257       // this on the first one we come across in the above foreach loop.)
258       if (isset($start[$update['module']])) {
259         drupal_set_installed_schema_version($update['module'], $update['number'] - 1);
260         unset($start[$update['module']]);
261       }
262       // Add this update function to the batch.
263       $function = $update['module'] . '_update_' . $update['number'];
264       $operations[] = array('drush_update_do_one', array($update['module'], $update['number'], $dependency_map[$function]));
265     }
266   }
267
268   // Apply post update hooks.
269   $post_updates = \Drupal::service('update.post_update_registry')->getPendingUpdateFunctions();
270   if ($post_updates) {
271     $operations[] = ['drush_update_cache_rebuild', []];
272     foreach ($post_updates as $function) {
273       $operations[] = ['update_invoke_post_update', [$function]];
274     }
275   }
276
277   // Lastly, perform entity definition updates, which will update storage
278   // schema if needed. If module update functions need to work with specific
279   // entity schema they should call the entity update service for the specific
280   // update themselves.
281   // @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface::applyEntityUpdate()
282   // @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface::applyFieldUpdate()
283   if (drush_get_option('entity-updates', FALSE) &&  \Drupal::entityDefinitionUpdateManager()->needsUpdates()) {
284     $operations[] = array('drush_update_entity_definitions', array());
285   }
286
287   $batch['operations'] = $operations;
288   $batch += array(
289     'title' => 'Updating',
290     'init_message' => 'Starting updates',
291     '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.',
292     'finished' => 'drush_update_finished',
293     'file' => 'includes/update.inc',
294   );
295   batch_set($batch);
296   \Drupal::service('state')->set('system.maintenance_mode', TRUE);
297   drush_backend_batch_process('updatedb-batch-process');
298   \Drupal::service('state')->set('system.maintenance_mode', FALSE);
299 }
300
301 /**
302  * Apply entity schema updates.
303  */
304 function drush_update_entity_definitions(&$context) {
305   try {
306     \Drupal::entityDefinitionUpdateManager()->applyUpdates();
307   }
308   catch (EntityStorageException $e) {
309     watchdog_exception('update', $e);
310     $variables = Error::decodeException($e);
311     unset($variables['backtrace']);
312     // The exception message is run through
313     // \Drupal\Component\Utility\SafeMarkup::checkPlain() by
314     // \Drupal\Core\Utility\Error::decodeException().
315     $ret['#abort'] = array('success' => FALSE, 'query' => t('%type: !message in %function (line %line of %file).', $variables));
316     $context['results']['core']['update_entity_definitions'] = $ret;
317     $context['results']['#abort'][] = 'update_entity_definitions';
318   }
319 }
320
321 // Copy of protected \Drupal\system\Controller\DbUpdateController::getModuleUpdates.
322 function drush_get_update_list() {
323   $return = array();
324   $updates = update_get_update_list();
325   foreach ($updates as $module => $update) {
326     $return[$module] = $update['start'];
327   }
328
329   return $return;
330 }
331
332 /**
333  * Process and display any returned update output.
334  *
335  * @see \Drupal\system\Controller\DbUpdateController::batchFinished()
336  * @see \Drupal\system\Controller\DbUpdateController::results()
337  */
338 function drush_update_finished($success, $results, $operations) {
339
340   if (!drush_get_option('cache-clear', TRUE)) {
341     drush_log(dt("Skipping cache-clear operation due to --cache-clear=0 option."), LogLevel::WARNING);
342   }
343   else {
344     drupal_flush_all_caches();
345   }
346
347   foreach ($results as $module => $updates) {
348     if ($module != '#abort') {
349       foreach ($updates as $number => $queries) {
350         foreach ($queries as $query) {
351           // If there is no message for this update, don't show anything.
352           if (empty($query['query'])) {
353             continue;
354           }
355
356           if ($query['success']) {
357             drush_log(strip_tags($query['query']));
358           }
359           else {
360             drush_set_error(dt('Failed: ') . strip_tags($query['query']));
361           }
362         }
363       }
364     }
365   }
366 }
367
368 /**
369  * Return a 2 item array with
370  *  - an array where each item is a 3 item associative array describing a pending update.
371  *  - an array listing the first update to run, keyed by module.
372  */
373 function updatedb_status() {
374   $pending = update_get_update_list();
375
376   $return = array();
377   // Ensure system module's updates run first.
378   $start['system'] = array();
379
380   foreach (\Drupal::entityDefinitionUpdateManager()->getChangeSummary() as $entity_type_id => $changes) {
381     foreach ($changes as $change) {
382       $return[] = array(
383         'module' => dt('@type entity type', array('@type' => $entity_type_id)), 'update_id' => '', 'description' => strip_tags($change));
384     }
385   }
386
387   // Print a list of pending updates for this module and get confirmation.
388   foreach ($pending as $module => $updates) {
389     if (isset($updates['start']))  {
390       foreach ($updates['pending'] as $update_id => $description) {
391         // Strip cruft from front.
392         $description = str_replace($update_id . ' -   ', '', $description);
393         $return[] = array('module' => ucfirst($module), 'update_id' => $update_id, 'description' => $description);
394       }
395       if (isset($updates['start'])) {
396         $start[$module] = $updates['start'];
397       }
398     }
399   }
400
401   return array($return, $start);
402 }
403
404 /**
405  * Apply pending entity schema updates.
406  */
407 function entity_updates_main() {
408   $change_summary = \Drupal::entityDefinitionUpdateManager()->getChangeSummary();
409   if (!empty($change_summary)) {
410     drush_print(dt('The following updates are pending:'));
411     drush_print();
412
413     foreach ($change_summary as $entity_type_id => $changes) {
414       drush_print($entity_type_id . ' entity type : ');
415       foreach ($changes as $change) {
416         drush_print(strip_tags($change), 2);
417       }
418     }
419
420     if (!drush_confirm(dt('Do you wish to run all pending updates?'))) {
421       return drush_user_abort();
422     }
423
424     $operations[] = array('drush_update_entity_definitions', array());
425
426
427     $batch['operations'] = $operations;
428     $batch += array(
429       'title' => 'Updating',
430       'init_message' => 'Starting updates',
431       '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.',
432       'finished' => 'drush_update_finished',
433       'file' => 'includes/update.inc',
434     );
435     batch_set($batch);
436     \Drupal::service('state')->set('system.maintenance_mode', TRUE);
437     drush_backend_batch_process('updatedb-batch-process');
438     \Drupal::service('state')->set('system.maintenance_mode', FALSE);
439   }
440   else {
441     drush_log(dt("No entity schema updates required"), LogLevel::SUCCESS);
442   }
443 }