1. 8.5.x core/includes/form.inc batch
  2. 8.0.x core/includes/form.inc batch
  3. 8.1.x core/includes/form.inc batch
  4. 8.2.x core/includes/form.inc batch
  5. 8.3.x core/includes/form.inc batch
  6. 8.4.x core/includes/form.inc batch
  7. 8.6.x core/includes/form.inc batch
  8. 6.x includes/form.inc batch
  9. 7.x includes/form.inc batch

Creates and processes batch operations.

Functions allowing forms processing to be spread out over several page requests, thus ensuring that the processing does not get interrupted because of a PHP timeout, while allowing the user to receive feedback on the progress of the ongoing operations.

The API is primarily designed to integrate nicely with the Form API workflow, but can also be used by non-Form API scripts (like update.php) or even simple page callbacks (which should probably be used sparingly).


$batch = array(
  'title' => t('Exporting'),
  'operations' => array(
  'finished' => 'my_finished_callback',
  'file' => 'path_to_file_containing_myfunctions',

// Only needed if not inside a form _submit handler.
// Setting redirect in batch_process.

Note: if the batch 'title', 'init_message', 'progress_message', or 'error_message' could contain any user input, it is the responsibility of the code calling batch_set() to sanitize them first with a function like check_plain() or filter_xss(). Furthermore, if the batch operation returns any user input in the 'results' or 'message' keys of $context, it must also sanitize them first.

Sample callback_batch_operation():

// Simple and artificial: load a node of a given type for a given user
function my_function_1($uid, $type, &$context) {

  // The $context array gathers batch context information about the execution (read),
  // as well as 'return values' for the current operation (write)
  // The following keys are provided :
  // 'results' (read / write): The array of results gathered so far by
  //   the batch processing, for the current operation to append its own.
  // 'message' (write): A text message displayed in the progress page.
  // The following keys allow for multi-step operations :
  // 'sandbox' (read / write): An array that can be freely used to
  //   store persistent data between iterations. It is recommended to
  //   use this instead of $_SESSION, which is unsafe if the user
  //   continues browsing in a separate window while the batch is processing.
  // 'finished' (write): A float number between 0 and 1 informing
  //   the processing engine of the completion level for the operation.
  //   1 (or no value explicitly set) means the operation is finished
  //   and the batch processing can continue to the next operation.
  $node = node_load(array(
    'uid' => $uid,
    'type' => $type,
  $context['results'][] = $node->nid . ' : ' . check_plain($node->title);
  $context['message'] = check_plain($node->title);

// More advanced example: multi-step operation - load all nodes, five by five
function my_function_2(&$context) {
  if (empty($context['sandbox'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['current_node'] = 0;
    $context['sandbox']['max'] = db_query('SELECT COUNT(DISTINCT nid) FROM {node}')
  $limit = 5;
  $result = db_select('node')
    ->fields('node', array(
    ->condition('nid', $context['sandbox']['current_node'], '>')
    ->range(0, $limit)
  foreach ($result as $row) {
    $node = node_load($row->nid, NULL, TRUE);
    $context['results'][] = $node->nid . ' : ' . check_plain($node->title);
    $context['sandbox']['current_node'] = $node->nid;
    $context['message'] = check_plain($node->title);
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];

Sample callback_batch_finished():

function my_finished_callback($success, $results, $operations) {

  // The 'success' parameter means no fatal PHP errors were detected. All
  // other error management should be handled using 'results'.
  if ($success) {
    $message = format_plural(count($results), 'One post processed.', '@count posts processed.');
  else {
    $message = t('Finished with an error.');

  // Providing data for the redirected page is done through $_SESSION.
  foreach ($results as $result) {
    $items[] = t('Loaded node %title.', array(
      '%title' => $result,
  $_SESSION['my_batch_results'] = $items;


includes/form.inc, line 4407
Functions for form and batch generation and processing.


Namesort descending Location Description
batch_get includes/form.inc Retrieves the current batch.
batch_process includes/form.inc Processes the batch.
batch_set includes/form.inc Adds a new batch.
hook_batch_alter modules/system/system.api.php Alter batch information before a batch is processed.
_batch_populate_queue includes/form.inc Populates a job queue with the operations of a batch set.
_batch_queue includes/form.inc Returns a queue object for a batch set.


Rickdrummer’s picture

If you are running your batch process without using FAPI submit form, you have to use drupal_goto('path') in your batch_test_finished() function, otherwise your batch will loop endlessly.

squall1023’s picture

my version is 7.2 and I try to run drupal_mail() in the operation, it run though the query and I receive the mail, but no any title and body somehow, the changes that I made in hook_mail seems doesn't affect the output, could anyone please tell me about this problem? thanks!

dalin’s picture

How do you throw an error? Bonus points if you can tell me how to halt future pending batch jobs as well.

dalin’s picture

Well this
throw new Exception(t('My error message.'));
technically works. But it doesn't show pretty output. Ideally there would be a method that has nicer display.

jojonaloha’s picture

I was wanting to do something similar, not sure if it's the best method, but after looking at batch.js and progress.js and trial and error, I figured out that you can do something like this (at least in D7)

function some_batch_operation(&$context) {
  // do stuff
  // if something goes wrong
  $result = array('status' => FALSE, 'data' => $message);

What you set as your batch 'error_message' will be displayed first in a p tag with class="error" (see batch.js errorCallback), and whatever you put in $message will be wrapped in a div tag with class="messages error" (see progress.js displayError).

nerdacus’s picture

FYI: if you have Background Batch enabled, this will not work.
I have yet to find a way to throw an error for Background Batch.

clintu’s picture

Create another batch based on the condition and display the error message.

technicalknockout’s picture

Is function batch_test_finished() supposed to be function my_finished_callback() in the example code? Seems like 'my_finished_callback' is not defined anywhere and 'batch_test_finished' is not referenced anywhere else in the code.

vegantriathlete’s picture

$batch['finished'] = 'my_finished_callback'

It looks like this was probably originally copy / pasted from http://drupal.org/node/180528 and then modified.

mastermindg’s picture

Here's a simple example of passing array values between operations:


stomerfull’s picture

Hello every one

Is it possible to pass a parameter to my finished function like operations like this

$batch = array(
  'title' => t('Exporting'),
  'operations' => array(
    array('my_function_1', array($account->uid, 'story')),
    array('my_function_2', array()),
  'finished' => array(
    array('my_finished_callback', array($data))),
  'file' => 'path_to_file_containing_myfunctions',

Thanks in advance

roderik’s picture

No. 'parameters' can be passed through $results['my-custom-key']. You can set $context['results']['my-custom-key'] inside your my_function_1() or my_function_2().

It's quite indirect, depending on your code structure... but it works.

sbilde’s picture

Would there be any way to call a batch function, creating some entities, from a submit-handler where it could return the ids of the newly created entities?
I have almost been giving up on this.

roderik’s picture

If I read you correctly, you want something that is like:

$ids = my_batch_funtion();

That concept does not work. a 'batch function' is not a function.

Since a batch operation is designed to do things across (potentially) multiple HTTP requests, finishing far in the future... it can never finish and return values to your code thread executing right now in that same HTTP request.

You must find a way to e.g. do_someting() in your finish callback.

amb11’s picture

which file should this be written to?

amb11’s picture

Please I need help, when ever I try opening module page or configuration, it gives me the same error as above. I need help on how to over come it.

Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 71 bytes) in /home/sitename/public_html/includes/form.inc on line 1841

fuzzy76’s picture

That error has absolutely nothing to do with the batch API. It does, however, look like you are running Drupal 7 at the absolute minimum of 32 MB memory. In that case, you won't be able to add many modules before running into that error message.

gmario’s picture

Hi all,

I'm unable to delete a batch process. I've defined a batch process within a form's submit function. For a couple of times to delete batch process from "batch" table is been enough but from one point on, despite I have deleted batch's table content or/and clear endlessly the cache, every time I load a page (any page) the batch process runs (I have evidence by log messages). Is anybody known how to removes completely the batch process?

many thanks.

a si biri mellusu

stefan.r’s picture

Possibly relevant: How does the batch API work internally?

As I was trying to get XDebug to trigger on a Batch API call, this Stack Exchange page got me on the right track -- editing my has_js cookie to be 0 made it trigger.

Elijah Lynn’s picture

Elijah Lynn’s picture

NoRandom’s picture

I'm creating a module that imports entries in an XML file (~10-20MB and ~10-20K entries) as new nodes or update the old nodes if they already exists. To my surprise, I was getting timeouts when trying to set a batch with more than ~2K operations:

    foreach ($a_parsed_xml as $o_parsed_entry) {
        $a_batch_job['operations'][] = array('process_entry', array($s_platform, $o_parsed_entry));
    // No problem until here

The timeout is produced by **batch_set** function or another one downstream and importing a 10MB-10K entries XML takes ~30s. I can't even reach the progress bar.

The solution, so far because I haven't finished the code yet, seems to be split the entries in chunks (of 20 elements in the example below) and process them together.

    $i_chunk_size = 20;
    $a_xml_chunks = array_chunk($a_parsed_xml);
    foreach ($a_xml_chunks as $a_chunk) {
        $a_batch_job['operations'][] = array('process_entries_chunk', array($s_platform, $a_chunk));

So, to my understanding, there are only two options:

  1. Process each element as an individual operation => it will fail with ~2k operations.
  2. Process elements in chunks => You reduce the number of operations but you increase the processing time for each operation so it will fail with complex operations.

What would be the proper way to create a batch job with high number of high time consuming operations?

Regards and thanks in advance.

Maxime Gilbert’s picture

An another way is to process each of the 100 first elements as an individual operation then the batch finished process the next 100 elements as an individual operation until all elements are processed.