Documentation location/URL
https://api.drupal.org/api/drupal/developer!topics!forms_api_reference.h...
Problem/Motivation
The 'values' section for #empty_value
contains the following text.
Values: Defaults to empty string. Can be anything except
NULL
.
anything except NULL
would mean that #empty_value
could also be set to TRUE
, an object, or an array.
Setting #empty_value
to TRUE when the form element is defined as in the following code would also have a side effect.
$form['selected'] = array(
'#type' => 'select',
'#title' => t('Selected'),
'#options' => array(
0 => t('No'),
1 => t('Yes'),
),
'#empty_value' => TRUE,
'#default_value' => $category['selected'],
'#description' => t('Set this to <em>Yes</em> if you would like this category to be selected by default.'),
);
With that code, #options
would be changed to array(0 => t('No'), 1 => t('- None -'))
. This happens because form_process_select()
contains the following code.
// The empty option is prepended to #options and purposively not merged
// to prevent another option in #options mistakenly using the same value
// as #empty_value.
$empty_option = array(
$element['#empty_value'] => $element['#empty_option'],
);
$element['#options'] = $empty_option + $element['#options'];
In PHP, settings $array[TRUE]
or $array['1'] is like setting $array[1]
. This means that the option whose value is 1 will be replaced by the empty option, when the empty option value is TRUE
.
The documentation should not say that #empty_value
can be any value and make clear that #empty_value
can overrides a value given for #options
.
[The issue summary have been updated to make more explicit what the issue is.]
Original report
The 'values' section for #empty_value states "Values: Defaults to empty string. Can be anything except NULL." -- this is incorrect, if it is set to the value TRUE then the empty option will overwrite $options[1] preventing the original value in $options[1] from being selected.
Comments
Comment #1
jhodgdonThe Form API reference is in the Documentation project Git repository, so moving to that project.
Comment #1.0
jhodgdoninstructions inadvertently left in description.
Comment #2
apadernoComment #3
apadernoComment #4
apadernoform_process_select()
gives the following description for#empty_value
.It is different from the description given in #empty_value, but it does not say anything about setting
#empty_value
toTRUE
.Even the code in
form_process_select()
does not seem to handle#empty_value
in a particular way, when it is equal toTRUE
.Comment #5
apadernoThe reason for which #empty_value say it can be any value except
NULL
is thatform_process_select()
checks its value withisset($element['#empty_value'])
; for aNULL
value,isset()
returnsFALSE
.Given that
#empty_value
is used as array index, saying it can be any value is too broad. It can be any value PHP allows for an array index.Comment #6
tunicI think the problem is in:
Let's say we have this array of options ($element['#options']) like this:
If #required is TRUE and #empty_value is TRUE execution will reach that line, setting $empty_option to:
TRUE is equivalent to 1, so when execution reaches the line:
... the final value of $element['#options'] will be:
The array value with index 1 is overwritten.
But the problem is not #empty_value is TRUE but #empty_value having a value that's already used in the $element['#options'] array.
So I would say that #empty_value can be any valid PHP array index value but should not collide with any already defined index value of $element['#options'] because it will be overwritten.
I have deduced this form the code, I didn't test it manually using a real Drupal site.
In Drupal 10 almost the same code is present. I'm wondering if Form API should check if the #empty_value is already in use and trigger an error, or fail silently. I think the second option will lead to obscure bugs hard to debug.
Comment #7
apaderno@tunic Thank you! Your explanation makes clear what the exact issue is. I tried the following code on PHP 7.4; it prints out 100.
Even replacing the
$array[TRUE] = 100;
line with$array['1'] = 100;
would get the same value printed.Comment #8
apadernoComment #9
tunicGood! What would be the next steps? I'm not sure where is the code used to generate https://api.drupal.org/api/drupal/developer!topics!forms_api_reference.h.... Or just content in D.O?
Additionally, I was wondering about triggering a warning is #empty_value is already used in #options, but it seems Logger is not available in the class.
But I think it would be great to do somethingf when the problem is detected.
Comment #10
apadernoThe file used for the Drupal 7 Form API Reference page is the forms_api_reference.html file in this very project repository.
The documentation page for
#empty_value
is present in Drupal 10 too, even if the documentation page is a different one. (SeeSelect
.)The documentation page should be first changed on Drupal 10, if it needs to be changed, and then the Drupal 7 documentation page should be changed to match the Drupal 10 documentation page (at least for the
#empty_value
part).Can be anything except
NULL
. in the Drupal 7 documentation should at least be removed, or changed.Comment #11
tunicCreated issue in Drupal core to fix the documentation: #3361695: Fix the documentation for #empty_value on Drupal\Core\Render\Element\Select.