function FormBuilder::handleInputElement

Same name in other branches
  1. 8.9.x core/lib/Drupal/Core/Form/FormBuilder.php \Drupal\Core\Form\FormBuilder::handleInputElement()
  2. 10 core/lib/Drupal/Core/Form/FormBuilder.php \Drupal\Core\Form\FormBuilder::handleInputElement()
  3. 11.x core/lib/Drupal/Core/Form/FormBuilder.php \Drupal\Core\Form\FormBuilder::handleInputElement()

Adds the #name and #value properties of an input element before rendering.

1 call to FormBuilder::handleInputElement()
FormBuilder::doBuildForm in core/lib/Drupal/Core/Form/FormBuilder.php
Builds and processes all elements in the structured form array.

File

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

Class

FormBuilder
Provides form building and processing.

Namespace

Drupal\Core\Form

Code

protected function handleInputElement($form_id, &$element, FormStateInterface &$form_state) {
    if (!isset($element['#name'])) {
        $name = array_shift($element['#parents']);
        $element['#name'] = $name;
        if ($element['#type'] == 'file') {
            // To make it easier to handle files in file.inc, we place all
            // file fields in the 'files' array. Also, we do not support
            // nested file names.
            // @todo Remove this files prefix now?
            $element['#name'] = 'files[' . $element['#name'] . ']';
        }
        elseif (count($element['#parents'])) {
            $element['#name'] .= '[' . implode('][', $element['#parents']) . ']';
        }
        array_unshift($element['#parents'], $name);
    }
    // Setting #disabled to TRUE results in user input being ignored regardless
    // of how the element is themed or whether JavaScript is used to change the
    // control's attributes. However, it's good UI to let the user know that
    // input is not wanted for the control. HTML supports two attributes for:
    // this: http://www.w3.org/TR/html401/interact/forms.html#h-17.12. If a form
    // wants to start a control off with one of these attributes for UI
    // purposes, only, but still allow input to be processed if it's submitted,
    // it can set the desired attribute in #attributes directly rather than
    // using #disabled. However, developers should think carefully about the
    // accessibility implications of doing so: if the form expects input to be
    // enterable under some condition triggered by JavaScript, how would someone
    // who has JavaScript disabled trigger that condition? Instead, developers
    // should consider whether a multi-step form would be more appropriate
    // (#disabled can be changed from step to step). If one still decides to use
    // JavaScript to affect when a control is enabled, then it is best for
    // accessibility for the control to be enabled in the HTML, and disabled by
    // JavaScript on document ready.
    if (!empty($element['#disabled'])) {
        if (!empty($element['#allow_focus'])) {
            $element['#attributes']['readonly'] = 'readonly';
        }
        else {
            $element['#attributes']['disabled'] = 'disabled';
        }
    }
    // With JavaScript or other easy hacking, input can be submitted even for
    // elements with #access=FALSE or #disabled=TRUE. For security, these must
    // not be processed. Forms that set #disabled=TRUE on an element do not
    // expect input for the element, and even forms submitted with
    // self::submitForm() must not be able to get around this. Forms that set
    // #access=FALSE on an element usually allow access for some users, so forms
    // submitted with self::submitForm() may bypass access restriction and be
    // treated as high-privilege users instead.
    $process_input = empty($element['#disabled']) && !in_array($element['#type'], [
        'item',
        'value',
    ], TRUE) && ($form_state->isProgrammed() && $form_state->isBypassingProgrammedAccessChecks() || $form_state->isProcessingInput() && (!isset($element['#access']) || ($element['#access'] instanceof AccessResultInterface && $element['#access']->isAllowed() || $element['#access'] === TRUE)));
    // Set the element's #value property.
    if (!isset($element['#value']) && !array_key_exists('#value', $element)) {
        // @todo Once all elements are converted to plugins in
        //   https://www.drupal.org/node/2311393, rely on
        //   $element['#value_callback'] directly.
        $value_callable = !empty($element['#value_callback']) ? $element['#value_callback'] : 'form_type_' . $element['#type'] . '_value';
        if (!is_callable($value_callable)) {
            $value_callable = '\\Drupal\\Core\\Render\\Element\\FormElement::valueCallback';
        }
        if ($process_input) {
            // Get the input for the current element. NULL values in the input need
            // to be explicitly distinguished from missing input. (see below)
            $input_exists = NULL;
            $input = NestedArray::getValue($form_state->getUserInput(), $element['#parents'], $input_exists);
            // For browser-submitted forms, the submitted values do not contain
            // values for certain elements (empty multiple select, unchecked
            // checkbox). During initial form processing, we add explicit NULL
            // values for such elements in FormState::$input. When rebuilding the
            // form, we can distinguish elements having NULL input from elements
            // that were not part of the initially submitted form and can therefore
            // use default values for the latter, if required. Programmatically
            // submitted forms can submit explicit NULL values when calling
            // self::submitForm() so we do not modify FormState::$input for them.
            if (!$input_exists && !$form_state->isRebuilding() && !$form_state->isProgrammed()) {
                // Add the necessary parent keys to FormState::$input and sets the
                // element's input value to NULL.
                NestedArray::setValue($form_state->getUserInput(), $element['#parents'], NULL);
                $input_exists = TRUE;
            }
            // If we have input for the current element, assign it to the #value
            // property, optionally filtered through $value_callback.
            if ($input_exists) {
                // Skip all value callbacks except safe ones like text if the CSRF
                // token was invalid.
                if (!$form_state->hasInvalidToken() || $this->valueCallableIsSafe($value_callable)) {
                    $element['#value'] = call_user_func_array($value_callable, [
                        &$element,
                        $input,
                        &$form_state,
                    ]);
                }
                else {
                    $input = NULL;
                }
                if (!isset($element['#value']) && isset($input)) {
                    $element['#value'] = $input;
                }
            }
            // Mark all posted values for validation.
            if (isset($element['#value']) || !empty($element['#required'])) {
                $element['#needs_validation'] = TRUE;
            }
        }
        // Load defaults.
        if (!isset($element['#value'])) {
            // Call #type_value without a second argument to request default_value
            // handling.
            $element['#value'] = call_user_func_array($value_callable, [
                &$element,
                FALSE,
                &$form_state,
            ]);
            // Final catch. If we haven't set a value yet, use the explicit default
            // value. Avoid image buttons (which come with garbage value), so we
            // only get value for the button actually clicked.
            if (!isset($element['#value']) && empty($element['#has_garbage_value'])) {
                $element['#value'] = $element['#default_value'] ?? '';
            }
        }
    }
    // Determine which element (if any) triggered the submission of the form and
    // keep track of all the clickable buttons in the form for
    // \Drupal\Core\Form\FormState::cleanValues(). Enforce the same input
    // processing restrictions as above.
    if ($process_input) {
        // Detect if the element triggered the submission via Ajax.
        if ($this->elementTriggeredScriptedSubmission($element, $form_state)) {
            $form_state->setTriggeringElement($element);
        }
        // If the form was submitted by the browser rather than via Ajax, then it
        // can only have been triggered by a button, and we need to determine
        // which button within the constraints of how browsers provide this
        // information.
        if (!empty($element['#is_button'])) {
            // All buttons in the form need to be tracked for
            // \Drupal\Core\Form\FormState::cleanValues() and for the
            // self::doBuildForm() code that handles a form submission containing no
            // button information in \Drupal::request()->request.
            $buttons = $form_state->getButtons();
            $buttons[] = $element;
            $form_state->setButtons($buttons);
            if ($this->buttonWasClicked($element, $form_state)) {
                $form_state->setTriggeringElement($element);
            }
        }
    }
    // Set the element's value in $form_state->getValues(), but only, if its key
    // does not exist yet (a #value_callback may have already populated it).
    if (!NestedArray::keyExists($form_state->getValues(), $element['#parents'])) {
        $form_state->setValueForElement($element, $element['#value']);
    }
}

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