Problem/Motivation

#lazy_builder callbacks may return data that is not proper for parsing cacheable metadata from (e.g. NULL). Additionally, the stack trace for the type error caused by that (CacheableMetadata::createFromRenderArray() requires $build to be an array) provides very little (if any) details.

Proposed resolution

Detect non-array or non-renderable-array return value and throw an exception with useful info. Something like this should do the job:

// Pseudo-code
if (!is_array($new_elements)) {
  throw new \LogicException("#lazy_builder callbacks must return a valid renderable array, got $value from $callback.");
}

Which can be added right after the ->doCallback() call (line 353 in current 8.9.x).

Remaining tasks

  • Identify a proper way to check if $new_elements is not a renderable array, in this particular context. Just a !is_array() check should be enough.

User interface changes

None.

API changes

None.

Data model changes

None.

Release notes snippet

[DX]: #lazy_builder callbacks returning invalid renderable array will now throw an exception with useful debug details.

CommentFileSizeAuthor
#27 3143096-lazybuilder-non-array-return-27--interdiff.patch833 bytesjedihe
#27 3143096-lazybuilder-non-array-return-27.patch4.68 KBjedihe
#22 3143096-lazybuilder-non-array-return-22--interdiff.patch852 bytesjedihe
#22 3143096-lazybuilder-non-array-return-22.patch4.71 KBjedihe
#22 3143096-lazybuilder-non-array-return-22--tests-only.patch3.24 KBjedihe
#21 3143096-lazybuilder-non-array-return-21--interdiff.patch833 bytesjedihe
#21 3143096-lazybuilder-non-array-return-21.patch4.68 KBjedihe
#21 3143096-lazybuilder-non-array-return-21--tests-only.patch3.22 KBjedihe
#20 3143096-lazybuilder-non-array-return-20--interdiff.patch4.08 KBjedihe
#20 3143096-lazybuilder-non-array-return-20.patch4.67 KBjedihe
#20 3143096-lazybuilder-non-array-return-20--tests-only.patch3.21 KBjedihe
#15 3143096-lazybuilder-non-array-return-15--interdiff.patch3.24 KBjedihe
#15 3143096-lazybuilder-non-array-return-15.patch3.06 KBjedihe
#15 3143096-lazybuilder-non-array-return-15--tests-only.patch1.97 KBjedihe
#12 interdiff.txt1.01 KBjyotimishra-developer
#12 3143096-12.patch1.01 KBjyotimishra-developer
#9 lazybuilder.png286.09 KBshetpooja04
#9 lazybuilderarrayexception-3143096-9.patch958 bytesshetpooja04

Issue fork drupal-3143096

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

jedihe created an issue. See original summary.

jedihe’s picture

Issue summary: View changes
jyotimishra-developer’s picture

Assigned: Unassigned » jyotimishra-developer
jyotimishra-developer’s picture

Pooja Ganjage’s picture

Hii,

This is Pooja Ganjage, I am commenting over the issue,
There is an error syntax in code, put closed parenthesis of if statement, this is below correct code.
if(!is_array($new_elements)) {
throw new \LogicException("#lazy_builder callbacks must return a valid renderable array, got $value from $callback.");
}

Thanks

jedihe’s picture

Issue summary: View changes

Thanks @Pooja Ganjage! just fixed the typo. Also added a note, as it's pseudo-code, an actual working version requires some more work.

Wim Leers’s picture

Looks like a fine DX improvement to me! 👍🥳🙏

shetpooja04’s picture

shetpooja04’s picture

Assigned: shetpooja04 » Unassigned
Status: Active » Needs review
FileSize
958 bytes
286.09 KB

Hi,

I have implemented the code changes as per above suggestion.

I have also attached the screenshot showing the exception, if callback does not return a renderable array.

andypost’s picture

Looks better to check special private method !isString() because PHP 8 bringing interface https://github.com/php/php-src/pull/5083

andypost’s picture

Version: 8.9.x-dev » 9.1.x-dev

Proper branch, other naming could be validateResponse()

jyotimishra-developer’s picture

updated the comment and rolled the patch

Pooja Ganjage’s picture

jedihe’s picture

Assigned: Unassigned » jedihe
Status: Needs review » Needs work

Will try to improve patch a bit and add a test.

jedihe’s picture

Just checking that the returned value is an array should be enough, I think. Added a test.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

mradcliffe’s picture

Status: Needs review » Needs work
  1. +++ b/core/lib/Drupal/Core/Render/Renderer.php
    @@ -353,6 +353,12 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
    +        throw new \LogicException("#lazy_builder callbacks must return a valid renderable array, got $wrong_type from " . $elements['#lazy_builder'][0]);
    

    Do we need to pass this through XSS filter?

  2. +++ b/core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php
    @@ -962,6 +962,19 @@ public function testCreatePlaceholderPropertyWithoutLazyBuilder() {
    +   * @covers ::render
    

    Nit: We should have a full sentence for a method in addition to the annotations.

  3. +++ b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php
    @@ -314,11 +314,21 @@ public static function callbackTagCurrentTemperature($animal) {
    +   * #lazy_builder callback; returns invalid renderable.
    

    Nit: Reword this sentence.

    "Returns invalid renderable to #lazy_builder callback."

mradcliffe’s picture

Issue tags: +NorthAmerica2021, +Novice

Fixing these up is relatively straightforward so I added the Novice tag.

jedihe’s picture

jedihe’s picture

jedihe’s picture

Ready for review!

joachim’s picture

Status: Needs review » Reviewed & tested by the community

LGTM!

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

One minor nit otherwise this looks pretty helpful.

+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -353,6 +353,23 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
+        throw new \LogicException("#lazy_builder callbacks must return a valid renderable array, got $wrong_type from " . Xss::filter($callable_name));

Exception text is filtered if it is output to a browser. See \Drupal\Core\EventSubscriber\FinalExceptionSubscriber::onException().

jedihe’s picture

Thanks @alexpott! patch updated according #26.

No tests-only patch, as this change is a no-op from the POV of tests (so, tests-only patch and results are the same as #22).

joachim’s picture

Status: Needs review » Reviewed & tested by the community
catch’s picture

This looks helpful, however I'm wondering what happens if some buggy code is already on production (i.e. maybe it only returns FALSE occasionally) - this will convert a PHP error to an exception. Maybe that is OK but maybe it's disruptive.

One possible option would be to trigger_error('....', E_USER_WARNING) the helpful error message, and change to an exception in Drupal 10, but I'm not sure if that's being overly careful for what is already a bug.

alexpott’s picture

@catch isn't the fact we hit a type error mean that we're replacing a fatal with an exception - so there's no really any disruption. In both cases execution is going to stop and you're going to a get 500?

catch’s picture

Ah good point if it's already a fatal error then #29 is moot, one fatal to a better one is fine.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

The last submitted patch, 27: 3143096-lazybuilder-non-array-return-27.patch, failed testing. View results

alexpott’s picture

Version: 9.3.x-dev » 9.2.x-dev
Status: Reviewed & tested by the community » Fixed

Committed and pushed e9a53cb2b3 to 9.3.x and eebe864522 to 9.2.x. Thanks!

  • alexpott committed e9a53cb on 9.3.x
    Issue #3143096 by jedihe, jyotimishra123, shetpooja04, alexpott,...

  • alexpott committed eebe864 on 9.2.x
    Issue #3143096 by jedihe, jyotimishra123, shetpooja04, alexpott,...

  • alexpott committed 8dccf4b on 9.3.x
    Revert "Issue #3143096 by jedihe, jyotimishra123, shetpooja04, alexpott...

  • alexpott committed b5241ea on 9.2.x
    Revert "Issue #3143096 by jedihe, jyotimishra123, shetpooja04, alexpott...

alexpott’s picture

Version: 9.2.x-dev » 9.3.x-dev
Status: Fixed » Needs work

After reviewing #3212049: Renderer::renderRoot() can cause fatal errors when trying to describe object-based callables I decided to revert this. As @phenaproxima points out - there is a callback type that this would error on. But also in reviewing the code I realised that we ought to be using an assert here instead.

The code in Renderer should look something like this:

assert(is_array($new_elements), sprintf("#lazy_builder callbacks must return a valid renderable array, got %s from %s", gettype($new_elements), Variable::callbackToString($elements['#lazy_builder'][0]));

phenaproxima’s picture

Status: Needs work » Needs review

Alright; gave it a shot. I really like the change that this issue makes, so hopefully we can get this back in soon :)

alexpott’s picture

Status: Needs review » Needs work

@phenaproxima that's looking very nice. I think we should only add one test to RenderPlaceholdersTest - since now we should be adding a test for Variable::callableToString() to \Drupal\Tests\Component\Utility\VariableTest

phenaproxima’s picture

Status: Needs work » Needs review

Fixed!

tim.plunkett’s picture

Status: Needs review » Needs work

First point is not important. The second one is. After that I think this is ready to go

phenaproxima’s picture

Credited @tim.plunkett for review.

phenaproxima’s picture

Status: Needs work » Needs review

I think this is ready for another look.

tim.plunkett’s picture

alexpott’s picture

Version: 9.3.x-dev » 9.2.x-dev
Status: Reviewed & tested by the community » Fixed

Committed and pushed a3c114e65f to 9.3.x and 9b94f12237 to 9.2.x. Thanks!

  • alexpott committed a3c114e on 9.3.x
    Issue #3143096 by jedihe, phenaproxima, jyotimishra123, shetpooja04,...

  • alexpott committed 9b94f12 on 9.2.x
    Issue #3143096 by jedihe, phenaproxima, jyotimishra123, shetpooja04,...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.