function FormBuilder::prepareForm

Same name in other branches
  1. 9 core/lib/Drupal/Core/Form/FormBuilder.php \Drupal\Core\Form\FormBuilder::prepareForm()
  2. 8.9.x core/lib/Drupal/Core/Form/FormBuilder.php \Drupal\Core\Form\FormBuilder::prepareForm()
  3. 11.x core/lib/Drupal/Core/Form/FormBuilder.php \Drupal\Core\Form\FormBuilder::prepareForm()
3 calls to FormBuilder::prepareForm()
FormBuilder::buildForm in core/lib/Drupal/Core/Form/FormBuilder.php
FormBuilder::rebuildForm in core/lib/Drupal/Core/Form/FormBuilder.php
FormBuilder::submitForm in core/lib/Drupal/Core/Form/FormBuilder.php

File

core/lib/Drupal/Core/Form/FormBuilder.php, line 687

Class

FormBuilder
Provides form building and processing.

Namespace

Drupal\Core\Form

Code

public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
    $user = $this->currentUser();
    $form['#type'] = 'form';
    // Only update the action if it is not already set.
    if (!isset($form['#action'])) {
        // Instead of setting an actual action URL, we set the placeholder, which
        // will be replaced at the very last moment. This ensures forms with
        // dynamically generated action URLs don't have poor cacheability.
        // Use the proper API to generate the placeholder, when we have one.
        // See https://www.drupal.org/node/2562341.
        // The placeholder uses a unique string that is returned by
        // Crypt::hashBase64('Drupal\Core\Form\FormBuilder::prepareForm').
        // cspell:disable-next-line
        $placeholder = 'form_action_p_pvdeGsVG5zNF_XLGPTvYSKCf43t8qZYSwcfZl2uzM';
        $form['#attached']['placeholders'][$placeholder] = [
            '#lazy_builder' => [
                'form_builder:renderPlaceholderFormAction',
                [],
            ],
        ];
        $form['#action'] = $placeholder;
    }
    // Fix the form method, if it is 'get' in $form_state, but not in $form.
    if ($form_state->isMethodType('get') && !isset($form['#method'])) {
        $form['#method'] = 'get';
    }
    // GET forms should not use a CSRF token.
    if (isset($form['#method']) && $form['#method'] === 'get') {
        $form += [
            '#token' => FALSE,
        ];
    }
    // Generate a new #build_id for this form, if none has been set already.
    // The form_build_id is used as key to cache a particular build of the form.
    // For multi-step forms, this allows the user to go back to an earlier
    // build, make changes, and re-submit.
    // @see self::buildForm()
    // @see self::rebuildForm()
    if (!isset($form['#build_id'])) {
        $form['#build_id'] = 'form-' . Crypt::randomBytesBase64();
    }
    $form['form_build_id'] = [
        '#type' => 'hidden',
        '#value' => $form['#build_id'],
        '#id' => $form['#build_id'],
        '#name' => 'form_build_id',
        // Form processing and validation require this value. Ensure the
        // submitted form value appears literally, regardless of custom #tree
        // and #parents being set elsewhere.
'#parents' => [
            'form_build_id',
        ],
    ];
    // Add a token, based on either #token or form_id, to any form displayed to
    // authenticated users. This ensures that any submitted form was actually
    // requested previously by the user and protects against cross site request
    // forgeries.
    // This does not apply to programmatically submitted forms. Furthermore,
    // since tokens are session-bound and forms displayed to anonymous users are
    // very likely cached, we cannot assign a token for them.
    // During installation, there is no $user yet.
    // Form constructors may explicitly set #token to FALSE when cross site
    // request forgery is irrelevant to the form, such as search forms.
    if ($form_state->isProgrammed() || isset($form['#token']) && $form['#token'] === FALSE) {
        unset($form['#token']);
    }
    else {
        $form['#cache']['contexts'][] = 'user.roles:authenticated';
        if ($user && $user->isAuthenticated()) {
            // Generate a public token and placeholder based on the form ID.
            $placeholder = 'form_token_placeholder_' . Crypt::hashBase64($form_id);
            $form['#token'] = $placeholder;
            $form['form_token'] = [
                '#id' => Html::getUniqueId('edit-' . $form_id . '-form-token'),
                '#type' => 'token',
                '#default_value' => $placeholder,
                // Form processing and validation require this value. Ensure the
                // submitted form value appears literally, regardless of custom #tree
                // and #parents being set elsewhere.
'#parents' => [
                    'form_token',
                ],
                // Instead of setting an actual CSRF token, we've set the placeholder
                // in form_token's #default_value and #placeholder. These will be
                // replaced at the very last moment to ensure forms with a CSRF token
                // don't have poor cacheability.
'#attached' => [
                    'placeholders' => [
                        $placeholder => [
                            '#lazy_builder' => [
                                'form_builder:renderFormTokenPlaceholder',
                                [
                                    $placeholder,
                                ],
                            ],
                        ],
                    ],
                ],
                '#cache' => [
                    'max-age' => 0,
                ],
            ];
        }
    }
    if (isset($form_id)) {
        $form['form_id'] = [
            '#type' => 'hidden',
            '#value' => $form_id,
            '#id' => Html::getUniqueId("edit-{$form_id}"),
            // Form processing and validation require this value. Ensure the
            // submitted form value appears literally, regardless of custom #tree
            // and #parents being set elsewhere.
'#parents' => [
                'form_id',
            ],
        ];
    }
    if (!isset($form['#id'])) {
        $form['#id'] = Html::getUniqueId($form_id);
        // Provide a selector usable by JavaScript. As the ID is unique, it's not
        // possible to rely on it in JavaScript.
        $form['#attributes']['data-drupal-selector'] = Html::getId($form_id);
    }
    $form += $this->elementInfo
        ->getInfo('form');
    $form += [
        '#tree' => FALSE,
        '#parents' => [],
    ];
    $form['#validate'][] = '::validateForm';
    $form['#submit'][] = '::submitForm';
    $build_info = $form_state->getBuildInfo();
    // If no #theme has been set, automatically apply theme suggestions.
    // The form theme hook itself, which is rendered by form.html.twig,
    // is in #theme_wrappers. Therefore, the #theme function only has to care
    // for rendering the inner form elements, not the form itself.
    if (!isset($form['#theme'])) {
        $form['#theme'] = [
            $form_id,
        ];
        if (isset($build_info['base_form_id'])) {
            $form['#theme'][] = $build_info['base_form_id'];
        }
    }
    // Add the 'CACHE_MISS_IF_UNCACHEABLE_HTTP_METHOD:form' cache tag to
    // identify this render array as a form to the render cache.
    $form['#cache']['tags'][] = 'CACHE_MISS_IF_UNCACHEABLE_HTTP_METHOD:form';
    // Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
    // hook_form_FORM_ID_alter() implementations.
    $hooks = [
        'form',
    ];
    if (isset($build_info['base_form_id'])) {
        $hooks[] = 'form_' . $build_info['base_form_id'];
    }
    $hooks[] = 'form_' . $form_id;
    $this->moduleHandler
        ->alter($hooks, $form, $form_state, $form_id);
    $this->themeManager
        ->alter($hooks, $form, $form_state, $form_id);
}

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