Problem/Motivation
The theme()
doc block sums up this issue nicely:
/**
* Generates themed output.
*
* All requests for themed output must go through this function (however,
* calling the theme() function directly is strongly discouraged - see next
* paragraph). It examines the request and routes it to the appropriate
* @link themeable theme function or template @endlink, by checking the theme
* registry.
*
* Avoid calling this function directly. It is preferable to replace direct
* calls to the theme() function with calls to drupal_render() by passing a
* render array with a #theme key to drupal_render(), which in turn calls
* theme().
*...
In order to really drive home this dissuasion, we should mark the function as being "private" with an underscore.
Calling the theme()
function directly raises several issues:
- Circumvents caching.
- Circumvents defaults of types defined in
hook_element_info()
, including attached assets - Circumvents the
pre_render
andpost_render
stages. - Circumvents JavaScript states information.
theme()
should only be invoked as part of the drupal_render
process in order to convert data structures into output.
This issue has become clear as we have progressively hidden asset-adding functions like drupal_add_js
:
[#2169605]
#2171071: Rename drupal_add_library() to _drupal_add_library() and remove its uses
#2073819: [META] Remove direct calls to drupal_add_css()
#2073823: [META] Remove drupal_add_js() calls
#2168113: Add leading underscore and other discouragement to drupal_add_css() and drupal_add_js()
By making _theme()
explicitly a private Drupal function, we will also reduce confusion for module developers. This is something that until only recently was still fuzzy for me -- Do I use drupal_render()
or theme()
?
Proposed resolution
- Prefix
theme()
with an underscore to mark it as private:_theme()
- Write a very detailed Change Record that details why
theme()
should not be called directly. Draft at https://drupal.org/node/2195739 - Update documentation to provide best-practice approaches to building data structures, attaching assets and ushering them efficiently into the
drupal_render()
pipeline.
User interface changes
None
API changes
The theme()
function will be removed from the public API.
Original report by @jessebeach
Comment | File | Size | Author |
---|---|---|---|
#45 | interdiff.txt | 986 bytes | star-szr |
#45 | 2173655-45.patch | 52.16 KB | star-szr |
#44 | 2173655.44.patch | 52.16 KB | alexpott |
#44 | 37-44-patch_diff.txt | 1001 bytes | alexpott |
#40 | non-rename-changes.txt | 10.97 KB | xjm |
Comments
Comment #1
jessebeach CreditAttribution: jessebeach commentedComment #2
xjmThis is something that we need to do before beta IMO, if it's critical.
Comment #3
star-szrYes please! Thanks for opening this.
Comment #4
jessebeach CreditAttribution: jessebeach commentedComment #5
jessebeach CreditAttribution: jessebeach commentedI used the following regex to find instances of
theme()
to change to_theme()
./([\s\(]+)(theme\([\$\'a-z\)])/g
I found one false positive in
Drupal/views/Plugin/views/field/FieldPluginBase.php
. This plugin has a theme method that is a wrapper for_theme()
. By not including[A-Z]
in the portion of the regex that matches the arguments totheme()
, this false positive is avoided.Lowercase letters are matched in the arguments portion of the regex --
[a-z]
-- in order to matcharray()
which is a valid argument totheme()
when passing multiple theme hook suggestions.The regex found 134 matches across 41 files.
I insert the underscore with
$1_$2
.Pages are loading locally without issue. I'd like to give the bot a chance to pass judgement.
Comment #7
star-szrNice! Hope you don't mind, this should fix the one test fail.
Comment #8
jessebeach CreditAttribution: jessebeach commentedNice! Thanks for the test fix Cottser.
Comment #9
jessebeach CreditAttribution: jessebeach commentedAt this point, we really need input from additional Theme layer devs. I'd like to get some consensus around this change.
Comment #10
star-szrDefinitely, I will bring it up in the Twig/Theme layer call tonight! Adding to the agenda right now. http://lb.cm/twig#meetings
And adding a very related meta.
Comment #11
joelpittetI totally for this issue! RTBC++
Any theme(), render(), drupal_render() call from preprocess/alter hooks makes it tricky to autoescape and mark them as "safe strings" #1825952: Turn on twig autoescape by default. So this would help:)
Comment #12
joelpittetWe will still need to convert these _theme() calls to renderable arrays, but we can still do that in conversion, not here because some can cause trouble...
Comment #13
webchickHm. In other similar issues, such as #2073819: [META] Remove direct calls to drupal_add_css() and #2073823: [META] Remove drupal_add_js() calls, we removed the calls first, then deprecated the function. I think we should do the same here, since according to the patch there are 131 remaining calls in core. If core can't even not use this function itself, it's not really reasonable to expect contrib not to use it either, so marking it deprecated feels premature.
Also, this should be run past a core committer (probably catch) to ensure the API change is actually approved + we're comfortable adding yet another beta blocker.
Comment #14
jessebeach CreditAttribution: jessebeach commentedAssigning to catch to obtain his opinion.
I'll link the "theme() to drupa_render()" issue here once I've created it.
Comment #15
jessebeach CreditAttribution: jessebeach commentedPostponing on #2006152: [meta] Don't call theme() directly anywhere outside drupal_render(). We should still come to consensus about whether this change should be applied if and once the issue is unblocked.
Comment #16
catchI'm personally fine with renaming the function, it's been 'discouraged' for ages, but should've been marked deprecated for ages. It's also a hard blocker to certain Twig functionality
However agreed on #2006152: [meta] Don't call theme() directly anywhere outside drupal_render() first.
Comment #17
thedavidmeister CreditAttribution: thedavidmeister commentedYup, I totally agree with this issue, and #13, #15 and #16.
I believe this is the direction we want to move in.
Comment #18
markhalliwellAdding appropriate tag per #16
Also, a personal: w00t!
Comment #19
jessebeach CreditAttribution: jessebeach commented#2006152: [meta] Don't call theme() directly anywhere outside drupal_render() is fixed. It's time to reroll this patch.
Comment #20
catchLooks like there's a few theme() calls outside tests in Views left. However that issue is over 300 comments so not keen on re-opening - could we open one more sub-issue for those and postpone this on that? This issue should stick to only the rename/docs if at all possible.
The calls in tests I'm fine with leaving as _theme() at least for now.
Comment #21
xjmGood times.
Edit: Crossposted. I'm unclear on which other uses we need to remove. There's a lot of references to theme('whatever') in the docs.
Edit 2: Empty patch anyway...
Comment #22
xjmAh. From #2006152: [meta] Don't call theme() directly anywhere outside drupal_render() #60:
Comment #23
xjmSo, I'd suggest:
theme()
to_theme()
so that it's scriptable)._theme()
so that they're more accurate. (Replace "Avoid" / "It is preferable" with clear indication that the function is for internal use only, and possibly marking it @deprecated if we have an existing replacement for all uses including the internal ones.) (Probably also beta-blocking.)_theme('all_the_things')
in comments where appropriate. (Not beta-blocking.)_theme()
in tests and test modules where appropriate. (Not beta-blocking.)Comment #24
star-szrMakes sense to me, thanks @xjm!
Comment #25
xjmNow postponed on:
#2191101: Replace calls to theme() with drupal_render() in Views plugins
Followups:
#2191107: Update API documentation for _theme() to clearly indicate that it is for internal use only
#2191115: Clean up stale references to theme('foo') in documentation
#2191135: Refactor use of _theme() in tests where possible
Comment #26
star-szrTagging for rocketship. Thanks for creating those issues @xjm!
Comment #27
thedavidmeister CreditAttribution: thedavidmeister commented#2094585: [policy, no patch] Core review bonus from https://drupal.org/comment/8461329#comment-8461329
Comment #28
ianthomas_uk#2191101: Replace calls to theme() with drupal_render() in Views plugins has been committed.
Comment #29
jessebeach CreditAttribution: jessebeach commentedSome references to
theme()
should really be replaced withdrupal_render()
in order to provide better examples to developers using core code as a model. I've replaced these withdrupal_render()
.Some references to
theme()
are legitimately callingtheme()
in order to test it, so I've left these and prepended them with an underscore.There are a few instances of
theme()
where the word 'theme' should not be prepended with an underscore, such as the phrase 'Block test theme' in BlockHiddenRegionTest.php, a theme in the styling sense.Only a few functional changes were made and they all follow from this
function theme() {}
becomesfunction _theme() {}
Because of #2190427: Replace theme() with drupal_render() in form.inc, no calls to
theme()
exist outside ofdrupal_render()
and tests. Tests not directly testingtheme()
have been converted todrupal_render()
. The remaining calls totheme()
that are legitimately called indrupal_render()
simply become_theme()
.I know that Drupal installs with this patch. Let's see what the bot thinks. I also need reviews for content, since I added comments, the most significant one being the following in
theme.api.php
.This patch is fresh and has no diff from #21.
Comment #30
jessebeach CreditAttribution: jessebeach commentedOh, and here's the grep again:
/([\s\(]+)(theme\([\$\'a-z\)])/g
Please verify that I found all instances of
theme()
!Comment #31
star-szrNice, thanks!
Comment #32
xjm@jessebeach, can you document how you are grepping with that regex and creating the patch? I get:
Edit: Using -E instead of -e simply returned nothing, for the record.
Comment #33
rlmumfordJust a couple of nit-picky things.
Double semi-colon.
Shouldn't this be $pager = array('#theme' => 'pager'); ?
Comment #34
Gábor HojtsyAdded a draft change notice for this at https://drupal.org/node/2195739 :)
Comment #35
Gábor HojtsyComment #36
star-szrThanks very much @Gábor Hojtsy! I just added a note about early rendering which we should be avoiding whenever possible.
https://drupal.org/node/2195739/revisions/view/6916007/6916139
Comment #37
rlmumfordUpdated the patch as per comment #33. Also did a search for every instance of 'theme(' and updated where appropriate (I think there was only one that was missed!)
Comment #38
jessebeach CreditAttribution: jessebeach commentedrlmumford, thanks for the finding those typos and misses on my part.
These are the last three mentions of "theme" that I can find. They refer to "theme" as in "the Bartik theme"
The grep I posted isn't working on some command line versions of grep, including mine :/ I've narrowed this down to the space character
\s
. grep isn't recognizing it. So I've updated the grep to this, from the root of the repo:grep -HRIn -E "([ \(]+)(theme\([\$\'a-z\)])" core
This pattern replaces
, a space character. This matches the same three entries noted above:
\s
withThis is my grep version:
grep (BSD grep) 2.5.1-FreeBSD
Comment #39
xjmThanks @jessebeach, that works for me!
I discussed this patch with @webchick today and we agreed to target a commit for Friday, Feb. 21, during the "disruptive patch" window. So let's reroll it the 20th if needed and postpone any issues that change
theme()
calls starting the 19th.This API change has signoff from @catch and the theme system maintainers, so it's just a matter of reviewing that all the changes in the final patch are correct (use
git diff --color-words
) and that all calls are converted.Comment #40
xjmI checked all the
theme()
to_theme()
changes locally and they look good. I also searched using a wider grep than the regex to confirm there were no missed cases:So "theme(s)" as a word (as above), the
FieldPluginBase
method, and JavaScript.Attached includes the changes from the patch that are not simple renames, for reviewer convenience. It all looks correct to me, but would be great if someone with more frontend sense than me could RTBC. :)
Comment #41
xjmOh, and this one:
core/modules/block/lib/Drupal/block/Tests/BlockHiddenRegionTest.php: $this->assertText('Block test theme(' . t('active tab') . ')', 'Default local task on blocks admin page is the block test theme.');
Is matching:
Not the function, the word. :)
Comment #42
xjmMOAR TAGS
Comment #43
joelpittetI had another thorough look too with another tool and just 'theme\(' just in case and same results. This is good to go, great work!
ag '(?<!>|_)theme\('
orack '(?<!>|_)theme\('
Comment #44
alexpottRerolling because I broke it with #2028025: Expand CommentInterface to provide methods.
Yay for git!
Comment #45
star-szrCleaning up super-duper-minor coding standards in @code samples. Thanks for all the work on this everyone :)
Comment #46
catch@xjm and @webchick #39 - this is currently the only RTBC critical issue, and there's only a couple of majors, and we're only going to be committing crticals/majors until after the alpha, so why wait with this one?
Comment #47
xjmThat's fine with me too.
Comment #48
catchOK. Went ahead and committed/pushed to 8.x so this is in the next alpha. It'll mean contrib modules need to update for the next alpha, but shouldn't break much in the way of core patches.
Change notice is in place so moving straight to fixed.
Comment #49
Gábor HojtsyPublished the change record.
Comment #50
star-szrRelated issue to remove more references to _theme(): #2201789: Don't print "_theme()" in twig_debug output