- 🇮🇹Italy apaderno Brescia, 🇮🇹
form_process_select()
gives the following description for#empty_value
.#empty_value: (optional) The value for the first default option, which is used to determine whether the user submitted a value or not.
- If #required is TRUE, this defaults to '' (an empty string).
- If #required is not TRUE and this value isn't set, then no extra option is added to the select control, leaving the control in a slightly illogical state, because there's no way for the user to select nothing, since all user agents automatically preselect the first available option. But people are used to this being the behavior of select controls. @todo Address the above issue in Drupal 8.
- If #required is not TRUE and this value is set (most commonly to an empty string), then an extra option (see #empty_option above) representing a "non-selection" is added with this as its 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()
seems to handle#empty_value
in a particular way, when it is equal toTRUE
.// If the element is required and there is no #default_value, then add an // empty option that will fail validation, so that the user is required to // make a choice. Also, if there's a value for #empty_value or // #empty_option, then add an option that represents emptiness. if ($required && !isset($element['#default_value']) || isset($element['#empty_value']) || isset($element['#empty_option'])) { $element += array( '#empty_value' => '', '#empty_option' => $required ? t('- Select -') : t('- None -'), ); // 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']; } }
- 🇮🇹Italy apaderno Brescia, 🇮🇹
The 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. - 🇪🇸Spain tunic Madrid
I think the problem is in:
$empty_option = array( $element['#empty_value'] => $element['#empty_option'], );
Let's say we have this array of options ($element['#options']) like this:
$element['#options'] = [ 0 => 'zero', 1 => 'one', 2 => 'two' ];
If #required is TRUE and #empty_option is TRUE execution will reach that line, setting $empty_option to:
$empty_option = [ TRUE => 'empty option string' ];
TRUE is equivalent to 1, so when execution reaches the line:
$element['#options'] = $empty_option + $element['#options']
... the final value of $element['#options'] will be:
[ 1 => "empty option string", 0 => "zero", 2 => "two", ];
The array valuw itrh 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.
- 🇮🇹Italy apaderno Brescia, 🇮🇹
@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.
$array = []; $array[true] = 100; echo $array[1], "\n";
Even replacing the
$array[true] = 100;
line with$array['1'] = 100;
would get the same value printed. - 🇪🇸Spain tunic Madrid
Good! 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.
// 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. However, do something is this condition is detected. if (array_key_exists($element['#empty_value'], $element['#options'])) { // Do something. } $empty_option = [$element['#empty_value'] => $element['#empty_option']]; $element['#options'] = $empty_option + $element['#options'];
- 🇮🇹Italy apaderno Brescia, 🇮🇹
The 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. - 🇪🇸Spain tunic Madrid
Created issue in Drupal core to fix the documentation: 📌 Fix the documentation for #empty_value on Drupal\Core\Render\Element\Select Needs work .