In drupal 7 the drupal_render() function takes a single render array argument.
If a render array is not passed in warnings will be emitted.
In our case the collection module it passing a render array with a child element
set to 1 instead of a nested child array. This only happens on some collections
but its really hard to figure out whats happening due to the sheer size of
the render array.
Nothing is wrong with the html that is emitted but 12 warning messages get generated.
To reproduce the issue:
Call the drupal_render() function in drupal 7 and pass in a
scaler or object instead of the expected render array.
Expected behavior:
The drupal_render() function should act in the same way as it does when an empty render array is passed in
or a render array with no '#access' element is passed in.
That is return '' (empty string).
What happened instead:
Three warning messages are emitted.
Warning: Cannot use a scalar value as an array in drupal_render() (line 6119 of /srv/bindings/4ab358cd87db416f8ab8088ddf37708d/code/includes/common.inc).
Warning: Invalid argument supplied for foreach() in element_children() (line 6607 of /srv/bindings/4ab358cd87db416f8ab8088ddf37708d/code/includes/common.inc).
Warning: Cannot use a scalar value as an array in drupal_render() (line 6064 of /srv/bindings/4ab358cd87db416f8ab8088ddf37708d/code/includes/common.inc).
No one would want the code in the drupal_render() to actually execute without a valid render array but that's what happens.
in the common.inc file. In the drupal_render() function the first line looks like this.
if (empty($elements) || (isset($elements['#access']) && !$elements['#access'])) {
return '';
}
The patch for this the first line should look like this.
if (empty($elements) || !is_array($elements) || (isset($elements['#access']) && !$elements['#access'])) {
return '';
}
Adds a test !is_array($elements) to make sure the render array is valid.
patch
function drupal_render(&$elements) {
// Early-return nothing if user does not have access.
- if (empty($elements) || (isset($elements['#access']) && !$elements['#access'])) {
+ if (empty($elements) || !is_array($elements) || (isset($elements['#access']) && !$elements['#access'])) {
return '';
}
| Comment | File | Size | Author |
|---|---|---|---|
| #39 | 2884171-39.patch | 1.05 KB | mcdruid |
| #39 | 2884171-39_test_only.patch | 559 bytes | mcdruid |
| #28 | drupal_render_array_checks-2884171-28-D7.patch | 519 bytes | darrenwh |
| #17 | drupal_render_array_checks-2884171-17-D7.65-do-not-test.patch | 519 bytes | beram |
Comments
Comment #2
seth snyder commentedAttaching a patch file
drupal_render_array_checks-2884171-2.patch
Comment #3
seth snyder commentedComment #4
seth snyder commentedComment #5
ronino commentedThis patch is great as it fixes the errors described above for me (in a different scenario) and probably in a lot of other cases (like well-described e.g. in #2958665: Warning: Invalid argument supplied for foreach() in element_children() /includes/common.inc). Thanks!
Comment #6
_KurT_ commentedReviewed, patch is pretty small and straightforward, don't see any reasons not to proceed next.
Comment #7
darrenwh commentedRerolled patch
Comment #8
darrenwh commentedComment #9
sokru commented+1 for patch in comment #7
Comment #10
lgough commented+1 for patch in #7, works well
Fixed 'Warning: Invalid argument supplied for foreach() in element_children() includes/common.inc' warnings on Drupal 7.60
Comment #11
desammer commented+1 for patch in comment #7 as well
Comment #12
danielen commentedas beautiful as it is simple +1
Comment #13
Neo13 commented+1 RTBC
Comment #14
kobee commented+1 RTBC
Comment #15
kobee commentedPatch to be ported.
Comment #16
bserem commentedA bit late, but I just run into this problem and the patch works for me too!
+1 RTBC
Comment #17
beram commentedReroll for the latest version of 7.x and also for 7.65 in case anyone else needed it.
Comment #18
rolodmonkey commentedComment #19
mustanggb commentedSeems to do the job over here.
Comment #20
sgdev commentedNot sure I agree with this patch. Every situation I have seen this come up, it's due to some module or custom code not following the rules of how a render array should be generated.
I think it might cause more harm than good by obfuscating the root cause of a problem.
Comment #21
izmeez commented@ron_s I think comment #5 and the link provided there may explain the rationale for this issue and the need for "a bit more protection".
Comment #22
mustanggb commentedComment #23
sgdev commented@izmeez, I understand what is being said in #5 and the link to Issue #2958665. I still believe this patch covers up the root cause of why
drupal_renderhas issues in certain cases, and will make debugging more difficult.Comment #24
izmeez commented@ron_s Your point is good from a developers perspective but from an end use perspective and the number of modules with issues listed in #2958665: Warning: Invalid argument supplied for foreach() in element_children() /includes/common.inc the rationale for the fix in this issue:
makes good sense IMHO. We have been using the patch for sometime.
Comment #25
mustanggb commentedComment #26
darrenwh commented@ron_s perhaps we can add some logging if the value is not set so at least the issue is recorded
Comment #27
sgdev commented@darrenwh, I think that would be a great idea.
In fact, I just found an issue with the Metatag module's latest 7.x release that is due to a faulty patch. The reason why it was so easy to find the problem is
drupal_renderstarting logging errors to watchdog and the screen. If the patch in this issue had been applied, it would have been far more difficult to find.See the issue here: https://www.drupal.org/project/metatag/issues/3104933
Comment #28
darrenwh commentedRe-roll for 7.73
Comment #29
mustanggb commentedComment #30
izmeez commentedStill no logging added as suggested and discussed in comments #26 and #27.
Comment #31
tormiUpdating core target, thanks for the patch!
Comment #32
ressaThanks @tormi, this issue has been added to #3192080: [meta] Priorities for 2021-04-07 release of Drupal 7, and will be tracked from there, or a future "[meta] priorities for 2021-06-02 release of Drupal 7" issue. So it is probably no longer necessary to update "Drupal 7.xx target" tags in individual issues.
From #3179845: [meta] Priorities for 2020-12-02 bugfix release of Drupal 7.76 / 7.77:
Comment #33
izmeez commentedStill could use some logging as suggested in #26 and #27.
Comment #34
mcdruid commentedI think logging something useful seems like a good idea, but what would we actually log?
If $elements is not an array, would we log a message containing whatever else it is (most likely a string)? I am a bit concerned that we'd be adding unnecessary complexity to cater for all eventualities there (e.g. what if it's a not a string? what if it's a really really long string? etc..).
Just a plain message that says "elements wasn't an array" would likely not be very useful other than as a "canary in a coal mine" when other changes are deployed, per #27.
What severity would we use, and what type?
I am leaning towards just committing this extra check as it is, but if anyone wants to add a very simple logging implementation before that happens, patches welcome (but don't wait too long!).
Comment #35
izmeez commentedIn #26 @darrenwh suggested
Comment #36
mcdruid commentedRight, but I'm not sure exactly what "the value is not set" means?
There's already an early return
if (empty($elements), so I'm not sure I'd add new logging in that situation.I presume we're talking about if
$elementsis not an array?In which case, is it helpful to literally just log that fact (without e.g. trying to log some or all of what
$elementsactually did contain?)Comment #37
poker10 commentedPersonally I agree that it should be better to commit this without additional logging, because that will add more complexity to this simple fix. And mcdruid is right that the
$elementsvariable can consist of different data types, so we would need to take care of all of them.Also we know how much modules does trigger these warnings, so it should be a good idea to "babysit" that in core (as we have done in some PHP 8 issues already).
Comment #38
mcdruid commentedYup, I think we're agreed that #28 can be committed (a unit test might be nice, but it's a simple enough change that I don't think we need to block it on that).
If anyone wants to create a follow-up that adds some useful logging in specific circumstances, please feel free.
Comment #39
mcdruid commentedIn fact it was really easy to add to an existing test.
@poker10 if tests go as we expect, and you're happy you can move this back to RTBC and commit it.
Comment #41
poker10 commentedThe test looks good, thanks! And the patch passes the tests, so setting this back to RTBC.
Comment #43
poker10 commentedThanks everyone who contributed!