4.7.x form.inc form_builder($form_id, $form)
5.x form.inc form_builder($form_id, $form)
6.x form.inc form_builder($form_id, $form, &$form_state)
7.x form.inc form_builder($form_id, &$element, &$form_state)

Walk through the structured form array, adding any required properties to each element and mapping the incoming $_POST data to the proper elements.


$form_id: A unique string identifying the form for validation, submission, theming, and hook_form_alter functions.

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

$form_state: A keyed array containing the current state of the form. In this context, it is used to accumulate information about which button was clicked when the form was submitted, as well as the sanitized $_POST data.

Related topics

3 calls to form_builder()
book_form_update in modules/book/book.pages.inc
Renders a new parent page select element when the book selection changes.
drupal_process_form in includes/form.inc
This function is the heart of form API. The form gets built, validated and in appropriate cases, submitted.
upload_js in modules/upload/upload.module
Menu-callback for JavaScript-based uploads.


includes/form.inc, line 917


function form_builder($form_id, $form, &$form_state) {
  static $complete_form, $cache;

  // Initialize as unprocessed.
  $form['#processed'] = FALSE;

  // Use element defaults.
  if (!empty($form['#type']) && ($info = _element_info($form['#type']))) {

    // Overlay $info onto $form, retaining preexisting keys in $form.
    $form += $info;

  // Special handling if we're on the top level form element.
  if (isset($form['#type']) && $form['#type'] == 'form') {
    $cache = NULL;
    $complete_form = $form;
    if (!empty($form['#programmed'])) {
      $form_state['submitted'] = TRUE;
    else {

      // If the session token was set by drupal_prepare_form(), ensure that it
      // matches the current user's session before processing input.
      if (isset($form['#token']) && isset($form['#post']) && (isset($form['#post']['form_id']) && $form['#post']['form_id'] == $form_id)) {
        $form_state['invalid_token'] = FALSE;
        if (empty($form['#post']['form_token']) || !drupal_valid_token($form['#post']['form_token'], $form['#token'])) {

          // Setting this error will cause the form to fail validation.
          form_set_error('form_token', t('Validation error, please try again. If this error persists, please contact the site administrator.'));

          // This value is checked in _form_builder_handle_input_element().
          $form_state['invalid_token'] = TRUE;

          // Make sure file uploads do not get processed.
          $_FILES = array();
  if (isset($form['#input']) && $form['#input']) {
    _form_builder_handle_input_element($form_id, $form, $form_state, $complete_form);
  $form['#defaults_loaded'] = TRUE;

  // We start off assuming all form elements are in the correct order.
  $form['#sorted'] = TRUE;

  // Recurse through all child elements.
  $count = 0;
  foreach (element_children($form) as $key) {
    $form[$key]['#post'] = $form['#post'];
    $form[$key]['#programmed'] = $form['#programmed'];

    // Don't squash an existing tree value.
    if (!isset($form[$key]['#tree'])) {
      $form[$key]['#tree'] = $form['#tree'];

    // Deny access to child elements if parent is denied.
    if (isset($form['#access']) && !$form['#access']) {
      $form[$key]['#access'] = FALSE;

    // Don't squash existing parents value.
    if (!isset($form[$key]['#parents'])) {

      // Check to see if a tree of child elements is present. If so,
      // continue down the tree if required.
      $form[$key]['#parents'] = $form[$key]['#tree'] && $form['#tree'] ? array_merge($form['#parents'], array(
      )) : array(
      $array_parents = isset($form['#array_parents']) ? $form['#array_parents'] : array();
      $array_parents[] = $key;
      $form[$key]['#array_parents'] = $array_parents;

    // Assign a decimal placeholder weight to preserve original array order.
    if (!isset($form[$key]['#weight'])) {
      $form[$key]['#weight'] = $count / 1000;
    else {

      // If one of the child elements has a weight then we will need to sort
      // later.
    $form[$key] = form_builder($form_id, $form[$key], $form_state);

  // The #after_build flag allows any piece of a form to be altered
  // after normal input parsing has been completed.
  if (isset($form['#after_build']) && !isset($form['#after_build_done'])) {
    foreach ($form['#after_build'] as $function) {
      $form = $function($form, $form_state);
      $form['#after_build_done'] = TRUE;

  // Now that we've processed everything, we can go back to handle the funky
  // Internet Explorer button-click scenario.
  _form_builder_ie_cleanup($form, $form_state);

  // We shoud keep the buttons array until the IE clean up function
  // has recognized the submit button so the form has been marked
  // as submitted. If we already know which button was submitted,
  // we don't need the array.
  if (!empty($form_state['submitted'])) {

  // If some callback set #cache, we need to flip a static flag so later it
  // can be found.
  if (!empty($form['#cache'])) {
    $cache = $form['#cache'];

  // We are on the top form, we can copy back #cache if it's set.
  if (isset($form['#type']) && $form['#type'] == 'form' && isset($cache)) {
    $form['#cache'] = TRUE;
  return $form;