Skip navigation
Help

drupal_build_form

7 form.inc drupal_build_form($form_id, &$form_state)

Build and process a form based on a form id.

The form may also be retrieved from the cache if the form was built in a previous page-load. The form is then passed on for processing, validation and submission if there is proper input.

Parameters

$form_id: The unique string identifying the desired form. If a function with that name exists, it is called to build the form array. Modules that need to generate the same form (or very similar forms) using different $form_ids can implement hook_forms(), which maps different $form_id values to the proper form constructor function. Examples may be found in node_forms(), search_forms(), and user_forms().

$form_state: An array which stores information about the form. This is passed as a reference so that the caller can use it to examine what in the form changed when the form submission process is complete. Furthermore, it may be used to store information related to the processed data in the form, which will persist across page requests when the 'cache' or 'rebuild' flag is set. The following parameters may be set in $form_state to affect how the form is rendered:

  • build_info: A keyed array of build information that is necessary to rebuild the form from cache when the original context may no longer be available:

    • args: An array of arguments to pass to the form builder.
    • files: An optional array defining include files that need to be loaded for building the form. Each array entry may be the path to a file or another array containing values for the parameters 'type', 'module' and 'name' as needed by module_load_include(). The files listed here are automatically loaded by form_get_cache(). By default the current menu router item's 'file' definition is added, if existent.
  • rebuild: Normally, after the entire form processing is completed and submit handlers ran, a form is considered to be done and drupal_redirect_form() will redirect the user to a new page using a GET request (so a browser refresh does not re-submit the form). However, if 'rebuild' has been set to TRUE, then a new copy of the form is immediately built and sent to the browser; instead of a redirect. This is used for multi-step forms, such as wizards and confirmation forms. Normally, $form_state['rebuild'] is set by a submit handler, since it is usually logic within a submit handler that determines whether a form is done or requires another step. However, a validation handler may already set $form_state['rebuild'] to cause the form processing to bypass submit handlers and rebuild the form instead, even if there are no validation errors.
  • input: An array of input that corresponds to $_POST or $_GET, depending on the 'method' chosen (see below).
  • method: The HTTP form method to use for finding the input for this form. May be 'post' or 'get'. Defaults to 'post'. Note that 'get' method forms do not use form ids so are always considered to be submitted, which can have unexpected effects. The 'get' method should only be used on forms that do not change data, as that is exclusively the domain of post.
  • no_redirect: If set to TRUE the form will NOT perform a drupal_goto(), even if 'redirect' is set.
  • cache: If set to TRUE the original, unprocessed form structure will be cached, which allows to rebuild the entire form from cache.
  • no_cache: If set to TRUE the form will NOT be cached, even if 'cache' is set.
  • always_process: If TRUE and the method is GET, a form_id is not necessary. This should only be used on RESTful GET forms that do NOT write data, as this could lead to security issues. It is useful so that searches do not need to have a form_id in their query arguments to trigger the search.
  • must_validate: Ordinarily, a form is only validated once but there are times when a form is resubmitted internally and should be validated again. Setting this to TRUE will force that to happen. This is most likely to occur during AHAH or Ajax operations.
  • temporary: An array holding temporary data accessible during the current page request only. It may be used to temporary save any data that doesn't need to or shouldn't be cached during the whole form workflow, e.g. data that needs to be accessed during the current form build process only.
  • wrapper_callback: Modules that wish to pre-populate certain forms with common elements, such as back/next/save buttons in multi-step form wizards, may define a form builder function name that returns a form structure, which is passed on to the actual form builder function. Such implementations may either define the 'wrapper_callback' via hook_forms() or have to invoke drupal_build_form() (instead of drupal_get_form()) on their own in a custom menu callback to prepare $form_state accordingly.

Further $form_state properties controlling the redirection behavior after form submission may be found in drupal_redirect_form().

Return value

The rendered form. This function may also perform a redirect and hence may not return at all, depending upon the $form_state flags that were set.

See also

drupal_redirect_form()

Related topics

4 calls to drupal_build_form()

File

drupal/includes/form.inc, line 280

Code

function drupal_build_form($form_id, &$form_state) {
  // Ensure some defaults; if already set they will not be overridden.
  $form_state += form_state_defaults();

  if (!isset($form_state['input'])) {
    $form_state['input'] = $form_state['method'] == 'get' ? $_GET : $_POST;
  }

  if (isset($_SESSION['batch_form_state'])) {
    // We've been redirected here after a batch processing. The form has
    // already been processed, but needs to be rebuilt. See _batch_finished().
    $form_state = $_SESSION['batch_form_state'];
    unset($_SESSION['batch_form_state']);
    return drupal_rebuild_form($form_id, $form_state);
  }

  // If the incoming input contains a form_build_id, we'll check the cache for a
  // copy of the form in question. If it's there, we don't have to rebuild the
  // form to proceed. In addition, if there is stored form_state data from a
  // previous step, we'll retrieve it so it can be passed on to the form
  // processing code.
  $check_cache = isset($form_state['input']['form_id']) && $form_state['input']['form_id'] == $form_id && !empty($form_state['input']['form_build_id']);
  if ($check_cache) {
    $form = form_get_cache($form_state['input']['form_build_id'], $form_state);
  }

  // If the previous bit of code didn't result in a populated $form object, we
  // are hitting the form for the first time and we need to build it from
  // scratch.
  if (!isset($form)) {
    // If we attempted to serve the form from cache, uncacheable $form_state
    // keys need to be removed after retrieving and preparing the form, except
    // any that were already set prior to retrieving the form.
    if ($check_cache) {
      $form_state_before_retrieval = $form_state;
    }

    $form = drupal_retrieve_form($form_id, $form_state);
    drupal_prepare_form($form_id, $form, $form_state);

    // form_set_cache() removes uncacheable $form_state keys defined in
    // form_state_keys_no_cache() in order for multi-step forms to work
    // properly. This means that form processing logic for single-step forms
    // using $form_state['cache'] may depend on data stored in those keys
    // during drupal_retrieve_form()/drupal_prepare_form(), but form
    // processing should not depend on whether the form is cached or not, so
    // $form_state is adjusted to match what it would be after a
    // form_set_cache()/form_get_cache() sequence. These exceptions are
    // allowed to survive here:
    // - always_process: Does not make sense in conjunction with form caching
//   in the first place, since passing form_build_id as a GET parameter is
//   not desired.
    // - temporary: Any assigned data is expected to survives within the same
//   page request.
    if ($check_cache) {
      $uncacheable_keys = array_flip(array_diff(form_state_keys_no_cache(), array('always_process', 'temporary')));
      $form_state = array_diff_key($form_state, $uncacheable_keys);
      $form_state += $form_state_before_retrieval;
    }
  }

  // Now that we have a constructed form, process it. This is where:
  // - Element #process functions get called to further refine $form.
  // - User input, if any, gets incorporated in the #value property of the
//   corresponding elements and into $form_state['values'].
  // - Validation and submission handlers are called.
  // - If this submission is part of a multistep workflow, the form is rebuilt
//   to contain the information of the next step.
  // - If necessary, the form and form state are cached or re-cached, so that
//   appropriate information persists to the next page request.
  // All of the handlers in the pipeline receive $form_state by reference and
  // can use it to know or update information about the state of the form.
  drupal_process_form($form_id, $form, $form_state);

  // If this was a successful submission of a single-step form or the last step
  // of a multi-step form, then drupal_process_form() issued a redirect to
  // another page, or back to this page, but as a new request. Therefore, if
  // we're here, it means that this is either a form being viewed initially
  // before any user input, or there was a validation error requiring the form
  // to be re-displayed, or we're in a multi-step workflow and need to display
  // the form's next step. In any case, we have what we need in $form, and can
  // return it for rendering.
  return $form;
}