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