diff --git a/core/includes/batch.inc b/core/includes/batch.inc index fbbcd09..2640fc4 100644 --- a/core/includes/batch.inc +++ b/core/includes/batch.inc @@ -205,316 +205,6 @@ function _batch_progress_page() { } /** - * Processes sets in a batch. - * - * If the batch was marked for progressive execution (default), this executes as - * many operations in batch sets until an execution time of 1 second has been - * exceeded. It will continue with the next operation of the same batch set in - * the next request. - * - * @return - * An array containing a completion value (in percent) and a status message. - */ -function _batch_process() { - $batch = &batch_get(); - $current_set = &_batch_current_set(); - // Indicate that this batch set needs to be initialized. - $set_changed = TRUE; - - // If this batch was marked for progressive execution (e.g. forms submitted by - // \Drupal::formBuilder()->submitForm(), initialize a timer to determine - // whether we need to proceed with the same batch phase when a processing time - // of 1 second has been exceeded. - if ($batch['progressive']) { - Timer::start('batch_processing'); - } - - if (empty($current_set['start'])) { - $current_set['start'] = microtime(TRUE); - } - - $queue = _batch_queue($current_set); - - while (!$current_set['success']) { - // If this is the first time we iterate this batch set in the current - // request, we check if it requires an additional file for functions - // definitions. - if ($set_changed && isset($current_set['file']) && is_file($current_set['file'])) { - include_once \Drupal::root() . '/' . $current_set['file']; - } - - $task_message = $label = ''; - // Assume a single pass operation and set the completion level to 1 by - // default. - $finished = 1; - - if ($item = $queue->claimItem()) { - list($callback, $args) = $item->data; - - // Build the 'context' array and execute the function call. - $batch_context = array( - 'sandbox' => &$current_set['sandbox'], - 'results' => &$current_set['results'], - 'finished' => &$finished, - 'message' => &$task_message, - ); - call_user_func_array($callback, array_merge($args, array(&$batch_context))); - - if ($finished >= 1) { - // Make sure this step is not counted twice when computing $current. - $finished = 0; - // Remove the processed operation and clear the sandbox. - $queue->deleteItem($item); - $current_set['count']--; - $current_set['sandbox'] = array(); - } - } - - // When all operations in the current batch set are completed, browse - // through the remaining sets, marking them 'successfully processed' - // along the way, until we find a set that contains operations. - // _batch_next_set() executes form submit handlers stored in 'control' - // sets (see \Drupal::service('form_submitter')), which can in turn add new - // sets to the batch. - $set_changed = FALSE; - $old_set = $current_set; - while (empty($current_set['count']) && ($current_set['success'] = TRUE) && _batch_next_set()) { - $current_set = &_batch_current_set(); - $current_set['start'] = microtime(TRUE); - $set_changed = TRUE; - } - - // At this point, either $current_set contains operations that need to be - // processed or all sets have been completed. - $queue = _batch_queue($current_set); - - // If we are in progressive mode, break processing after 1 second. - if ($batch['progressive'] && Timer::read('batch_processing') > 1000) { - // Record elapsed wall clock time. - $current_set['elapsed'] = round((microtime(TRUE) - $current_set['start']) * 1000, 2); - break; - } - } - - if ($batch['progressive']) { - // Gather progress information. - - // Reporting 100% progress will cause the whole batch to be considered - // processed. If processing was paused right after moving to a new set, - // we have to use the info from the new (unprocessed) set. - if ($set_changed && isset($current_set['queue'])) { - // Processing will continue with a fresh batch set. - $remaining = $current_set['count']; - $total = $current_set['total']; - $progress_message = $current_set['init_message']; - $task_message = ''; - } - else { - // Processing will continue with the current batch set. - $remaining = $old_set['count']; - $total = $old_set['total']; - $progress_message = $old_set['progress_message']; - } - - // Total progress is the number of operations that have fully run plus the - // completion level of the current operation. - $current = $total - $remaining + $finished; - $percentage = _batch_api_percentage($total, $current); - $elapsed = isset($current_set['elapsed']) ? $current_set['elapsed'] : 0; - $values = array( - '@remaining' => $remaining, - '@total' => $total, - '@current' => floor($current), - '@percentage' => $percentage, - '@elapsed' => \Drupal::service('date.formatter')->formatInterval($elapsed / 1000), - // If possible, estimate remaining processing time. - '@estimate' => ($current > 0) ? \Drupal::service('date.formatter')->formatInterval(($elapsed * ($total - $current) / $current) / 1000) : '-', - ); - $message = strtr($progress_message, $values); - if (!empty($task_message)) { - $label = $task_message; - } - - return array($percentage, $message, $label); - } - else { - // If we are not in progressive mode, the entire batch has been processed. - return _batch_finished(); - } -} - -/** - * Formats the percent completion for a batch set. - * - * @param $total - * The total number of operations. - * @param $current - * The number of the current operation. This may be a floating point number - * rather than an integer in the case of a multi-step operation that is not - * yet complete; in that case, the fractional part of $current represents the - * fraction of the operation that has been completed. - * - * @return - * The properly formatted percentage, as a string. We output percentages - * using the correct number of decimal places so that we never print "100%" - * until we are finished, but we also never print more decimal places than - * are meaningful. - * - * @see _batch_process() - */ -function _batch_api_percentage($total, $current) { - return Percentage::format($total, $current); -} - -/** - * Returns the batch set being currently processed. - */ -function &_batch_current_set() { - $batch = &batch_get(); - return $batch['sets'][$batch['current_set']]; -} - -/** - * Retrieves the next set in a batch. - * - * If there is a subsequent set in this batch, assign it as the new set to - * process and execute its form submit handler (if defined), which may add - * further sets to this batch. - * - * @return - * TRUE if a subsequent set was found in the batch. - */ -function _batch_next_set() { - $batch = &batch_get(); - if (isset($batch['sets'][$batch['current_set'] + 1])) { - $batch['current_set']++; - $current_set = &_batch_current_set(); - if (isset($current_set['form_submit']) && ($callback = $current_set['form_submit']) && is_callable($callback)) { - // We use our stored copies of $form and $form_state to account for - // possible alterations by previous form submit handlers. - $complete_form = &$batch['form_state']->getCompleteForm(); - call_user_func_array($callback, array(&$complete_form, &$batch['form_state'])); - } - return TRUE; - } -} - -/** - * Ends the batch processing. - * - * Call the 'finished' callback of each batch set to allow custom handling of - * the results and resolve page redirection. - */ -function _batch_finished() { - $batch = &batch_get(); - $batch_finished_redirect = NULL; - - // Execute the 'finished' callbacks for each batch set, if defined. - foreach ($batch['sets'] as $batch_set) { - if (isset($batch_set['finished'])) { - // Check if the set requires an additional file for function definitions. - if (isset($batch_set['file']) && is_file($batch_set['file'])) { - include_once \Drupal::root() . '/' . $batch_set['file']; - } - if (is_callable($batch_set['finished'])) { - $queue = _batch_queue($batch_set); - $operations = $queue->getAllItems(); - $batch_set_result = call_user_func_array($batch_set['finished'], array($batch_set['success'], $batch_set['results'], $operations, \Drupal::service('date.formatter')->formatInterval($batch_set['elapsed'] / 1000))); - // If a batch 'finished' callback requested a redirect after the batch - // is complete, save that for later use. If more than one batch set - // returned a redirect, the last one is used. - if ($batch_set_result instanceof RedirectResponse) { - $batch_finished_redirect = $batch_set_result; - } - } - } - } - - // Clean up the batch table and unset the static $batch variable. - if ($batch['progressive']) { - \Drupal::service('batch.storage')->delete($batch['id']); - foreach ($batch['sets'] as $batch_set) { - if ($queue = _batch_queue($batch_set)) { - $queue->deleteQueue(); - } - } - // Clean-up the session. Not needed for CLI updates. - if (isset($_SESSION)) { - unset($_SESSION['batches'][$batch['id']]); - if (empty($_SESSION['batches'])) { - unset($_SESSION['batches']); - } - } - } - $_batch = $batch; - $batch = NULL; - - // Redirect if needed. - if ($_batch['progressive']) { - // Revert the 'destination' that was saved in batch_process(). - if (isset($_batch['destination'])) { - \Drupal::request()->query->set('destination', $_batch['destination']); - } - - // Determine the target path to redirect to. If a batch 'finished' callback - // returned a redirect response object, use that. Otherwise, fall back on - // the form redirection. - if (isset($batch_finished_redirect)) { - return $batch_finished_redirect; - } - elseif (!isset($_batch['form_state'])) { - $_batch['form_state'] = new FormState(); - } - if ($_batch['form_state']->getRedirect() === NULL) { - $redirect = $_batch['batch_redirect'] ?: $_batch['source_url']; - // Any path with a scheme does not correspond to a route. - if (!$redirect instanceof Url) { - $options = UrlHelper::parse($redirect); - if (parse_url($options['path'], PHP_URL_SCHEME)) { - $redirect = Url::fromUri($options['path'], $options); - } - else { - $redirect = \Drupal::pathValidator()->getUrlIfValid($options['path']); - if (!$redirect) { - // Stay on the same page if the redirect was invalid. - $redirect = Url::fromRoute(''); - } - $redirect->setOptions($options); - } - } - $_batch['form_state']->setRedirectUrl($redirect); - } - - // Use \Drupal\Core\Form\FormSubmitterInterface::redirectForm() to handle - // the redirection logic. - $redirect = \Drupal::service('form_submitter')->redirectForm($_batch['form_state']); - if (is_object($redirect)) { - return $redirect; - } - - // If no redirection happened, redirect to the originating page. In case the - // form needs to be rebuilt, save the final $form_state for - // \Drupal\Core\Form\FormBuilderInterface::buildForm(). - if ($_batch['form_state']->isRebuilding()) { - $_SESSION['batch_form_state'] = $_batch['form_state']; - } - $callback = $_batch['redirect_callback']; - /** @var \Drupal\Core\Url $source_url */ - $source_url = $_batch['source_url']; - if (is_callable($callback)) { - $callback($_batch['source_url'], array('query' => array('op' => 'finish', 'id' => $_batch['id']))); - } - elseif ($callback === NULL) { - // Default to RedirectResponse objects when nothing specified. - $url = $source_url - ->setAbsolute() - ->setOption('query', ['op' => 'finish', 'id' => $_batch['id']]); - return new RedirectResponse($url->toString()); - } - } -} - -/** * Shutdown function: Stores the current batch data for the next request. * * @see _batch_page() diff --git a/core/tests/Drupal/Tests/Core/Batch/BatchProcessorTest.php b/core/tests/Drupal/Tests/Core/Batch/BatchProcessorTest.php index 198b796..45c7033 100644 --- a/core/tests/Drupal/Tests/Core/Batch/BatchProcessorTest.php +++ b/core/tests/Drupal/Tests/Core/Batch/BatchProcessorTest.php @@ -7,6 +7,11 @@ namespace Drupal\Tests\Core\Batch; -class BatchProcessorTest { +use Drupal\Tests\UnitTestCase; + +/** + * @coversDefaultClass \Drupal\Core\Batch\BatchProcessor + */ +class BatchProcessorTest extends UnitTestCase { }