Values for checkboxes in $form_state do not match input

Created on 21 June 2011, almost 14 years ago
Updated 21 April 2024, about 1 year ago

In form.inc, form_type_checkbox_value, line 2195, 0 is returned instead of NULL for a checkbox. This creates a problem when trying to execute a valid form_state that contains an element of type 'checkboxes'. An error will occur on forms that use this element, for example the 'block_admin_configure' form.

I have saved a previously submitted form and try to submit it using drupal_form_submit later on. This form_state has input, values, etc. The complete thing. I'd say that this must be possible.

But drupal_form_submit simply overwrites the input array with the form_state['values'] array. Needless to say, this is only sensible when their formats are identical. In the case of a checkboxes-element, the net result is that 'NULL' values (not-selected checkboxes) in the input field will be overwritten by the value '0'. This is asking for trouble.

In the function 'form_type_checkboxes_value' (form.inc, line 2211) the '0' value is not recognized as a 'not set' value (check the comment block!). isset is used, and as the value is not NULL, it thinks that the 0 value is actually a chosen option and the value is taken into the '#value' array key of the element.

The trouble happens later in the function _form_validate (form.inc, line 1249), where the value 0 is not recognized as a correct 'option', and the error 'An illegal choice has been detected. Please contact the site administrator.' is set. The form will not be accepted.

My solution is to re-implement drupal_form_submit locally, but of course this is not a good solution. Some more intelligence is required in drupal_form_submit, so that form_state['values'] are correctly converted to input values. Personally, I think that the input and form_state['values'] should always have the exact same format if the input array is overwritten.

🐛 Bug report
Status

Needs review

Version

7.0 ⚰️

Component
Form 

Last updated 2 days ago

Created by

🇳🇱Netherlands bvanmeurs

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • 🇮🇹Italy apaderno Brescia, 🇮🇹

    The latest patch does not apply anymore.

  • Status changed to Needs review about 1 year ago
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update about 1 year ago
    2,179 pass
  • 🇮🇳India keshav patel

    Updated the #8 patch, the patch on #10 applies cleanly. please review.

  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update about 1 year ago
    2,094 pass, 110 fail
  • 🇮🇹Italy apaderno Brescia, 🇮🇹

    I created a MR with the same changes in the patch in comment #7.

  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update about 1 year ago
    2,094 pass, 94 fail
  • Pipeline finished with Canceled
    about 1 year ago
    Total: 1100s
    #151950
  • Pipeline finished with Failed
    about 1 year ago
    Total: 1899s
    #151963
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update about 1 year ago
    2,179 pass
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update about 1 year ago
    Patch Failed to Apply
  • Pipeline finished with Success
    about 1 year ago
    Total: 429s
    #152274
  • 🇮🇹Italy apaderno Brescia, 🇮🇹

    There is something that is not clear for me, in form_type_checkbox_value().

    function form_type_checkbox_value($element, $input = FALSE) {
      if ($input === FALSE) {
        return isset($element['#default_value']) ? $element['#default_value'] : 0;
      }
      else {
        return isset($input) ? $element['#return_value'] : 0;
      }
    }
    

    isset($input) would always return TRUE, even when form_type_checkbox_value() is called as form_type_checkbox_value($element) or form_type_checkbox_value($element, NULL).
    Given that return isset($input) ? $element['#return_value'] : 0; is only executed when $input is not FALSE, why is not that line simply return $element['#return_value'];? Eventually, if $element['#return_value'] could be not set, that line should be return isset($element['#return_value']) ? $element['#return_value'] : 0;.

  • Status changed to Needs work about 1 year ago
  • 🇮🇹Italy apaderno Brescia, 🇮🇹

    The description of the return value needs to be changed too.

    The data that will appear in $form_state['values'] for this element, or nothing to use the default.

    form_type_checkbox_value() always return a value, even if that value is 0. It is not true it can return nothing. That comment cannot be changed to or nothing to use the default either. That would contrast with the comment inside
    form_type_checkbox_value(), which contains the following sentence.

    Returning NULL from a value callback means to use the default value, which is not what is wanted when an unchecked checkbox is submitted, so we use integer 0 as the value indicating an unchecked checkbox.

    Furthermore, this comment needs to be changed too, since the code is changed.

    // Checked checkboxes are submitted with a value (possibly '0' or ''):
    // http://www.w3.org/TR/html401/interact/forms.html#successful-controls.
    // For checked checkboxes, browsers submit the string version of
    // #return_value, but we return the original #return_value. For unchecked
    // checkboxes, browsers submit nothing at all, but
    // _form_builder_handle_input_element() detects this, and calls this
    // function with $input=NULL. Returning NULL from a value callback means to
    // use the default value, which is not what is wanted when an unchecked
    // checkbox is submitted, so we use integer 0 as the value indicating an
    // unchecked checkbox. Therefore, modules must not use integer 0 as a
    // #return_value, as doing so results in the checkbox always being treated
    // as unchecked. The string '0' is allowed for #return_value. The most
    // common use-case for setting #return_value to either 0 or '0' is for the
    // first option within a 0-indexed array of checkboxes, and for this,
    // form_process_checkboxes() uses the string rather than the integer.
    

    we return the original #return_value is not always true, as $input is returned for programmatically submitted forms.

  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7
    last update about 1 year ago
    2,179 pass
  • Pipeline finished with Success
    about 1 year ago
    Total: 270s
    #152339
  • Status changed to Needs review about 1 year ago
  • 🇮🇹Italy apaderno Brescia, 🇮🇹

    I did not change the other phrase.
    Given the context in which it is used (browsers submit the string version of #return_value, but we return the original #return_value), it is not necessary to say that also $input could be returned.

Production build 0.71.5 2024