function drupal_process_form

Processes a form submission.

This function is the heart of form API. The form gets built, validated and in appropriate cases, submitted and rebuilt.


$form_id: The unique string identifying the current form.

$form: An associative array containing the structure of the form.

$form_state: A keyed array containing the current state of the form. This includes the current persistent storage data for the form, and any data passed along by earlier steps when displaying a multi-step form. Additional information, like the sanitized $_POST data, is also accumulated here.

Related topics

7 calls to drupal_process_form()
ajax_form_callback in includes/
Menu callback; handles Ajax requests for the #ajax Form API property.
drupal_build_form in includes/
Builds and process a form based on a form id.
drupal_form_submit in includes/
Retrieves, populates, and processes a form.
FieldAttachOtherTestCase::testFieldAttachSubmit in modules/field/tests/field.test
Test field_attach_submit().
file_ajax_upload in modules/file/file.module
Menu callback; Shared Ajax callback for file uploads and deletions.

... See full list


includes/, line 867


function drupal_process_form($form_id, &$form, &$form_state) {
    $form_state['values'] = array();
    // With $_GET, these forms are always submitted if requested.
    if ($form_state['method'] == 'get' && !empty($form_state['always_process'])) {
        if (!isset($form_state['input']['form_build_id'])) {
            $form_state['input']['form_build_id'] = $form['#build_id'];
        if (!isset($form_state['input']['form_id'])) {
            $form_state['input']['form_id'] = $form_id;
        if (!isset($form_state['input']['form_token']) && isset($form['#token'])) {
            $form_state['input']['form_token'] = drupal_get_token($form['#token']);
    // form_builder() finishes building the form by calling element #process
    // functions and mapping user input, if any, to #value properties, and also
    // storing the values in $form_state['values']. We need to retain the
    // unprocessed $form in case it needs to be cached.
    $unprocessed_form = $form;
    $form = form_builder($form_id, $form, $form_state);
    // Only process the input if we have a correct form submission.
    if ($form_state['process_input']) {
        drupal_validate_form($form_id, $form, $form_state);
        // drupal_html_id() maintains a cache of element IDs it has seen,
        // so it can prevent duplicates. We want to be sure we reset that
        // cache when a form is processed, so scenarios that result in
        // the form being built behind the scenes and again for the
        // browser don't increment all the element IDs needlessly.
        if (!form_get_errors()) {
            // In case of errors, do not break HTML IDs of other forms.
        if ($form_state['submitted'] && !form_get_errors() && !$form_state['rebuild']) {
            // Execute form submit handlers.
            form_execute_handlers('submit', $form, $form_state);
            // We'll clear out the cached copies of the form and its stored data
            // here, as we've finished with them. The in-memory copies are still
            // here, though.
            if (!variable_get('cache', 0) && !empty($form_state['values']['form_build_id'])) {
                cache_clear_all('form_' . $form_state['values']['form_build_id'], 'cache_form');
                cache_clear_all('form_state_' . $form_state['values']['form_build_id'], 'cache_form');
            // If batches were set in the submit handlers, we process them now,
            // possibly ending execution. We make sure we do not react to the batch
            // that is already being processed (if a batch operation performs a
            // drupal_form_submit).
            if (($batch =& batch_get()) && !isset($batch['current_set'])) {
                // Store $form_state information in the batch definition.
                // We need the full $form_state when either:
                // - Some submit handlers were saved to be called during batch
                //   processing. See form_execute_handlers().
                // - The form is multistep.
                // In other cases, we only need the information expected by
                // drupal_redirect_form().
                if ($batch['has_form_submits'] || !empty($form_state['rebuild'])) {
                    $batch['form_state'] = $form_state;
                else {
                    $batch['form_state'] = array_intersect_key($form_state, array_flip(array(
                $batch['progressive'] = !$form_state['programmed'];
                // Execution continues only for programmatic forms.
                // For 'regular' forms, we get redirected to the batch processing
                // page. Form redirection will be handled in _batch_finished(),
                // after the batch is processed.
            // Set a flag to indicate that the form has been processed and executed.
            $form_state['executed'] = TRUE;
            // Redirect the form based on values in $form_state.
        // Don't rebuild or cache form submissions invoked via drupal_form_submit().
        if (!empty($form_state['programmed'])) {
        // If $form_state['rebuild'] has been set and input has been processed
        // without validation errors, we are in a multi-step workflow that is not
        // yet complete. A new $form needs to be constructed based on the changes
        // made to $form_state during this request. Normally, a submit handler sets
        // $form_state['rebuild'] if a fully executed form requires another step.
        // However, for forms that have not been fully executed (e.g., Ajax
        // submissions triggered by non-buttons), there is no submit handler to set
        // $form_state['rebuild']. It would not make sense to redisplay the
        // identical form without an error for the user to correct, so we also
        // rebuild error-free non-executed forms, regardless of
        // $form_state['rebuild'].
        // @todo D8: Simplify this logic; considering Ajax and non-HTML front-ends,
        //   along with element-level #submit properties, it makes no sense to have
        //   divergent form execution based on whether the triggering element has
        //   #executes_submit_callback set to TRUE.
        if (($form_state['rebuild'] || !$form_state['executed']) && !form_get_errors()) {
            // Form building functions (e.g., _form_builder_handle_input_element())
            // may use $form_state['rebuild'] to determine if they are running in the
            // context of a rebuild, so ensure it is set.
            $form_state['rebuild'] = TRUE;
            $form = drupal_rebuild_form($form_id, $form_state, $form);
    // After processing the form, the form builder or a #process callback may
    // have set $form_state['cache'] to indicate that the form and form state
    // shall be cached. But the form may only be cached if the 'no_cache' property
    // is not set to TRUE. Only cache $form as it was prior to form_builder(),
    // because form_builder() must run for each request to accommodate new user
    // input. Rebuilt forms are not cached here, because drupal_rebuild_form()
    // already takes care of that.
    if (!$form_state['rebuild'] && $form_state['cache'] && empty($form_state['no_cache'])) {
        form_set_cache($form['#build_id'], $unprocessed_form, $form_state);

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.