batch.inc

  1. 8.0.x includes/batch.inc
  2. 8.0.x commands/core/drupal/batch.inc
  3. 6.x includes/batch.inc
  4. 6.x commands/core/drupal/batch.inc
  5. 7.x includes/batch.inc
  6. 7.x commands/core/drupal/batch.inc
  7. 3.x includes/batch.inc
  8. 4.x commands/core/drupal/batch.inc
  9. 4.x includes/batch.inc
  10. 5.x commands/core/drupal/batch.inc
  11. 5.x includes/batch.inc
  12. master commands/core/drupal/batch.inc
  13. master includes/batch.inc

Drupal 7 engine for the Batch API

Functions

Namesort descending Description
_drush_backend_batch_process Main loop for the Drush batch API.
_drush_batch_command Initialize the batch command and call the worker function.
_drush_batch_finished End the batch processing: Call the 'finished' callbacks to allow custom handling of results, and resolve page redirection.
_drush_batch_shutdown Shutdown function: store the batch data for next request, or clear the table if the batch is finished.
_drush_batch_worker Process batch operations

File

commands/core/drupal/batch.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Drupal 7 engine for the Batch API
  5. */
  6. use Drush\Log\LogLevel;
  7. /**
  8. * Main loop for the Drush batch API.
  9. *
  10. * Saves a record of the batch into the database, and progressively call $command to
  11. * process the operations.
  12. *
  13. * @param command
  14. * The command to call to process the batch.
  15. *
  16. */
  17. function _drush_backend_batch_process($command = 'batch-process', $args, $options) {
  18. $batch =& batch_get();
  19. if (isset($batch)) {
  20. $process_info = array(
  21. 'current_set' => 0,
  22. );
  23. $batch += $process_info;
  24. // The batch is now completely built. Allow other modules to make changes
  25. // to the batch so that it is easier to reuse batch processes in other
  26. // enviroments.
  27. if (drush_drupal_major_version() >= 8) {
  28. \Drupal::moduleHandler()->alter('batch', $batch);
  29. }
  30. else {
  31. drupal_alter('batch', $batch);
  32. }
  33. // Assign an arbitrary id: don't rely on a serial column in the 'batch'
  34. // table, since non-progressive batches skip database storage completely.
  35. $batch['id'] = db_next_id();
  36. $args[] = $batch['id'];
  37. $batch['progressive'] = TRUE;
  38. // Move operations to a job queue. Non-progressive batches will use a
  39. // memory-based queue.
  40. foreach ($batch['sets'] as $key => $batch_set) {
  41. _batch_populate_queue($batch, $key);
  42. }
  43. drush_include_engine('drupal', 'environment');
  44. // Store the batch.
  45. if (drush_drupal_major_version() >= 8) {
  46. /** @var \Drupal\Core\Batch\BatchStorage $batch_storage */
  47. $batch_storage = \Drupal::service('batch.storage');
  48. $batch_storage->create($batch);
  49. }
  50. else {
  51. db_insert('batch')
  52. ->fields(array(
  53. 'bid' => $batch['id'],
  54. 'timestamp' => REQUEST_TIME,
  55. 'token' => drush_get_token($batch['id']),
  56. 'batch' => serialize($batch),
  57. ))
  58. ->execute();
  59. }
  60. $finished = FALSE;
  61. // Not used in D8+ and possibly earlier.
  62. global $user;
  63. while (!$finished) {
  64. $data = drush_invoke_process('@self', $command, $args, array('user' => drush_user_get_class()->getCurrentUserAsSingle()->id()));
  65. $finished = drush_get_error() || !$data || (isset($data['context']['drush_batch_process_finished']) && $data['context']['drush_batch_process_finished'] == TRUE);
  66. }
  67. }
  68. }
  69. /**
  70. * Initialize the batch command and call the worker function.
  71. *
  72. * Loads the batch record from the database and sets up the requirements
  73. * for the worker, such as registering the shutdown function.
  74. *
  75. * @param id
  76. * The batch id of the batch being processed.
  77. */
  78. function _drush_batch_command($id) {
  79. $batch =& batch_get();
  80. $data = db_query("SELECT batch FROM {batch} WHERE bid = :bid", array(
  81. ':bid' => $id))->fetchField();
  82. if ($data) {
  83. $batch = unserialize($data);
  84. }
  85. else {
  86. return FALSE;
  87. }
  88. if (!isset($batch['running'])) {
  89. $batch['running'] = TRUE;
  90. }
  91. // Register database update for end of processing.
  92. register_shutdown_function('_drush_batch_shutdown');
  93. if (_drush_batch_worker()) {
  94. _drush_batch_finished();
  95. }
  96. }
  97. /**
  98. * Process batch operations
  99. *
  100. * Using the current $batch process each of the operations until the batch
  101. * has been completed or half of the available memory for the process has been
  102. * reached.
  103. */
  104. function _drush_batch_worker() {
  105. $batch =& batch_get();
  106. $current_set =& _batch_current_set();
  107. $set_changed = TRUE;
  108. if (empty($current_set['start'])) {
  109. $current_set['start'] = microtime(TRUE);
  110. }
  111. $queue = _batch_queue($current_set);
  112. while (!$current_set['success']) {
  113. // If this is the first time we iterate this batch set in the current
  114. // request, we check if it requires an additional file for functions
  115. // definitions.
  116. if ($set_changed && isset($current_set['file']) && is_file($current_set['file'])) {
  117. include_once DRUPAL_ROOT . '/' . $current_set['file'];
  118. }
  119. $task_message = '';
  120. // Assume a single pass operation and set the completion level to 1 by
  121. // default.
  122. $finished = 1;
  123. if ($item = $queue->claimItem()) {
  124. list($function, $args) = $item->data;
  125. // Build the 'context' array and execute the function call.
  126. $batch_context = array(
  127. 'sandbox' => &$current_set['sandbox'],
  128. 'results' => &$current_set['results'],
  129. 'finished' => &$finished,
  130. 'message' => &$task_message,
  131. );
  132. // Magic wrap to catch changes to 'message' key.
  133. $batch_context = new DrushBatchContext($batch_context);
  134. // Tolerate recoverable errors.
  135. // See https://github.com/drush-ops/drush/issues/1930
  136. $halt_on_error = drush_get_option('halt-on-error', TRUE);
  137. drush_set_option('halt-on-error', FALSE);
  138. call_user_func_array($function, array_merge($args, array(&$batch_context)));
  139. drush_set_option('halt-on-error', $halt_on_error);
  140. $finished = $batch_context['finished'];
  141. if ($finished >= 1) {
  142. // Make sure this step is not counted twice when computing $current.
  143. $finished = 0;
  144. // Remove the processed operation and clear the sandbox.
  145. $queue->deleteItem($item);
  146. $current_set['count']--;
  147. $current_set['sandbox'] = array();
  148. }
  149. }
  150. // When all operations in the current batch set are completed, browse
  151. // through the remaining sets, marking them 'successfully processed'
  152. // along the way, until we find a set that contains operations.
  153. // _batch_next_set() executes form submit handlers stored in 'control'
  154. // sets (see form_execute_handlers()), which can in turn add new sets to
  155. // the batch.
  156. $set_changed = FALSE;
  157. $old_set = $current_set;
  158. while (empty($current_set['count']) && ($current_set['success'] = TRUE) && _batch_next_set()) {
  159. $current_set = &_batch_current_set();
  160. $current_set['start'] = microtime(TRUE);
  161. $set_changed = TRUE;
  162. }
  163. // At this point, either $current_set contains operations that need to be
  164. // processed or all sets have been completed.
  165. $queue = _batch_queue($current_set);
  166. // If we are in progressive mode, break processing after 1 second.
  167. if (drush_memory_limit() > 0 && (memory_get_usage() * 2) >= drush_memory_limit()) {
  168. drush_log(dt("Batch process has consumed in excess of 50% of available memory. Starting new thread"), LogLevel::BATCH);
  169. // Record elapsed wall clock time.
  170. $current_set['elapsed'] = round((microtime(TRUE) - $current_set['start']) * 1000, 2);
  171. break;
  172. }
  173. }
  174. // Reporting 100% progress will cause the whole batch to be considered
  175. // processed. If processing was paused right after moving to a new set,
  176. // we have to use the info from the new (unprocessed) set.
  177. if ($set_changed && isset($current_set['queue'])) {
  178. // Processing will continue with a fresh batch set.
  179. $remaining = $current_set['count'];
  180. $total = $current_set['total'];
  181. $progress_message = $current_set['init_message'];
  182. $task_message = '';
  183. }
  184. else {
  185. // Processing will continue with the current batch set.
  186. $remaining = $old_set['count'];
  187. $total = $old_set['total'];
  188. $progress_message = $old_set['progress_message'];
  189. }
  190. $current = $total - $remaining + $finished;
  191. $percentage = _batch_api_percentage($total, $current);
  192. return ($percentage == 100);
  193. }
  194. /**
  195. * End the batch processing:
  196. * Call the 'finished' callbacks to allow custom handling of results,
  197. * and resolve page redirection.
  198. */
  199. function _drush_batch_finished() {
  200. $batch = &batch_get();
  201. // Execute the 'finished' callbacks for each batch set, if defined.
  202. foreach ($batch['sets'] as $batch_set) {
  203. if (isset($batch_set['finished'])) {
  204. // Check if the set requires an additional file for function definitions.
  205. if (isset($batch_set['file']) && is_file($batch_set['file'])) {
  206. include_once DRUPAL_ROOT . '/' . $batch_set['file'];
  207. }
  208. if (is_callable($batch_set['finished'])) {
  209. $queue = _batch_queue($batch_set);
  210. $operations = $queue->getAllItems();
  211. $elapsed = $batch_set['elapsed'] / 1000;
  212. $elapsed = drush_drupal_major_version() >=8 ? \Drupal::service('date.formatter')->formatInterval($elapsed) : format_interval($elapsed);
  213. $batch_set['finished']($batch_set['success'], $batch_set['results'], $operations, $elapsed);
  214. }
  215. }
  216. }
  217. // Clean up the batch table and unset the static $batch variable.
  218. if (drush_drupal_major_version() >= 8) {
  219. /** @var \Drupal\Core\Batch\BatchStorage $batch_storage */
  220. $batch_storage = \Drupal::service('batch.storage');
  221. $batch_storage->delete($batch['id']);
  222. }
  223. else {
  224. db_delete('batch')
  225. ->condition('bid', $batch['id'])
  226. ->execute();
  227. }
  228. foreach ($batch['sets'] as $batch_set) {
  229. if ($queue = _batch_queue($batch_set)) {
  230. $queue->deleteQueue();
  231. }
  232. }
  233. $_batch = $batch;
  234. $batch = NULL;
  235. drush_set_option('drush_batch_process_finished', TRUE);
  236. }
  237. /**
  238. * Shutdown function: store the batch data for next request,
  239. * or clear the table if the batch is finished.
  240. */
  241. function _drush_batch_shutdown() {
  242. if ($batch = batch_get()) {
  243. if (drush_drupal_major_version() >= 8) {
  244. /** @var \Drupal\Core\Batch\BatchStorage $batch_storage */
  245. $batch_storage = \Drupal::service('batch.storage');
  246. $batch_storage->update($batch);
  247. }
  248. else {
  249. db_update('batch')
  250. ->fields(array('batch' => serialize($batch)))
  251. ->condition('bid', $batch['id'])
  252. ->execute();
  253. }
  254. }
  255. }