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.
Comment | File | Size | Author |
---|
Issue fork drupal-3143096
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:
Comments
Comment #2
jedihe CreditAttribution: jedihe commentedComment #3
jyotimishra-developer CreditAttribution: jyotimishra-developer commentedComment #4
jyotimishra-developer CreditAttribution: jyotimishra-developer at Srijan | A Material+ Company commentedComment #5
Pooja Ganjage CreditAttribution: Pooja Ganjage commentedHii,
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
Comment #6
jedihe CreditAttribution: jedihe commentedThanks @Pooja Ganjage! just fixed the typo. Also added a note, as it's pseudo-code, an actual working version requires some more work.
Comment #7
Wim LeersLooks like a fine DX improvement to me! 👍🥳🙏
Comment #8
shetpooja04 CreditAttribution: shetpooja04 at QED42 commentedComment #9
shetpooja04 CreditAttribution: shetpooja04 at QED42 commentedHi,
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.
Comment #10
andypostLooks better to check special private method
!isString()
because PHP 8 bringing interface https://github.com/php/php-src/pull/5083Comment #11
andypostProper branch, other naming could be
validateResponse()
Comment #12
jyotimishra-developer CreditAttribution: jyotimishra-developer at Srijan | A Material+ Company commentedupdated the comment and rolled the patch
Comment #13
Pooja Ganjage CreditAttribution: Pooja Ganjage at Asentech LLC commentedComment #14
jedihe CreditAttribution: jedihe as a volunteer commentedWill try to improve patch a bit and add a test.
Comment #15
jedihe CreditAttribution: jedihe as a volunteer commentedJust checking that the returned value is an array should be enough, I think. Added a test.
Comment #18
mradcliffeDo we need to pass this through XSS filter?
Nit: We should have a full sentence for a method in addition to the annotations.
Nit: Reword this sentence.
"Returns invalid renderable to #lazy_builder callback."
Comment #19
mradcliffeFixing these up is relatively straightforward so I added the Novice tag.
Comment #20
jedihe CreditAttribution: jedihe as a volunteer commentedAddressed the feedback from #18, plus expanded the fix (+tests) to properly consider array and closure callbacks.
Comment #21
jedihe CreditAttribution: jedihe as a volunteer commentedFix coding standards.
Comment #22
jedihe CreditAttribution: jedihe as a volunteer commentedCoding standards fix, hopefully for real this time.
Comment #24
jedihe CreditAttribution: jedihe as a volunteer commentedReady for review!
Comment #25
joachim CreditAttribution: joachim as a volunteer commentedLGTM!
Comment #26
alexpottOne minor nit otherwise this looks pretty helpful.
Exception text is filtered if it is output to a browser. See \Drupal\Core\EventSubscriber\FinalExceptionSubscriber::onException().
Comment #27
jedihe CreditAttribution: jedihe as a volunteer commentedThanks @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).
Comment #28
joachim CreditAttribution: joachim as a volunteer commentedComment #29
catchThis 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.
Comment #30
alexpott@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?
Comment #31
catchAh good point if it's already a fatal error then #29 is moot, one fatal to a better one is fine.
Comment #34
alexpottCommitted and pushed e9a53cb2b3 to 9.3.x and eebe864522 to 9.2.x. Thanks!
Comment #40
alexpottAfter 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:
Comment #42
phenaproximaAlright; gave it a shot. I really like the change that this issue makes, so hopefully we can get this back in soon :)
Comment #43
alexpott@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
Comment #44
phenaproximaFixed!
Comment #45
tim.plunkettFirst point is not important. The second one is. After that I think this is ready to go
Comment #46
phenaproximaCredited @tim.plunkett for review.
Comment #47
phenaproximaI think this is ready for another look.
Comment #48
tim.plunkettOkay I think that addresses all my concerns. Thanks @phenaproxima!
Comment #49
alexpottCommitted and pushed a3c114e65f to 9.3.x and 9b94f12237 to 9.2.x. Thanks!