function drupal_get_form

Retrieves a form from a constructor function, or from the cache if
the form was built in a previous page-load. The form is then passed
on for processing, after and rendered for display if necessary.

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().

...:
Any additional arguments are passed on to the functions called by
drupal_get_form(), including the unique form constructor function.
For example, the node_edit form requires that a node object be passed
in here when it is called. These are available to implementations of
hook_form_alter() and hook_form_FORM_ID_alter() as the array
$form['#parameters'].

File

Code

functiondrupal_get_form($form_id) {
$form_state = array('storage' => NULL, 'submitted' => FALSE);
$args = func_get_args();
$cacheable = FALSE;
if (isset($_SESSION['batch_form_state'])) {
// We've been redirected here after a batch processing : the form has
// already been processed, so we grab the post-process $form_state value
// and move on to form display. See _batch_finished() function.
$form_state = $_SESSION['batch_form_state'];
unset($_SESSION['batch_form_state']);
}
else {
// If the incoming $_POST 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.
if (isset($_POST['form_id']) && $_POST['form_id'] == $form_id && !empty($_POST['form_build_id'])) {
$form = form_get_cache($_POST['form_build_id'], $form_state);
}
// If the previous bit of code didn't result in a populated $form
// object, we're hitting the form for the first time and we need
// to build it from scratch.
if (!isset($form)) {
$form_state['post'] = $_POST;
// Use a copy of the function's arguments for manipulation
$args_temp = $args;
$args_temp[0] = &$form_state;
array_unshift($args_temp, $form_id);
$form = call_user_func_array('drupal_retrieve_form', $args_temp);
$form_build_id = 'form-' . drupal_random_key();
$form['#build_id'] = $form_build_id;
drupal_prepare_form($form_id, $form, $form_state);
// Store a copy of the unprocessed form for caching and indicate that it
// is cacheable if #cache will be set.
$original_form = $form;
$cacheable = TRUE;
unset($form_state['post']);
}
$form['#post'] = $_POST;
// Now that we know we have a form, we'll process it (validating,
// submitting, and handling the results returned by its submission
// handlers. Submit handlers accumulate data in the form_state by
// altering the $form_state variable, which is passed into them by
// reference.
drupal_process_form($form_id, $form, $form_state);
if ($cacheable && !empty($form['#cache'])) {
// Caching is done past drupal_process_form so #process callbacks can
// set #cache.
form_set_cache($form_build_id, $original_form, $form_state);
}
}
// Most simple, single-step forms will be finished by this point --
// drupal_process_form() usually redirects to another page (or to
// a 'fresh' copy of the form) once processing is complete. If one
// of the form's handlers has set $form_state['redirect'] to FALSE,
// the form will simply be re-rendered with the values still in its
// fields.
//
// If $form_state['storage'] or $form_state['rebuild'] has been set
// and input has been processed, we know that we're in a complex
// multi-part process of some sort and the form's workflow is NOT
// complete. We need to construct a fresh copy of the form, passing
// in the latest $form_state in addition to any other variables passed
// into drupal_get_form().
if ((!empty($form_state['storage']) || !empty($form_state['rebuild'])) && !empty($form_state['process_input']) && !form_get_errors()) {
$form = drupal_rebuild_form($form_id, $form_state, $args);
}
// If we haven't redirected to a new location by now, we want to
// render whatever form array is currently in hand.
returndrupal_render_form($form_id, $form);
}

I lost an afternoon (and the noon) trying to figure out why my form doesn't contain $node object (when created with drupal_get_form), .
So i think this is reasonable mistake (having in mind drupal 5.x functionality) and this comment has to become a link where ever the drupal_get_form is applied on documentation. That will save afternoons from a lot of people, investigating possible (syntax) errors . I'm sure!

All of you who are learning drupal by the Pro Drupal Development from Apress beware of the mistake in the source code from the second chapter.
When you call your annotate_entry_form() function remember to use the method Scott McCabe described above:

function annotate_entry_form(&$form_state, $node) {
// Your code goes here and you will have an access to those
// $node->annotation and $node->nid values at last!
}

I'm working on submitting a patch to an existing module, and this module has a theme function that was being called, but I couldn't figure out where in the world it was being called from. I found my answer in this function.

You'll notice in the code above that drupal_get_form returns return drupal_render_form($form_id, $form);. You can track down drupal_render_form in form.inc. That function adds the #theme callback to the form array if the #theme callback is registered but not already set in the current form array. (It might be already set if the theme function was being overridden by some *other* module). In this case, however, the module I want to patch *had* registered the theme function, but had not added it to the form array yet. The #theme callback is added in this step, and so the theme function from my module gets called from drupal_render. Mystery solved.

Not sure why this isn't in the documentation yet. Anyway, due to the nature of how this function grabs arguments (func_get_args()), it is currently not possible to pass an argument by reference through this function. This means this code won't work:

Anytime this function is called (regardless of whether an array, variable or reference [using the & operator to get the address of a variable] is used), one will get this error:

warning: Parameter 2 to someForm_form() expected to be a reference, value given in /...../drupal/includes/form.inc on line 377..

There is currently no way around this little annoyance. Additionally, passing an array by value can destroy its structure (say you have numeric keys that are strings, a pass by value resets the keys and renumbers them starting at 0, destroying all keys and their relevance). Thus, a pass by reference is required to get around this issue. To do so, one has to use the $GLOBALS array. That can be done like: