Problem/Motivation

Currently '#type' can be used to merge in some default renderable array properties http://drupal.org/node/930760:

When #type is declared for a render array, the default properties of the element type named are loaded through the use of hook_element_info().

Separately in drupal_render() we can declare '#theme' which defines the theme function responsible for rendering this array.

It's a very common pattern that in the defaults set by #type, either a #theme or #theme_wrappers attribute is set for the element. So common, in fact, that many people in the community are confused as to what the difference between #type and #theme actually is, and when to use each (at least, outside the context of the FAPI where everything is based on #type).

We have a DX/learnability problem where the expected behaviour of renderable arrays is split across the combination of '#type' and '#theme' or just '#markup' and there's no central documentation for what HTML elements "should" be using #type as the basis for a new render array of #theme. The FAPI docs are amazing, but they still fall short of covering a huge number of renderables in Core.

In short, predicting the behaviour of a given render array can be very complex - easy to learn (sort of) but hard to master. Creating a new render array from scratch and being confident that you've built it "the right way" can take a lot of research and experience.

Moshe said this:

#theme is responsible for turning the array into HTML
#type is used to merge in defaults from hook_element_info()

I believe that while this is true and simple enough in theory, it can be confusing to work with in practice given the current set of defined #type, #theme and #theme_wrappers elements used in core and contrib.

Proposed resolution

I think we could take the idea of #type setting defaults a step further and use it *as* a default theme hook suggestion.

- Using #type to set a default #theme hook is already a very common pattern in the Render API
- If you use the array syntax for theme hooks array('hookname') then it is harmless if theme() does not find a match for the suggestion #2923506: Deprecate and discourage use of the "array of theme hooks" feature
- Learning and using render arrays would be easier because you could almost always assume that setting the #type will set the right #theme hook for you automatically and almost never need to worry about #theme again, except when you're overriding the default
- If you wish to override a non-theme based render (#pre_render -> #markup for example), all you have to do is define the theme hook and template, no need to mess with hook_element_info_alter()

There is a slight issue of how to handle #theme_wrappers. For example, consider #type 'radios' that sets #theme_wrappers => array('radios') already as its default - in this case we don't want to add #theme 'radios' as this would lead to theme('radios') being called twice, inappropriately.

We also don't want to touch a render element that has an empty array set for #theme_wrappers, this is because currently core sets an empty array for #theme_wrappers when it wants to use the defaults provided by a #type sans #theme and #theme_wrappers. See form_process_vertical_tabs() for an example of this where vertical tabs use #type 'details' but forcing the call to theme() to be bypassed while still inheriting the #pre_render calls for 'details'.

This means that as well as avoiding adding #type as a default suggestion for #theme when #theme is already set, we also want to avoid using #type as a default #theme if #theme_wrappers is set and empty OR #theme_wrappers is set and already contains #type as a key/value (we need to check the key and value due to the two different syntaxes for #theme_wrappers).

Remaining tasks

There is a patch in #76 to be applied.

User interface changes

None.

API changes

$elements['#type'] will be used as a #theme suggestion in array syntax, ie. $elements['#theme'] = array($elements['#type']) when neither #theme nor #theme_wrappers has already been defined for $elements.

This API change does not break backwards compatibility with existing render arrays as the syntax of the theme suggestion means that theme() and drupal_render() simply ignore the suggested #type-hook if it is not implemented.

This issue is part of #2015147: [meta] Improve the DX of drupal_render(), renderable arrays and the Render API in general.

Implementing the suggested resolution of this issue will make #2025629: [meta] Ensure that all #theme/#theme_wrappers hooks provided by core are associated with a sensible, re-usable element #type much simpler, easier and cleaner - the changes proposed there will be more palatable and be less of an "API breakage" with this patch in place.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

c4rl’s picture

Great start, thedavidmeister.

'#type' is not optional, it is required!

Definitely, this is one of the core principles of the refactoring. #type and #theme currently have convolved responsibility and that needs to change.

if '#theme' is not set it is assumed to = '#type' / if '#theme' is set and === FALSE then theme() is not called by drupal_render() - this could be achieved with hook_element_info()

The way that hook_element_info() currently works is there's an implicit association here http://drupal.org/node/169815. If a theme callback named identical to the #type it will be used. So this doesn't seem necessarily new.

if '#type' and '#theme' are both set, '#theme' is passed to theme() as the first argument

What would be examples where #type and #theme do not have a 1-to-1 association? A question I have is if we envision a world where #theme goes away entirely? (I think it might). Do we feel that hook_element_info() and hook_theme() are substantively different in purpose to remain separate callbacks?

tim.plunkett’s picture

Adding something like

$render_array += array('#type' => 'markup');

would preserve the simplicity of #markup.

Otherwise I'm generally +1 to this.

thedavidmeister’s picture

#1 - Ah yeah, Fabianx and I were having some chats in IRC about possible futures that may or may not happen. I didn't want to preclude them here.

http://drupal.org/node/169815 says:

(note: in Drupal 7, you do need to add #theme to hook_element_info())

I thought it important to mention that '#theme' is always assumed to have some value but if it is set to FALSE you can explicitly bypass theme(). This is not how drupal_render() currently works as it only checks isset() for '#theme'.

For example, currently theme() has preprocess and process phases, for which there's a proposal to refactor a bit here #2004872: [meta] Theme system architecture changes. Fabianx explained to me though that *anything* that can be teased out into discrete "input -> processed variables -> altered + processed variables -> string" can be "drillable" by Twig if that workflow is structured so that Twig can invoke the processing without the string concatenation at the end. I was suggesting that drupal_render() could be responsible for making rendered elements "drillable", with the process phases and then theme() is only responsible for invoking the right theme/template function (which couldn't happen until #2006152: [meta] Don't call theme() directly anywhere outside drupal_render()) and so we could do the things like what '#type' => 'link' does and inline rendering inside drupal_alter() for speed.

Even if that doesn't happen though, if you had '#type' => 'link' which renders a string into '#markup' in the '#pre_process' you don't want drupal_render() to call theme_link() so you would set '#theme' => FALSE in hook_element_info() as otherwise '#theme' => 'link' would be assumed.

What would be examples where #type and #theme do not have a 1-to-1 association?

Well, probably nothing at the moment. Potentially though, if #type determined the preprocessing and not #theme, in contrib/custom you might want to do something like '#type' => 'link', '#theme' => 'my_crazy_link' to send the "base" processed/sanitized variables off to your own crazy link theme function/template.

A question I have is if we envision a world where #theme goes away entirely? (I think it might). Do we feel that hook_element_info() and hook_theme() are substantively different in purpose to remain separate callbacks?

Possibly not but I feel like that line of enquiry should be a followup issue as trying to do that here could derail this first step.

tstoeckler’s picture

Just a note that '#type' => 'table' and '#theme' => 'table' are two *very* different things currently. I totally applaud striving for consistency and I have suggested moving over to '#type' => 'table' for all tables before, but this will be a non-trivial effort.

thedavidmeister’s picture

#4 - if they're legitimately two different things, why don't we split them into two types rather than trying to force them to be merged?

tstoeckler’s picture

No, they are the same thing - an HTML < table > - but they are currently declared differently. Example:

// In '#type' => 'table' the render array structure is used to construct rows and cells.
$table = array(
  '#type' => 'table',
  ...
);
$table['row_1']['col_1'] = array(
  '#markup' => ...,
  ...
);
...

// '#theme' => 'table' works like a usual theme function
$rows['row_1'] = array(
  'col_1' => array(...),
  ...
);
$table = array(
  '#theme' => 'table',
  '#rows' => $rows,
  ...
);
thedavidmeister’s picture

#6 - sure... I think I'll update the issue summary because that's just another example of why we *should* make the change.

I wonder if it would be possible/easier to give '#type' => 'table' a pre_render callback that "magically" transforms the current type syntax to theme syntax and always passes it off to theme - at least as a stopgap measure. We could file a followup task to actually refactor everywhere tables are called and remove the magic later.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Wow, I just realised that for '#theme' => 'item_list', '#type' means "type: The type of list to return (e.g. "ul", "ol")."

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

Fabianx’s picture

Yeah, that should really be #list_type instead ... Issue?

tim.plunkett’s picture

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Based on c4rl's git repo I'm going to add in the proposal that if #type is not set in our array we simply concatenate the values and return. This allows partial compatibility with existing array('#markup' => 'foo') style arrays and gives us some kind of simple default fallback behaviour.

thedavidmeister’s picture

Currently if theme($elements['#theme'], $elements) returns an empty string then drupal_render() tries to render $elements['#children']. I don't understand the reasoning behind that, it looks like a bug to me so I'm going to add to the issue summary:

If theme() is called then theme() is 100% responsible for rendering both the element and it's children, drupal_render() will not interfere with or "fallback" to attempting to render #children once theme() has taken this responsibility.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

And currently if '#theme' is set, the behaviour of drupal_render() interpreting '#markup' changes as well! That's something that goes against this issue too. Updating summary with:

'#markup' is a reserved key for drupal_render() and its value will *always* be included in the output regardless of whether '#theme' is set, theme functions are therefore advised to not define a 'markup' parameter. The final output is always $prefix . $markup . $rendered . $suffix.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Status: Active » Needs review
FileSize
4.44 KB

Here's a patch that just puts the current proposal into drupal_render() without attempting to address any inevitable fallout or update much documentation. Setting to needs review just so we can see the damage ;)

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

tim.plunkett’s picture

#markup with no #type means that #type is 'markup.
#markup respects #prefix and #suffix and everything else.

thedavidmeister’s picture

#16 - Fair point.

@Fabianx was the one telling me he was looking for a simple and fast way to check for "is renderable array" (ie. without calling an drupal_is_renderable_array() function) so I'd like to hear if he's happy with if (isset($foo['#type']) || isset($foo['#markup']) outside of drupal_render().

Status: Needs review » Needs work

The last submitted patch, 2005970-14.patch, failed testing.

thedavidmeister’s picture

Status: Needs work » Needs review
FileSize
4.15 KB

What about this?

If #type is not set then markup is assumed and #theme is set to FALSE. This allows the existing simple array('#markup' => 'foo') style renderable arrays but enforces that any renderable array wanting to use theme() has to set a base #type and provide #theme only as an override for the default callback.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

also, if anyone is wondering about the new elseif logic, #render_children is set by theme()

Status: Needs review » Needs work

The last submitted patch, 2005970-19.patch, failed testing.

Anonymous’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Assigned: Unassigned » thedavidmeister
thedavidmeister’s picture

Assigned: thedavidmeister » Unassigned
Status: Needs work » Needs review
FileSize
15.25 KB

I added:

  // #theme with no #type is deprecated but lets assume #type = #theme for now
  // when we see an array like this.
  if (!isset($elements['#type']) && !empty($elements['#theme'])) {
    $elements['#type'] = $elements['#theme'];
  }

in an effort to reduce the immediate impact on the API of this issue and reduce the amount of work required here.

I also started cleaning up hook_element_info() implementations that I could find.

Status: Needs review » Needs work

The last submitted patch, 2005970-23.patch, failed testing.

Anonymous’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Status: Needs work » Needs review
FileSize
15.25 KB

fix for a fatal in #23

Status: Needs review » Needs work

The last submitted patch, 2005970-26.patch, failed testing.

Anonymous’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

mark does the same thing as item_list by using #type for input. Updated summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

thedavidmeister’s picture

thedavidmeister’s picture

splitting out a third bit #2012818: Remove #type 'markup'. Whittling the scope of this patch down :)

moshe weitzman’s picture

Lets disregard Doxygen bugs and various implementations that do things wrong. The relevant ideas of drupal_render() in my mind are:

  1. #theme is responsible for turning the array into HTML
  2. #type is used to merge in defaults from hook_element_info()

So, it is natural that drupal_render() does not care about #type because #type is not part of its API. When #item_list uses #type, it does so correctly because #type is not a reserved word.

We can certainly change how things work. I still think it is useful share understanding about our starting point. I'd be happy to do a phone call on drupal_render() topics if thats helpful.

thedavidmeister’s picture

#32 - Yes, this issue is looking to change how things work. To make #type required in renderable arrays and make it determine how drupal_render() behaves (ie. stop doing special casing for renderable arrays based on #markup and #theme beyond picking a theme function) - and therefore make it part of its API.

Lets disregard Doxygen bugs and various implementations that do things wrong.

I'm not sure I totally understand which things in core you'd consider are "doing things wrong" and which things you think are working as intended.

When #item_list uses #type, it does so correctly because #type is not a reserved word.

What about when #mark does it? I believe this throws errors - #2009578: Replace theme() with drupal_render() in history module. Part of this issue is a proposal to make #type into a reserved word so that the API doesn't allow us to make theme functions that don't have robust renderable array representations.

For example, I could define a valid theme function that expects to be called like this currently:

theme('foo', array('type' => 'table'));

type here expects 'table' or 'item_list' in this theme function. How would you represent this with a renderable array with the current system without the incorrect defaults being loaded by drupal_render()?

#theme is responsible for turning the array into HTML

That's not 100% true. #prefix, #suffix, #markup are all handled by drupal_render(), not the theme() function called. You can have a #type that doesn't require #markup to be set when drupal_render() is called, and then it is built during the pre_render "phase" without using theme functions -> see #type => 'link'. You can also set #children in the array that you pass in to drupal_render() and theme() won't be touched unless you have #theme_wrappers set.

#type is used to merge in defaults from hook_element_info()

That would still be the case, but I'm also proposing that if #theme isn't set then it is assumed to be the same as #type which would mean that for the majority of existing array('#theme' => 'foo') type renderable arrays the conversion would be as simple as changing it to array('#type' => 'foo') and leaving the rest as-is.

I'd be happy to do a phone call on drupal_render() topics if thats helpful.

Sure. I'm not sure if timezones would be an issue, I'm in Melbourne. An IRC chat is also an option, I'm usually in #drupal-twig when I'm on :)

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

I added #2015177: Update documentation around hook_element_info() to not say/imply element types are limited to the Form API and removed things from the issue summary that have not been split into their own issues. The patch in #26 now needs a re-roll with reduced scope.

thedavidmeister’s picture

Title: '#type' should be required in renderable arrays and should solely define the behaviour of drupal_render() » In renderable arrays, #theme should be assumed from #type if not set and #theme => FALSE should force theme() to be bypassed

Updating summary to more accurately reflect what is being proposed here.

thedavidmeister’s picture

Status: Needs work » Needs review
FileSize
14.54 KB

updating patch to not cover the issues opened elsewhere.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

Status: Needs review » Needs work

The last submitted patch, 2005970-36.patch, failed testing.

thedavidmeister’s picture

I've been thinking about this a bit more, really trying to reduce it down to something both more achievable for a single patch and with a more palatable scope of API changes.

This issue is still trying to fix two or three different things (down from like 7 but still more than one) which is bad.

1. Wanting to enforce #type for all renderable arrays to make other ideas possible (like a #type based drupal_alter(), for example).
2. Trying to make an explicit workaround around for a bug in drupal_render() where it's relatively easy to call theme() and also the not-theme() fallback, when the intended behaviour is to only ever choose one or the other.
3. Trying to make it both easier to do and easier to learn for developers using the render API to have rich, robust and consistent behaviour across *all* their renderable arrays.

1:

I just don't think we're ready for this kind of change yet and won't be for months, maybe years. #theme isn't even being used everywhere instead of theme() yet, there'd be at least one more complete sweep of core to get everything using #type over #theme, looking at 'table' as an example of this we can see that this can be a "non trivial effort" as @tstoeckler put it.

Additionally, I'm not aware of any of the ideas that require #type being set even having a proof of concept patch available, let alone something ready for review that's complaining about being blocked on this API change.

When/if the time comes to enforce #type, we can just open a new issue for that one thing.

2:

Let's fix that bug elsewhere, there's already tickets open for it #2012812: drupal_render() can't distinguish between empty strings from theme() and no hook being matched, #2015311: If #theme suggestions are set as an array for theme() but none are implemented #markup is treated incorrectly by drupal_render(). It's a little gnarly as it will probably require at least a minor API change to theme() to fix, but the solution will hopefully be better than having to explicitly set #theme = FALSE for over half the #types currently in core.

3:

This is something that could be done here. What about if we just aim to add #type as a theme suggestion for all renderable arrays if #theme is not set? If the theme suggestion isn't available then theme() will just return early and if the bug in 2 is fixed, drupal_render() should know at this point that we're supposed to fallback to the non-theme way of processing this array without needing to be told with #theme = FALSE style declarations.

Anyway, just wanted to write down what I was thinking about somewhere..

thedavidmeister’s picture

Title: In renderable arrays, #theme should be assumed from #type if not set and #theme => FALSE should force theme() to be bypassed » In renderable arrays, #type should provide a #theme suggestion, similar to how drupal_prepare_form() works

updating issue title to reflect #38

thedavidmeister’s picture

FileSize
3.63 KB

just whittling away...

thedavidmeister’s picture

Status: Needs work » Needs review

I uploaded the wrong patch >.< it's missing a few modifications to existing hook_element_info() implementations.

Status: Needs review » Needs work

The last submitted patch, 2005970-40.patch, failed testing.

thedavidmeister’s picture

Status: Needs work » Postponed
thedavidmeister’s picture

Status: Postponed » Needs review

#40: 2005970-40.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, 2005970-40.patch, failed testing.

thedavidmeister’s picture

Status: Needs work » Needs review

#40: 2005970-40.patch queued for re-testing.

thedavidmeister’s picture

hopefully #1985470: Remove theme_link() being closed means less than 147 fails.

Status: Needs review » Needs work

The last submitted patch, 2005970-40.patch, failed testing.

thedavidmeister’s picture

Status: Needs work » Needs review
FileSize
11.39 KB

Status: Needs review » Needs work

The last submitted patch, 2005970-49.patch, failed testing.

jenlampton’s picture

Status: Needs work » Needs review

#49: 2005970-49.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, 2005970-49.patch, failed testing.

thedavidmeister’s picture

Status: Needs work » Needs review
FileSize
630 bytes

This is the only part of #49 that is still relevant.

Status: Needs review » Needs work

The last submitted patch, 2005970-53.patch, failed testing.

thedavidmeister’s picture

hurrrrrrr.

thedavidmeister’s picture

Status: Needs work » Needs review
FileSize
729 bytes

This avoids setting #theme from #type if #theme_wrappers is set - so we don't break #type elements that only set #theme_wrappers, like the radio elements in the installer. I don't know why this wasn't already breaking in previous patches.

Status: Needs review » Needs work

The last submitted patch, 2005970-56.patch, failed testing.

thedavidmeister’s picture

FileSize
1.41 KB

The exceptions are from drupal_get_css() causing the theme to initialise on ajax requests when it calls drupal_render(), when it can't because $theme is empty.

This patch avoids that by adding a check for empty $css arrays in drupal_get_css() and hopefully makes what is happening to #type a little more legible.

thedavidmeister’s picture

Status: Needs work » Needs review
thedavidmeister’s picture

Issue tags: +needs profiling

This could add a lot of calls to theme(), which almost immediately return when the suggestion doesn't match, but I think it should be profiled. We could statically cache hooks that don't match or something if it's bad.

thedavidmeister’s picture

thedavidmeister’s picture

FileSize
1.91 KB

I had a bit more of a think about this, and I wanted to try changing $theme_is_implemented to start with #theme being !empty() rather than isset(). This way, if profiling showed that a particular #type was generating too much of an overhead by calling theme() a lot of times you could simply set #theme FALSE in hook_element_info() and that would be that.

thedavidmeister’s picture

Issue tags: -needs profiling
thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

star-szr’s picture

I'm still up for doing some profiling on this, may happen this weekend.

thedavidmeister’s picture

Ok, cool. If you want to do profiling, can you let us know what hooks are the slowest? I'm happy to drop a #theme => FALSE in hook_element_info() for anything that's noticeably slow.

star-szr’s picture

I won't have time in the near future to profile this after all. The patch does look sensible to me after reading the issue summary though.

Fabianx’s picture

Issue tags: +needs profiling

This is RTBC by me, but it needs profiling.

This is especially important for things that do not go via the Form API and an important step for a Theme Component Library.

$array = array(
  '#type' => link,
  // ...
);
$array = array(
  '#type' => 'container',
  '#theme' => 'entity__node',
  // ...
);

etc.

thedavidmeister’s picture

Status: Needs review » Needs work

So, in preparation for some profiling, I put the following code inside the new block of logic that adds #theme:

      if (!isset($counts[$elements['#type']])) {
        $counts[$elements['#type']] = 1;
      }
      else {
        $counts[$elements['#type']]++;
      }

I used devel to create 50 nodes with up to 4 comments each and displayed them all in the "full" view mode with comments displayed in the front page view.

The result was this:

contextual_links_placeholder (Integer) 34
value (Integer) 203
link (Integer) 7
html_tag (Integer) 67
styles (Integer) 1
scripts (Integer) 2

So, I'm going to re-roll the patch with #theme => FALSE in the element info for "contextual_links_placeholder", "value" "link" and "html_tag" before going through the process of profiling this as I think it's obvious enough that we don't need to be spamming theme() for elements with those #types.

thedavidmeister’s picture

FileSize
6.5 KB

After clicking around the site a bit with the counts on, here's the patch that I think should be profiled.

thedavidmeister’s picture

Status: Needs work » Needs review

needs review just for the testbots, still needs profiling.

thedavidmeister’s picture

Issue tags: -needs profiling

Here's my profiling results for the patch in #69, 50 nodes displayed with up to 4 comments each on the front page:

tdm:d8html thedavidmeister$ ./xhprof-kit/benchmark-branch.sh 8.x && say 'done'
Password:
loop time: |22.060801s|51f3746558d24|drupal-perf|8.x|<a href="http://127.0.0.1:8888/xhprof-kit/xhprof/xhprof_html/index.php?source=drupal-perf&url=%2F&run=51f3746558d24&extra=8.x" target="_blank">Profiler output</a>
tdm:d8html thedavidmeister$ ./xhprof-kit/benchmark-branches.sh 51f3746558d24 baseline-8.x 8.x 2005970-69 && say 'done'
Password:
=== baseline-8.x..8.x compared (51f3746558d24..51f3790f24ac9):

ct  : 4,158,319|4,158,319|0|0.0%
wt  : 22,061,397|22,032,353|-29,044|-0.1%
cpu : 21,952,414|21,917,056|-35,358|-0.2%
mu  : 28,152,760|28,152,760|0|0.0%
pmu : 29,516,360|29,516,360|0|0.0%

<a href="http://127.0.0.1:8888/xhprof-kit/xhprof/xhprof_html/index.php?source=drupal-perf&url=%2F&run1=51f3746558d24&run2=51f3790f24ac9&extra=baseline-8.x..8.x">Profiler output</a>

Password:
=== baseline-8.x..2005970-69 compared (51f3746558d24..51f3835a9b3e5):

ct  : 4,158,319|4,158,319|0|0.0%
wt  : 22,061,397|22,058,663|-2,734|-0.0%
cpu : 21,952,414|21,960,305|7,891|0.0%
mu  : 28,152,760|28,159,232|6,472|0.0%
pmu : 29,516,360|29,522,672|6,312|0.0%

<a href="http://127.0.0.1:8888/xhprof-kit/xhprof/xhprof_html/index.php?source=drupal-perf&url=%2F&run1=51f3746558d24&run2=51f3835a9b3e5&extra=baseline-8.x..2005970-69">Profiler output</a>

---
ct = function calls, wt = wall time, cpu = cpu time used, mu = memory usage, pmu = peak memory usage

Seems fine to me...

thedavidmeister’s picture

This might be more useful, I got an API key from Fabianx to get the runs uploaded to lionsad.de:

=== baseline-8.x..8.x compared (51f3746558d24..51f3790f24ac9):

ct  : 4,158,319|4,158,319|0|0.0%
wt  : 22,061,397|22,032,353|-29,044|-0.1%
cpu : 21,952,414|21,917,056|-35,358|-0.2%
mu  : 28,152,760|28,152,760|0|0.0%
pmu : 29,516,360|29,516,360|0|0.0%

http://www.lionsad.de/xhprof-kit/xhprof/xhprof_html/?run1=51f3746558d24&run2=51f3790f24ac9&source=drupal-perf-thedavidmeister&extra=baseline-8.x..8.x

Run 51f3746558d24 uploaded successfully for drupal-perf-thedavidmeister.
Run 51f3835a9b3e5 uploaded successfully for drupal-perf-thedavidmeister.
=== baseline-8.x..2005970-69 compared (51f3746558d24..51f3835a9b3e5):

ct  : 4,158,319|4,158,319|0|0.0%
wt  : 22,061,397|22,058,663|-2,734|-0.0%
cpu : 21,952,414|21,960,305|7,891|0.0%
mu  : 28,152,760|28,159,232|6,472|0.0%
pmu : 29,516,360|29,522,672|6,312|0.0%

http://www.lionsad.de/xhprof-kit/xhprof/xhprof_html/?run1=51f3746558d24&run2=51f3835a9b3e5&source=drupal-perf-thedavidmeister&extra=baseline-8.x..2005970-69
thedavidmeister’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

Needs tests though.

Fabianx’s picture

Issue tags: +needs profiling

Unfortunately this will need re-profiling as this discovered a critical core bug: #2051847: AnnotatedClassDiscovery::getDefinitions is critically slow when viewing nodes with comments

Alternative: Just use 50 nodes (and don't display comments) or 1 node with 50 comments.

Both scenarios had worked fine for me and would be suitable to show comparable performance.

thedavidmeister’s picture

yeaaaaah, I waited over an hour for those results. I'll re-profile, but I'm looking at tests now.

thedavidmeister’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
8.61 KB
1.82 KB

Added some tests.

Fabianx’s picture

Issue tags: -needs profiling
=== baseline-8.x..8.x compared (51f3c666a1782..51f3c7a08937b):

ct  : 375,289|375,289|0|0.0%
wt  : 1,406,293|1,405,628|-665|-0.0%
cpu : 1,400,087|1,400,088|1|0.0%
mu  : 37,144,544|37,144,544|0|0.0%
pmu : 37,269,720|37,269,720|0|0.0%

http://www.lionsad.de/xhprof-kit/xhprof/xhprof_html/?run1=51f3c666a1782&...

=== baseline-8.x..core-2005970 compared (51f3c666a1782..51f3c80c2ab79):

ct  : 375,289|375,289|0|0.0%
wt  : 1,406,293|1,405,119|-1,174|-0.1%
cpu : 1,400,087|1,400,087|0|0.0%
mu  : 37,144,544|37,142,456|-2,088|-0.0%
pmu : 37,269,720|37,263,896|-5,824|-0.0%

http://www.lionsad.de/xhprof-kit/xhprof/xhprof_html/?run1=51f3c666a1782&...

Fabianx’s picture

+++ b/core/includes/common.incundefined
@@ -3772,10 +3784,19 @@ function drupal_render(&$elements) {
+    if (!isset($elements['#theme']) && !isset($elements['#theme_wrappers'])) {
+      $elements['#theme'] = array($elements['#type']);

My only question before RTBC is:

Does it make sense to exclude #theme_wrappers here?

thedavidmeister’s picture

#78 - In the current codebase it does because there are types in core that have the name of theme hooks intended as #theme_wrappers rather than #theme.

See comment #56 where I discovered that #type 'radios' sets #theme_wrappers array('radios').

The intention of #2025629: [meta] Ensure that all #theme/#theme_wrappers hooks provided by core are associated with a sensible, re-usable element #type is to clean up anything confusing about the existing #type/#theme/#theme_wrappers namespaces and is well beyond the scope of this issue, so for now we can't support this "auto theme" behaviour on elements that define #theme_wrappers.

As I said on that issue, this is a bit "chicken or the egg" as getting this patch in makes it easier/neater to clean up the existing element/theme definitions while cleaning up the existing element/theme definitions may allow us to support #theme_wrappers here.

There's also this outstanding issue #2042285: #theme_wrappers should be able to have a unique set of variables per wrapper hook that could easily lead to unexpected/undesirable behaviour when adding #theme into an element that already has #theme_wrappers set.

In the end I didn't see the #theme_wrappers support as critical to what this patch was trying to achieve and right now it causes more problems than it solves with various conflicts. I'm sure this is something we could revisit in D9 if/when we get our theme system structured a bit better overall but for now I think we can play by the 80/20 rule :)

star-szr’s picture

This is looking good and I can see why #theme_wrappers were excluded. Great work @thedavidmeister and @Fabianx!

Fabianx’s picture

Category: feature » task
Status: Needs review » Reviewed & tested by the community

Makes perfect sense.

* A good usage for cleanup.
* A nicer integration of #type (FAPI) and #theme (theme system)
* Backwards compatible.
* Perfect integration with other theme system architecture changes.

Has tests, performance profiling looks good, all feedback addressed => RTBC

moshe weitzman’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs issue summary update

Any chance someone could update the Issue Summary? SInce it doesn't follow the standard template, I suspect it isn't current. If I'm wrong, please remove the tag and change status.

Are html_tag and value really commonly called, as the comment suggests. html_tag happens once per page. That quite infrequent, given how many theme() calls happen on a page. value is only used for forms? I'm not sure what value means in a drupal_render context, to be honest.

thedavidmeister’s picture

Are html_tag and value really commonly called, as the comment suggests. html_tag happens once per page. That quite infrequent, given how many theme() calls happen on a page. value is only used for forms? I'm not sure what value means in a drupal_render context, to be honest.

See comment 68.

html_tag is called more than once per page. Why do you think it happens only once per page?

You can do a very simple experiment yourself. Apply the patch in #62 so you don't have any of the modifications I made to element_info() definitions and then change this:

+    if (!isset($elements['#theme']) && !isset($elements['#theme_wrappers'])) {
+      $elements['#theme'] = array($elements['#type']);
+    }

so that it contains print $elements['#type'];

You should see 'html_tag' appear quite a few times, as well as a few other types.

From memory, I believe that I was seeing 'value' most on pages with forms, but that includes comment forms (wywiwygs, etc..)

The changes I made to the hook_element_info() were made based on measuring which #types would hit theme() the most, using the method outlined in #68 - this experiment should be entirely repeatable :)

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Status: Needs work » Reviewed & tested by the community
Issue tags: -Needs issue summary update

I have updated the issue summary to use the standard template as per #82.

Setting back to RTBC as per #82 and #81.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

xjm’s picture

#76: 2005970-76.patch queued for re-testing.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 2005970-76.patch, failed testing.

thedavidmeister’s picture

Status: Needs work » Needs review

#76: 2005970-76.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, 2005970-76.patch, failed testing.

star-szr’s picture

Status: Needs work » Needs review
Issue tags: +Needs issue summary update

#76: 2005970-76.patch queued for re-testing.

thedavidmeister’s picture

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

Status: Reviewed & tested by the community » Needs work
Issue tags: -Needs issue summary update +Needs tests
+++ b/core/includes/common.incundefined
@@ -3696,7 +3701,14 @@ function drupal_render_page($page) {
+ *     drupal_render(), such as form_builder() in the Form API. If the defaults
+ *     for this #type do not specify #theme or #theme_wrappers, #type is added
+ *     as a #theme suggestion for this element using array syntax. This means
+ *     that if #type is not implemented as a theme hook then theme() will
+ *     simply ignore it and return early. For #types that are very common and do
+ *     not have a corresponding #theme implementation, #theme can be set to
+ *     FALSE in hook_element_info() to bypass the theme() call for a modest
+ *     performance boost.

Afaics we're missing a test for the ability to set #theme to false to bypass the theme.

thedavidmeister’s picture

I'm not sure how to actually test that from outside drupal_render(), I'll have to think about that more :/

thedavidmeister’s picture

I don't know how to test exactly what @alexpott is asking for tests on. I started with this:

      // #theme is FALSE, #markup should be rendered.
      array(
        'name' => '#theme is FALSE, #markup is set',
        'value' => array(
          '#theme' => FALSE,
          '#markup' => 'foo',
        ),
        'expected' => 'foo',
      ),

But then, FALSE is not an implemented theme hook, so it's an identical test to:

      // Theme suggestion is not implemented, #markup should be rendered.
      array(
        'name' => '#markup fallback for #theme suggestion not implemented',
        'value' => array(
          '#theme' => array('suggestionnotimplemented'),
          '#markup' => 'foo',
        ),
        'expected' => 'foo',
      ),

Which already exists.

The only external difference between theme(FALSE, ... ) and theme(array('suggestionnotimplemented'), ... ) is that the former results in a watchdog log:

watchdog('theme', 'Theme hook %hook not found.', array('%hook' => $hook), WATCHDOG_WARNING);

I could try doing something with watchdog, but then if this log call is ever removed we will silently lose test coverage.

I'd love some advice on how to move forward here if anyone could lend a hand.

Fabianx’s picture

Well, I think you will need to do:

* Implement hook_element_info and return FALSE for #theme.
* Setup a pre_render function to render into #markup.
* Check that theme() is not called by providing a template / theme function that has different markup.

Done.

thedavidmeister’s picture

#94 - ooooh, is *that* what I'm testing. Yeah, I can do that. I think I misinterpreted the requirements.

thedavidmeister’s picture

Status: Needs work » Needs review
FileSize
10 KB

I added tests for:

- #type is set and is an implemented theme hook, #theme is FALSE -> should render #markup
- #type is set and is an implemented theme hook, #theme is a theme hook -> should use #theme
- #type is set and is an implemented theme hook, #theme_wrappers is set -> should render #markup wrapped in #theme_wrappers

Hope that helps :)

thedavidmeister’s picture

FileSize
1.95 KB

soz, interdiff attached.

thedavidmeister’s picture

slight updated on a comment.

Fabianx’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs tests

Nice work, that should address the concerns from #91.

Back to RTBC

thedavidmeister’s picture

Status: Reviewed & tested by the community » Needs work

Note to self:

thedavidmeister
4:40 webchick: so, I will:
4:40 add an explanation of #theme_wrappers not being touched to the issue summary
4:40 remove existing #theme => #type lines from element_info() hooks
4:40 samhasse1l left the room (quit: Ping timeout: 245 seconds).
4:41 samhassell left the room (quit: Ping timeout: 246 seconds).
thedavidmeister
4:41 workaround the AJAX thing rather than try to fix it and put a comment linking to the proposed fix issue
4:41 typhoniu_ left the room (quit: Remote host closed the connection).
webchick
4:42 Yeah, something like // @todo This needs more investigation as to why it causes exceptions. http://...?

webchick’s picture

So I am not on board with transitioning everything that's currently a theme function to hook_element_info() and thereby creating hundreds of #types as was in the issue summary prior to this evening, at least without much further discussion. To me, hook_element_info() is for a very special case: it defines re-usable components that other modules can make use of in order to build consistent UIs that behave the same across the Drupal product. If #types button, checkboxes, radios, etc. start getting conflated with #types aggregator_wakka_wakka_foo, that's going to create a huge learnability problem in and of itself. It needs to be really easy for new developers to understand what elements they're recommended to use for the 90% case.

So I've asked thedavidmeister to postpone #2025629: [meta] Ensure that all #theme/#theme_wrappers hooks provided by core are associated with a sensible, re-usable element #type until/unless a more holistic plan is developed there and agreed to by one or more core maintainers. Currently it's catch and I scratching our heads, so this still still needs some work. Remember that we are in the phase of release where each thing we commit need to move us further *towards* release, not further away. New initiatives that spawn 50 more sub-issues of work that risk leaving D8 inconsistent with itself if not 100% complete, and breaking contrib module developers' code unnecessarily is not doing that. Any DX improvements introduced need to be weighed against those types of impacts.

With that out of the way, reviewing this patch on its own merits.

+++ b/core/includes/common.inc
@@ -1710,6 +1710,11 @@ function drupal_get_css($css = NULL, $skip_alter = FALSE) {
+  // Return early if $css is still empty. This is most likely an AJAX request.
+  if (empty($css)) {
+    return '';
+  }

Hm. This looks very, very wrong to me. drupal_get_css() by definition should only be called when there is CSS to put on the page, and never for AJAX requests. I think this needs more discussion on its own in https://drupal.org/node/2032967 so don't really want to commit it here.

+++ b/core/includes/common.inc
@@ -3699,7 +3704,14 @@ function drupal_render_page($page) {
+ *     drupal_render(), such as form_builder() in the Form API. If the defaults
+ *     for this #type do not specify #theme or #theme_wrappers, #type is added
+ *     as a #theme suggestion for this element using array syntax. This means
+ *     that if #type is not implemented as a theme hook then theme() will
+ *     simply ignore it and return early. For #types that are very common and do
+ *     not have a corresponding #theme implementation, #theme can be set to
+ *     FALSE in hook_element_info() to bypass the theme() call for a modest

The first and last sentences here make sense. I don't quite understand though what "This means that if #type is not implemented as a theme hook then theme() will simply ignore it and return early. " means. Rather than explaining to me how it technically works, can you explain what the impact on me would be as a module developer?

This whole section might go better if it were something like:

"The #theme value defaults to the #type value if it's not already specified, and if #theme_wrappers is not set: This means that an element with '#type' => 'toolbar' will by default try to output its contents with theme_toolbar(). This behaviour can be overridden by specifying #theme or #theme_wrappers, or by explictly setting #theme to FALSE in the case of #types that will be called multiple times on the page."

I dunno, that needs some work. But basically, I'd try and aim the docs more at the consumer of this API and less at another core developer.

+++ b/core/modules/contextual/contextual.module
@@ -154,6 +154,10 @@ function contextual_library_info() {
+    // This element type is very common so we set #theme to FALSE to avoid
+    // excessive calls to theme() within drupal_render(). Modules can re-enable
+    // #theme suggestions for this type in hook_element_info_alter().
+    '#theme' => FALSE,

It concerns me how often this hunk is in the patch, and how random the elements it's placed on are (contextual_links_placeholder? Really? That would *never* in a million years enter my mind as one of the top 5 theme functions called on a page.).

It seems like it's *very* easy for a module developer to inadvertently introduce a performance regression by not changing their hook_element_info() to match this. This therefore represents an API change; before they'd have to explicitly set #theme to something in order to incur that performance impact. I didn't see the perf #s for what happens if something like theme_value is missing this #theme => FALSE thing. How bad are we talking here?

It's not necessarily that we can't do the API change, but we need to understand the impact it has and make sure the DX win here is enough to tip the scales. And to that DX benefit, one of the main reasons to do this is so we can remove duplicate #type => 'foo', #theme => 'foo' calls everywhere. But this isn't done in this patch. I think that should be added so we can compare before/after code in a more holistic way.

+++ b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php
@@ -255,6 +255,66 @@ function testDrupalRenderBasics() {
+      // Test that #type is used as a #theme suggestion if #theme is not defined

These tests are lovely! Great job.

thedavidmeister’s picture

Issue tags: +needs profiling

ok, back to "needs profiling" for the patch without the #theme => FALSE to ballpark gauge "worst case" regression here.

webchick’s picture

Btw, on IRC thedavidmeister rightly pointed out that this isn't really an impactful API change; people with D7 modules will have one of #theme, #theme_wrappers, #markup, etc. defined already because it was required then.

Who it would impact is module developers from here on out who use core as an example of what to do, and miss that there is an extra step they need if their elements are going to be on the page many times. I still think that "worst case" profiling would be good to do though, so we understand the impact of missing that detail.

thedavidmeister’s picture

Following on from #103, anything defining #theme, #theme_wrappers or #markup will still not be affected by any performance regression.

It concerns me how often this hunk is in the patch, and how random the elements it's placed on are (contextual_links_placeholder? Really? That would *never* in a million years enter my mind as one of the top 5 theme functions called on a page.).

It isn't, it's one of the top 5 renderable types that requires #pre_render to be rendered. I don't think it's that "random" if you consider that most renderable element types do not require #pre_render to create #markup, so most element types are not candidates for #theme => FALSE here. The performance of all theme functions that actually exist (#theme or #theme_wrappers) is unaffected by this patch.

Only module developers implementing the #pre_render -> #markup pattern (that is a bit of a hack anyway, see #2052253: [META] Add #render property to drupal_render() and convert #type "#pre_render -> #markup" calls to use it) would be affected and only module developers that are already concerned about the performance of calling theme() should even be considering #pre_render -> #markup as a way to implement rendering some markup - you would hope that someone this "deep" already can read the docs and see that setting #theme => FALSE is explicitly recommended for the exact outcome they're trying to achieve.

I will still get some profiling happening, but I want to ensure that the scope of who/what the performance overhead could possibly effect is not misunderstood or exaggerated - I suppose that's exactly what profiling is though ;)

thedavidmeister’s picture

I have opened #2067759: theme() could statically cache some of it's internals to avoid making function calls in an attempt to get the overhead of calling theme() with an unimplemented theme hook down to a bare minimum.

thedavidmeister’s picture

I'm glad that @webchick asked for this patch to actually remove existing hook_element_info() implementations that are made redundant by the API addition in this patch.

What this uncovered was that there's a bunch of element types that are defined like this:

$types['foo'] => array(
  '#theme' => 'foo',
  '#theme_wrappers' => array('not_foo'),
);

So I would like to have a look at a patch where drupal_render() only avoids setting #type in #theme if #type is actually not found in #theme_wrappers, rather than backing out the moment #theme_wrappers is set - which is likely a harmless 'container' or 'form_element'.

What this means is that you would have problems if:

- #type is 'foo'
- #theme is not set
- #theme_wrappers is not set or does not include 'foo'
- theme_foo is actually implemented, but is not intended to be used with #type 'foo'

It is possible, but downright confusing to define a #type 'foo' and #theme 'foo' that are by design incompatible. IMO, if we find an instance of this we should actually clean that up.

thedavidmeister’s picture

It's not necessarily that we can't do the API change, but we need to understand the impact it has and make sure the DX win here is enough to tip the scales.

Just as a yardstick on where the DX sits against HEAD, I turned every core module on and there are 55 items returned by Drupal::moduleHandler()->invokeAll('element_info')

There are 8 current clear duplicates, #2025707: Remove unused #theme "file_widget" would make that 9 so, ~ 15% of all types.

A lot of the #types that aren't affected are either FAPI elements using theme suggestions (19 types), like input__textfield for example, or those using just #theme_wrappers and not #theme at all.

thedavidmeister’s picture

Status: Needs work » Needs review
FileSize
11.81 KB

This patch:

- Removes #theme => FALSE from element_info() definitions, we can profile the overhead of this change
- Will still use #type as a theme suggestion if #theme_wrappers is set, provided #type is not already included in #theme_wrappers
- Improved the documentation by incorporating @webchick's suggestions in #101, with minor modifications to ensure accuracy
- Removes the now-redundant #theme declarations in hook_element_info
- Adds tests for the case that #theme_wrappers contains #type already as either a key or value
- Replaces the usage of #2032967: AssetResolver::getCssAssets() should not try to sort and optimise if $css is empty. with a #theme => FALSE + comment for the type definition of #type 'styles'

Setting to "needs review" for testbot review only.

Status: Needs review » Needs work

The last submitted patch, 2005970-108.patch, failed testing.

thedavidmeister’s picture

Status: Needs work » Needs review
FileSize
11.81 KB
function form_process_vertical_tabs($element, &$form_state) {
  // Inject a new details as child, so that form_process_details() processes
  // this details element like any other details.
  $element['group'] = array(
    '#type' => 'details',
    '#theme_wrappers' => array(),
    '#parents' => $element['#parents'],
  );

I have no idea why this is setting #theme_wrappers to an empty array instead of letting #type => 'details' provide '#theme_wrappers' => array('details')

Looking at the commit history, the empty array used to be for #type 'fieldset', nothing to do with #type 'details', so it looks like it could be a mistake?

Anyway, I'll see what happens if I use empty() instead of isset().

thedavidmeister’s picture

FileSize
11.85 KB

ignore, #110, I did it wrong.

thedavidmeister’s picture

thedavidmeister’s picture

FileSize
11.83 KB

this patch should be a little faster, not using array_key_exists(), using isset() instead.

thedavidmeister’s picture

FileSize
12.28 KB

Improved docs, added a test.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

thedavidmeister’s picture

Added the following to the issue summary:

There is a slight issue of how to handle #theme_wrappers. For example, consider #type 'radios' that sets #theme_wrappers => array('radios') already as its default - in this case we don't want to add #theme 'radios' as this would lead to theme('radios') being called twice, inappropriately.

We also don't want to touch a render element that has an empty array set for #theme_wrappers, this is because currently core sets an empty array for #theme_wrappers when it wants to use the defaults provided by a #type sans #theme and #theme_wrappers. See form_process_vertical_tabs() for an example of this where vertical tabs use #type 'details' but forcing the call to theme() to be bypassed while still inheriting the #pre_render calls for 'details'.

This means that as well as avoiding adding #type as a default suggestion for #theme when #theme is already set, we also want to avoid using #type as a default #theme if #theme_wrappers is set and empty OR #theme_wrappers is set and already contains #type as a key/value (we need to check the key and value due to the two different syntaxes for #theme_wrappers).

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

It appears a lot of thought went into this already. However, after reading through all comments, one fundamental part that I do not get is why you first want to introduce an automated suggestion, and as the right next step, introduce a special magic value to prevent the automated suggestion?

In terms of DX, I wonder how that is an improvement? Instead of:

  1. I'm declaring #type miotypo and this #type shall be themed with #theme miotypo.
  2. I'm declaring #type miotypo and because it's complicated, it does not use a #theme, so I do not set a #theme.

...it becomes:

  1. I'm declaring #type miotypo and (I guess) it will be themed with #theme miotypo?
  2. I'm declaring #type miotypo and because it's complicated, I have to set #theme to FALSE, to prevent theme_miotypo() from being used.
  3. I'm declaring #type miotypo and because I need a special #theme_wrapper, I have to manually set #theme to miotypo, since the automatic default #theme suggestion will not be applied because #theme_wrappers is set.

Long story short, the "sane magic default" concept only works if the condition for applying it is straightforward.

That is the case in e.g. the drupal_prepare_form() case being referenced in the issue title: #theme is only suggested, if no #theme is set. That's it, there is no further logic.

If there are additional conditions and special behaviors, then a magic suggestion only results in var_dump() DX pain — to figure out why something suddenly happened without having asked for it.

I'm not sure whether I'm missing something. Could you clarify the proposal regarding these concerns?

thedavidmeister’s picture

I'm declaring #type miotypo and because it's complicated, I have to set #theme to FALSE, to prevent theme_miotypo() from being used.

Well, part of the proposal is to discourage having #type and #theme with the same name if they're not compatible with each other.

I'm declaring #type miotypo and because I need a special #theme_wrapper, I have to manually set #theme to miotypo, since the automatic default #theme suggestion will not be applied because #theme_wrappers is set.

I'd prefer for this not to be the case, I don't *want* this behaviour and early versions of the patch did not include it. The reason it works this way is because of the blurred, poorly documented line between what's appropriate as a "theme wrapper" and what should be a "theme hook". Things in core like #type 'radios', that adds a 'radios' theme wrapper while setting no #theme make the situation complicated.

I did open #2053671: [meta] Name all theme functions that are only compatible with drupal_render() when nested within #theme_wrappers "foo_wrapper" but I don't even believe that's a good solution either because the naming becomes so awkward :/

Suggestions are welcome >.<

sun’s picture

joelpittet’s picture

Version: 8.0.x-dev » 8.1.x-dev
Status: Needs review » Needs work
Issue tags: +Needs reroll

The intentions behind this issue are great but I feel it may be unintentionally complicating things around those exception cases and needs a few more eyeballs to get behind it, sorry I didn't see this earlier but I think for now we should look at pushing this to 8.1.x as a target to improve this. It shouldn't be to disruptive to 8.x.x branch but definately needs more people to review or make improvements.

Still +1 to making that #type/#theme stuff a bit more sane.

There has been quite a few changes to the theme system that would need some big help re-rolling this for 8.0.x/8.1.x either way. Feel free to move it back if you have some ideas on how to make this simpler to deal with the exception cases.

drintios’s picture

Assigned: Unassigned » drintios
drintios’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll +Needs Review
FileSize
33.03 KB

Re-roll of patch #114

Status: Needs review » Needs work

The last submitted patch, 121: 2005970-121.patch, failed testing.

andypost’s picture

Assigned: drintios » Unassigned
Issue tags: -Needs Review

Patch needs to use approach from https://www.drupal.org/node/2320115
So no reroll possible

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.0-beta1 was released on March 2, 2016, which means new developments and disruptive changes should now be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

markhalliwell’s picture

Removed the snippet about using "array of theme hooks", but the entire IS needs consolidation and updating.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

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

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

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

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

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.

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.

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

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.