Problem/Motivation
When trying to render something with drupal_render_root() while already being within such a call, an exception is thrown.
While that should generally be avoided and anyone who can should just return render arrays, that is not always possible/correct.
Two examples:
1. Tokens
It is impossible to return a render array within the token system (at the moment), so anything that needs to be rendered within hook_tokens() has no way to do it properly and needs to render things manually.
This can also happen indirectly, for example in node_tokens(), $node->body->processed is called, which calls check_markup(), which calls drupal_render_root().
2. Rendering something to be sent as an e-mail (r something else)
Anything that is rendered and then *not* part of the returned html page must be rendered separately and nested assets must not bubble up to the main page (For example a html mail with it's own CSS)
Both those things are currently impossible and when called while being rendered, throw an exception right now.
I think this makes this critical.
Proposed resolution
Add a new method that allows to render something of the current render stack (but in a new, separate render stack).
Remaining tasks
User interface changes
API changes
Comment | File | Size | Author |
---|---|---|---|
#13 | renderer_render_plain-2382503-13.patch | 15.69 KB | Wim Leers |
Comments
Comment #1
Wim LeersA solution, based on an extensive IRC discussion between Berdir, Fabianx, catch and I.
(Reusing parts of #2364381: Exception thrown in drupal_render() causes an exception during the rendering of exceptions — if this lands first, we'll be able to reduce that other patch to a single hunk; if the other lands first, then this will be reduced to three hunks.)
Comment #2
Fabianx CreditAttribution: Fabianx commentedRTBC - if tests pass, the patch looks great.
Except that we also need tests for the new functionality ...
Comment #4
Wim LeersSame failure as #2364381: Exception thrown in drupal_render() causes an exception during the rendering of exceptions, which I've been totally unable to find the root cause of :(
Comment #5
BerdirSounds like I've got myself a challenge :)
Comment #6
Fabianx CreditAttribution: Fabianx commentedMy theory is that the renderStack needs to be a synthetic service to keep the state when ConfigUIImportTest rebuilds the container.
I _think_ (and am researching that currently) that a static in a class function is working like a static in a function and global and not per instance ...
That is the theory at least.
Thanks for taking the challenge, Berdir!
Comment #7
Fabianx CreditAttribution: Fabianx commentedMy theory is correct:
http://php.net/manual/en/language.variables.scope.php#59676
"Static variables in a function are shared across all instances of that class :)."
That means the stack at the moment is kinda "synthetic".
So we can make the service synthetic or push the stack to a static var for now. (or at least re-initialize it from a static:: var when the stack is NULL).
Comment #8
Wim LeersWow.
Fabianx++
Fabianx++
Fabianx++
Fabianx++
Fabianx++
:)
Making the service synthetic causes this:
So I went with the other option: a static
$stack
property.I can't get
ConfigImportAllTest
to pass locally, butConfigImportUITest
now does pass. Let's see what testbot says.Comment #9
Wim LeersNow with tests.
Comment #10
Fabianx CreditAttribution: Fabianx commentedRTBC, looks great!
Comment #11
alexpottI'm not sure that this needs to be public. Discussed with @Wim Leers in IRC and he agreed.
Do you not mean to set a $message here and use in the $this->fail()/$this->pass() later? Same with subsequent assertions in this test... Nice tests though :)
Comment #12
Fabianx CreditAttribution: Fabianx commented1. resetStack will need to be public for things where the ExceptionSubscriber will need to reset the stack before displaying the Bare HTML Page.
But we can make it protected first and then back public in the other issue. I am fine either way.
Comment #13
Wim Leers#12: I thought the same, but then I realized the other issue could also just use
renderPlain()
… :) In case that I'm wrongly thinking that'll work, we can indeed just make it public again.#11.2: Heh, thanks :) I specifically chose not to do that, I find that having too many variables in tests easily becomes cumbersome; just hardcoding strings makes tests more legible, because you don't need to look up what that variable contains. I left that as-is, hope that's okay.
Comment #14
alexpottThis issue addresses a critical bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 91b4829 and pushed to 8.0.x. Thanks!