Updated: Comment #70
Issue History
#939462: Specific preprocess functions for theme hook suggestions are not invoked was started as a D7 issue, and is now a D8 issue (and assuming an eventual D7 backport). D8 fixes were partially addressed in #1751194: Introduce hook_theme_suggestions[_HOOK]() and hook_theme_suggestions[_HOOK]_alter(). Most of the following was discussed with D8 in mind, but the decision was made to push the changes into D9.
Problem/Motivation
All changes to $variables that will be printed in templates currently happen in the same place, preprocess. Things that happen in preprocess include both creating initial variables and then altering those variables later. There is also inconsistency and confusion (for new themers) on when to use the module/theme name for hook_ prefixing or the very ambiguous template_ prefix.
Proposed resolution
Use a standard hook naming convention, exactly like the FAPI does. These standardized hooks would allow the theme system's phases to be split into a more systemic and easier to understand process. The two "phases" to add would be:
- A phase where the variables that are needed are prepared (created).
- A phase where anyone (modules or themes) can alter existing variables.
Remaining tasks
Add new prepare hooks.Add new prepare alter hooks.Create temporary tests for all the new hooks.Add new temporary method (Drupal::moduleHandler()->themeInvoke()) to invoke hooks on active theme, similar to howDrupal::moduleHandler()->alter()currently works.Keep backwards compatibility support forhook_preprocess()by allowing it to run after the new API. Once everything is converted over, we don't have to worry abouthook_preprocess()being last.Move thehook_preprocess()phase in between thehook_theme_prepare()andhook_theme_prepare_alter()phases (per discussion about this via Twig hangout meeting/IRCDeprecate hook_preprocess_HOOK() in API doc.Document hook_theme_prepare() in theme.api.phpAdd/change/remove subsections in theme() docblock.Add relevant@todocomments for cleanup later.Create test for hook_theme_prepare_THEME_HOOK__SUGGESTION().- Add before/after example code in issue summary to demonstrate the benefits of this refactoring. (per #70)
User interface changes
None.
API changes
None.
API additions
Technically this is just one new "hook" with variations for allowing specific suggestion targeting. These specific hooks are invoked based on which template was used. For example array('#theme' => 'node__page', ...), has two different THEME_HOOKs. Two different prepare functions are called: hook_theme_prepare_node() and hook_theme_prepare_node__page().
- hook_theme_prepare(&$variables, $hook)
- (and in a loop) hook_theme_prepare_THEME_HOOK(&$variables)
All hooks can be appended with _alter for the altering "phase". This phase alters existing $variables (added by the first phase above) by reference.
- hook_theme_prepare_alter(&$variables, $hook)
- (and in a loop) hook_theme_prepare_THEME_HOOK_alter(&$variables)
Mapping of Old API to New API
hook[template]_preprocess = hook_theme_prepare[_alter]
hook[template]_preprocess_THEME_HOOK = hook_theme_prepare_THEME_HOOK[_alter]
hook[template]_preprocess_THEME_HOOK__SUGGESTION = hook_theme_prepare_THEME_HOOK[_alter]
The last mapping is not actually not supported in old API. This issue would fix an existing critical bug for D8: #939462: Specific preprocess functions for theme hook suggestions are not invoked.
| Comment | File | Size | Author |
|---|---|---|---|
| #85 | 2035055-85.patch | 31.76 KB | star-szr |
Comments
Comment #1
markhalliwellComment #1.0
fabianx commentedSteal issue and add more explanation for new API
Comment #2
markhalliwellInitial proof-of-concept for simply adding the hooks into Drupal. It doesn't yet replace/deprecate preprocess. I didn't add or modify any core functions, but instead created a simpletest. The test should eventually be integrated properly with the normal Theme API tests, just didn't want to wait 5 mins each time.
Comment #4
markhalliwell#2: drupal-introduce-hook_theme_prepare-2035055-2.patch queued for re-testing.
Comment #6
fabianx commentedNice start!
Comment #7
markhalliwellUpdateModuleHandler needs to be modified to allow for system theme_prepare hooks. Bah! Should fully pass now :)
Comment #8
fabianx commentedI think we should be consistent in our order:
Either $hook, $variables OR
$variables, $hook.
Comment #9
jenlamptonI vote for $hook, $variables everywhere :)
Comment #10
fabianx commented#9: Even for FAPI?
Comment #11
markhalliwellPersonally, I'd rather stick with $variables, $hook (like FAPI does). That way it's consistent through out Drupal. Besides, $hook is supplemental. A module that implements hook_theme_prepare($variables) technically doesn't have to use $hook. If they want to just add to something to every $variables array, they could just ignore $hook.
Comment #11.0
markhalliwelltypo
Comment #12
markhalliwellUpdated issue summary (edit: per IRC conversations).
Comment #12.0
markhalliwellUpdated issue summary.
Comment #12.1
markhalliwellUpdated issue summary.
Comment #13
markhalliwellBumping to a major task since this would fix a critical bug in the existing 8.x code: #939462: Specific preprocess functions for theme hook suggestions are not invoked
Comment #14
markhalliwellForgot to set to NW since there's still a lot to do.
Comment #15
markhalliwellReroll
Comment #15.0
markhalliwellUpdated issue summary.
Comment #16
markhalliwellUpdating title to reflect issue summary.
Comment #17
markhalliwellOk, completely refactored code to coincide with issue summary and IRC chats:
Drupal::moduleHandler()->themeInvoke()method.hook_theme_prepare()andhook_theme_prepare_THEME_ID().@todocomments for replacing things like #1751194: Introduce hook_theme_suggestions[_HOOK]() and hook_theme_suggestions[_HOOK]_alter() (tentatively and in reality a blocker for this issue I think) and #2029819: Implement a ThemeHandler to manage themes (@see ModuleHandler).Still need to figure out how to deprecate
hook_preprocess()properly though.Comment #17.0
markhalliwellUpdated issue summary.
Comment #17.1
markhalliwellUpdated issue summary.
Comment #18
markhalliwellI know I changed it earlier, but was really starting to get on my nerves and thinking this is way too long of a title.
Comment #18.0
markhalliwellUpdated issue summary.
Comment #18.1
markhalliwellUpdated issue summary.
Comment #18.2
markhalliwellUpdated issue summary.
Comment #18.3
markhalliwellUpdated issue summary.
Comment #19.0
(not verified) commentedUpdated issue summary.
Comment #19.1
markhalliwellUpdated issue summary.
Comment #19.2
markhalliwellUpdated issue summary.
Comment #20
markhalliwellSigh... passed locally, wtf. I'm just going to include the theme ids and suggestions in the default
$context. Who knows how people will use it and it may be useful (even for suggestion specific hooks).Comment #20.0
markhalliwellUpdated issue summary.
Comment #21
seanrLooks good on an initial read and applies cleanly, but I need some good use cases/examples to test against and give it a thorough thrashing.
Comment #21.0
markhalliwellUpdated issue summary.
Comment #21.1
markhalliwellUpdated issue summary.
Comment #21.2
markhalliwellUpdated issue summary.
Comment #21.3
markhalliwellUpdated issue summary.
Comment #21.4
markhalliwellUpdated issue summary.
Comment #21.5
markhalliwellUpdated issue summary.
Comment #21.6
markhalliwellUpdated issue summary.
Comment #21.7
markhalliwellUpdated issue summary.
Comment #21.8
markhalliwellUpdated issue summary.
Comment #21.9
markhalliwellIssue summary update.
Comment #22
markhalliwellThis is introducing a new API to help with:
hook_preprocess()(edit: removed "example" code based on old talks, don't want to confuse this issue).
Comment #22.0
markhalliwellcorrect issue
Comment #22.1
jenlamptonlinks to issues
Comment #22.2
jenlamptonwho
Comment #23
seanrThis looks good to me. Don't see any brokenness anywhere and will definitely help themers.
Comment #24
markhalliwellSorry, should have marked this as NW. I have some remaining tasks I still need to do.
Comment #24.0
markhalliwellUpdated issue summary.
Comment #25
star-szrSome initial thoughts after a first review. This is looking good overall, I'm liking all the test coverage.
(edited to listify)
This won't actually be #base_theme_id in a lot of cases, especially for manually called theme suggestions like '#theme' => 'node__foo__bar'. $hook at this time is the most specific theme suggestion that an implementation was found for.
The inline comments in this section don't tell me that the code is for prepare hooks and prepare alter hooks.
Contains \Drupal\system\Tests\Theme\ThemePrepareTest.
Is this actually used?
Docblock needs updating.
Remove debug query code please :)
For all of these, leave the assert message off. They will then get a default assertion message of '"@text" found'. No need to duplicate, see WebTestBase::assertTextHelper().
Looks like there should probably be a blank line before the prepare() method, and both of these need docblocks.
Comment #26
star-szrAnd the references I forgot in my review:
Reference for point #3 - https://drupal.org/node/1354#file.
Reference for point #8 - https://drupal.org/node/608152 and https://drupal.org/node/1354#classes.
Comment #27
markhalliwellMarking critical per @catch's comment in #1751194-86: Introduce hook_theme_suggestions[_HOOK]() and hook_theme_suggestions[_HOOK]_alter() (which this issue is dependent on). This issue also helps fix #939462: Specific preprocess functions for theme hook suggestions are not invoked.
Comment #28
webchickMarking as an approved API change.
Comment #28.0
webchickUpdated issue summary.
Comment #28.1
jenlamptonupdated terminology
Comment #28.2
jenlamptonloop!
Comment #28.3
jenlamptonupdate
Comment #29
c4rl commentedBrushing the dust off this one. :)
Comment #30
c4rl commentedThis is simply a re-roll of #20 for the sake of having an updated patch that works against HEAD. Leaving as `needs work` and will incorporate suggestions from #25 with respective interdiff.
Comment #31
c4rl commentedHere's suggestions from #25 implemented.
Per the Remaining Tasks, I also believe I addressed "Move the hook_preprocess() phase in between the hook_theme_prepare() and hook_theme_prepare_alter() phases (per discussion about this via Twig hangout meeting/IRC)"
Comment #32
markhalliwellWill take a look at this in the AM.
Comment #34
c4rl commentedI guess bad things happen when you change variable names without grepping. :)
Comment #35
c4rl commented@Mark Carver, I'll try to resolve these in order to get this into an acceptable state before review.
Comment #36
c4rl commentedOkay, let's try this one. Fixed the context variable names, also updated the routing file.
Comment #36.0
c4rl commentedAdded the concept of $context back.
Comment #37
markhalliwellThanks @c4rl! Just adding an additional remain task: "Deprecate hook_preprocess_HOOK() in API doc."
Still need to finish out the additional tests too.
Comment #38
markhalliwellGoing to be working on this today, hopefully to knock it out completely :)
Comment #39
markhalliwellAdded documentation and deprecated hook_preprocess()
Comment #39.0
markhalliwellUpdated issue summary.
Comment #39.1
markhalliwellUpdated issue summary.
Comment #41
markhalliwellHere's an update to some of the theme() doc and also fixes the failing tests.
Comment #41.0
markhalliwellUpdated issue summary.
Comment #42
markhalliwellFixed $context and docs
Comment #44
star-szr#42: drupal-introduce-hook_theme_prepare_alter-2035055-42.patch queued for re-testing.
Comment #45
tstoecklerThis patch is a great clean-up, is well documented and comes with tests. Awesome!
Regarding \Drupal::moduleHandler->themeInvoke(): Since it doesn't use any ModuleHandler object state, I would prefer if it were actually just a theme_invoke() procedural function for now. I know procedural functions suck, but sticking methods on stuff where it doesn't conceptually make sense sucks also. I didn't read the entire issue, so I'm not sure if this was discussed above already.
SUGGESTION should be THEME_HOOK if I'm not mistaken.
Comment #46
c4rl commentedSummarizing some discussion in IRC here. :)
There is some debate about a change introduced in #41 and #42 to how
$contextworks. To me, the changes here seemed a digression from its intent as presented in the issue summary.In #39,
$contextwas simply an immutable array of a few internal parameters`'theme_id' => $hook, 'original_theme_id' => $original_hook, 'suggestions' => $suggestions`. It changed in #41 and #42 to be actually an argument for theme() and thus proposed to be user-defined/pluggable (even further via proposed ideas in https://gist.github.com/markcarver/6125045#file-theme-php-L20-L45).I see this expansion of
$contextas potentially problematic for the following reasons:$variables) into two (i.e.$variablesand$context). So, themers/developers will have to grok two things instead of simply one. The$variablesarray may contain nested renderable arrays, which potentially have defined nested$contextarrays. So,$variablesisn't as theoretically clean as we might think.#contextparameter exists in a nested situation, it is not necessarily immutable.$variablesand$contextwas acknowledged as being mostly motivated by separate issues for variable inspection, i.e. #1783134: [META] Make it possible to inspect twig variables and get information about objects and render arrays and #1804998: Add LadyBug from Symfony to Core to allow debugging variables within templates. I'm hesitant to convolve those motivations in this present issue.Because of these reasons, we've decided in IRC that we should create a separate issue that would allow
$contextto be an argument (as proposed in #41 and #42, and not simply the few internal parameters it was in #39 i.e.`'theme_id' => $hook, 'original_theme_id' => $original_hook, 'suggestions' => $suggestions`(or some set of similar parameters).Comment #47
markhalliwellAddressed conserns in #45 and created a temporary procedural function. Addressed concerns with #46 and removed all concepts of "$context" in theme() for a later issue. Updated documentation and this is now essentially a rename of preprocess (but allows the standardized "hook_" implementation through core, instead of the confusing template/theme/hook_ trifecta.
Comment #48
markhalliwellIssue summary will need to be updated, I'll have to do it later when/if I get a chance.
Also created the follow-up: #2121317: Introduce the "context" concept for theme system
Comment #48.0
markhalliwellUpdated issue summary.
Comment #48.1
markhalliwellUpdated issue summary.
Comment #50
markhalliwellUpdated issue summary. Forgot to change test data when taking out $context. Will upload a new patch here shortly.
Comment #51
star-szrAnd this also still fixes #939462: Specific preprocess functions for theme hook suggestions are not invoked for 8.x. Do we have tests for that? If so they're not jumping out at me.
Comment #52
markhalliwellFixed theme_invoke to pass by reference and fixed/added tests
Comment #52.0
markhalliwellUpdated issue summary.
Comment #52.1
markhalliwellUpdated issue summary.
Comment #54
c4rl commentedSeems like the change to using the `#` prefix on these params are making tests fail. Was the prefix intentional?
Comment #55
markhalliwellNope, I think I accidently spaced what these were and added them out of habit. Here is the fix.
Comment #55.0
markhalliwellUpdated issue summary.
Comment #56
c4rl commentedGoing to give this a review today (finally). :)
Comment #57
markhalliwell@c4rl, any chance you can get to this?
Comment #58
c4rl commentedThe changes look good to me, but I haven't had a chance to dig into the test coverage as much as I would like to. I can hopefully do this in the next week, but I'll unassign for now in case there is someone else who is available to give it a better review.
Comment #59
star-szrNeeded a quick reroll.
Comment #61
Jeff Burnz commentedNot sure about the test failures but I applied this patch regardless.
I tested this running the same hooks in a base and sub theme, e.g.:
Running all the above
subtheme_theme_prepare_html()is not fired. If I removebasetheme_theme_prepare_html()then the sub theme starts firing. Is this something to do with the test failures (I haven't really looked hard at this yet)?Comment #62
star-szrI suspect that's because we're not really using a true ThemeHandler here. This patch is adding theme_invoke().
Comment #63
star-szrThanks @Jeff Burnz, you found a bug (and we found the fix for it on the Twig call)! So we're missing test coverage there, for now I will try to get this green.
Comment #64
star-szrThis should fix the reported bug:
The rest of the patch here is just tweaking the way the test assertions work, I found this method easier to debug and work with because you can see the list of hooks fired in order not just pipe separated.
I'm still expecting at least 2 fails because the specific suggestion hooks are no longer being picked up for theme_invoke(). Next patch will have some tests around the base theme/subtheme to make sure that bug has in fact been squashed.
(Migrating related issues from summary to related issues field as well.)
Comment #65
star-szrI'm not sure how that last patch is green, but moving on…
Main changes here:
Comment #67
star-szrPatch ordering seems to be touchy on D7 d.o, I put the fail patch first.
Comment #68
jibranwe can replace @todo of #2029819: Implement a ThemeHandler to manage themes (@see ModuleHandler) with #2109287: Replace list_themes() with a service. which is RTBC.
Comment #69
star-szrThanks @jibran. Going to rework this a bit now that #2109287: Replace list_themes() with a service. (and thus ThemeHandler) is in.
Comment #70
tim.plunkettBecause the patch is only adding code, and not converting any, it is unclear how this is a DX improvement. Can that be expanded upon in the issue summary, with a full code before/after example?
Comment #71
joelpittetHere here on @tim.plunkett's comment in #70. I had a bit of a hard time grasping and trying to explain this in the twig talks I did in Van. Before/after would be amazing if the can explain the benefits we gain.
Comment #72
jwilson3Added #70 as a task.
Comment #73
star-szrAgreed - and I can work on that, just a busy time of year.
Comment #74
star-szr65: 2035055-65.patch queued for re-testing.
Comment #76
star-szrFixing up the test.
Comment #77
star-szrPassing this one back to @Mark Carver.
Comment #78
star-szrGoing to take another run at this. Currently needs reroll.
Comment #79
star-szrRerolled and took out the extra changes to theme_test.routing.yml, leaving them for the more modest #2111079: Add @code sample and test coverage per hook_theme_suggestions[_HOOK]() and hook_theme_suggestions[_HOOK]_alter().
Comment #80
star-szrComment #81
star-szrI read through #2004872: [meta] Theme system architecture changes again a few nights ago and the way forward is clear to me now. The patch was trying to introduce non-alter hooks to themes which is not related IMO and was actually making me not want to work on this issue. Also importantly, for consistency hook_theme_prepare() should return $variables instead of altering by reference.
This still needs more work but I wanted to put something up tonight. This just strips out the theme hook_theme_prepare implementations and updates/cleans up the tests. I will also be working on updating the issue summary and drafting a change notice in the near future. I'm still considering what makes the most sense to convert to the new API here but I will definitely incorporate that.
Comment #82
star-szrComment #83
star-szrComment #84
star-szrGot a bit excited with the tags.
Comment #85
star-szrThis needed a small reroll, I'm hoping to have some time this weekend to get back to this in a more meaningful way.
Comment #86
star-szrI was able to make some progress but not ready to post quite yet.
Comment #87
star-szrAfter exploring hook_theme_prepare() as a hook_menu() style hook (and finding the downfalls of that approach), myself and some of the other theme system maintainers had some discussions about this and I can’t speak for everyone but in general we are now favouring a more minimal approach which would maintain the current preprocess system while still providing the same capabilities as theme hook suggestion-specific preprocess/prepare hooks.
Following our discussions and after re-reviewing #939462: Specific preprocess functions for theme hook suggestions are not invoked again (@effulgentsia's comment in #939462-6: Specific preprocess functions for theme hook suggestions are not invoked stood out in particular), we would favour the following approach for allowing suggestion-specific variable preparation/preprocessing:
#2201781: Pass all theme hook suggestions to theme preprocess functions to allow for suggestion-specific overrides
I think this is a better and more flexible API than trying to build a stack of separate suggestion-specific prepare/preprocess functions and deciding in which order and circumstances they should run, something that gets even more complicated when you have theme_hook_suggestions__that__can__cascade__down. After going through some architecture and use cases, we have determined the method proposed in the above issue gives both module developers and advanced themers the power they need to perform suggestion-specific variable preparation as necessary. This approach still paves the way for template consolidation as well.
Having said all that, I'm bumping this to 9.x in favour of the above issue.
Comment #88
markhalliwellI'm moving focus back to this issue because of my comment in #2201781-9: Pass all theme hook suggestions to theme preprocess functions to allow for suggestion-specific overrides. Also the related issue I'm attaching now explains in more detail why using this approach is not an acceptable solution.
@todos:
We need to pass suggestions in the
$infoarray.Also if, in the spirit of time, we need to keep the preprocess name for this issue to progress quickly (no API change), then I think that is an acceptable (albeit very unfortunate) compromise. The underlying "fix" in this patch is still valuable, regardless of which hook implements it.
Comment #89
markhalliwellGiven the short deadline, it is highly unlikely that this will get done (including all the necessary conversions in core). Moving this to be addressed in 9.x (if still necessary) and moving focus back to #939462: Specific preprocess functions for theme hook suggestions are not invoked.
Comment #90
markhalliwellComment #91
star-szrComment #92
akalata commentedComment #93
catchWe should see whether any of this is viable for a minor version with a bc layer.
Comment #95
joelpittetI don't see the benefit of renaming this for the 8.x lifecycle and would suggest keeping this in 9.x. It's a hook rename, we shouldn't be creating variables in preprocess nor prepare. It's an alter hook to change or alter them before they hit the template. It should also be said that variables changing via preprocess should in theory be very rare and ideally not at all in core (save for tests to make sure it works).
Comment #100
markhalliwellSigh... despite all the work I did on this issue and as sad as I am to see this issue get sideswiped, it may have been for the best.
Closing in favor of #2869859: [PP-1] Refactor theme hooks/registry into plugin managers.