Currently there is no way to know when HTML attributes arrays (i.e. attributes, title_attributes, content_attributes, etc.) should be cast to Attribute objects. template_preprocess(), for example, creates three Attribute objects, but the template to be rendered may not use any of them.

Proposed resolution

Instantiate Attribute objects in a consistent fashion and/or location. Since the main benefit of the Attribute class is only printing the attributes once, consider moving all instantiation of Attribute objects to as late as possible in the rendering process.

Remaining tasks


User interface changes


API changes


#1982018: [meta] Refactor template_preprocess()
#1757550: [Meta] Convert core theme functions to Twig templates

#14 drupal-theme-Attribute--1982024-14.patch1.86 KBsteveoliver
FAILED: [[SimpleTest]]: [MySQL] 56,007 pass(es), 4 fail(s), and 4,538 exception(s). View
#12 0001-Issue-1982024-by-Cottser-Fabianx-Lazy-load-Attribute.patch4.31 KBFabianx
PASSED: [[SimpleTest]]: [MySQL] 55,501 pass(es). View
#12 0001-Issue-1982024-by-Cottser-Fabianx-Lazy-load-Attribute-interdiff-6-12.txt465 bytesFabianx
#6 core_0001-Issue-1982024-by-Cottser-Fabianx-Lazy-load-Attribute.patch4.31 KBFabianx
PASSED: [[SimpleTest]]: [MySQL] 55,486 pass(es). View
#4 1982024-4.patch3.28 KBCottser
PASSED: [[SimpleTest]]: [MySQL] 55,413 pass(es). View
#4 interdiff.txt2.16 KBCottser
#3 1982024-3.patch2.53 KBCottser
FAILED: [[SimpleTest]]: [MySQL] 55,419 pass(es), 5 fail(s), and 4 exception(s). View
#3 interdiff.txt559 bytesCottser
#1 1982024-1.patch2.39 KBCottser
FAILED: [[SimpleTest]]: [MySQL] 55,474 pass(es), 5 fail(s), and 4 exception(s). View


Cottser’s picture

Status:Active» Needs review
2.39 KB
FAILED: [[SimpleTest]]: [MySQL] 55,474 pass(es), 5 fail(s), and 4 exception(s). View

Here is the relevant part of @Fabianx's patch from #1938430-16: Don't add a default theme hook class in template_preprocess().

Fabianx’s picture

Status:Needs review» Needs work

CNW: The call to default_attributes and the var can be removed from template_preprocess then ;-).

Cottser’s picture

Assigned:Unassigned» Cottser
559 bytes
2.53 KB
FAILED: [[SimpleTest]]: [MySQL] 55,419 pass(es), 5 fail(s), and 4 exception(s). View

Revised for #2, looking in to the test failures locally.

Cottser’s picture

Assigned:Cottser» Unassigned
Status:Needs work» Needs review
2.16 KB
3.28 KB
PASSED: [[SimpleTest]]: [MySQL] 55,413 pass(es). View

With this patch an Attribute object is always created, but at least this approach seems to get rid of empty class attributes. Haven't dug into why that is yet.

<h2 class="">


Empty arrays should be fine to print in a template with Twig but it won't fly with PHPTemplate, not sure how we should handle that.

Fabianx’s picture

+++ b/core/includes/theme.incundefined
@@ -2637,13 +2646,10 @@ function template_preprocess(&$variables, $hook) {
-  if (!isset($default_attributes)) {
-    $default_attributes = new Attribute(array('class' => array()));

There is still a variable defined static at the top :)

> Empty arrays should be fine to print in a template with Twig but it won't fly with PHPTemplate, not sure how we should handle that.

Hm, right. In that case you can just clone a static default one or we could have an EmptyAttributes object, which returns '' for each ->get().

Fabianx’s picture

4.31 KB
PASSED: [[SimpleTest]]: [MySQL] 55,486 pass(es). View

Re-rolled with default_attributes cloned for empty array addressing #4, #5.

Fabianx’s picture

Aaaand benchmark (first is baseline vs. baseline), all with 50 nodes and APC classloader:

This first is just to show that the used measuring method is precise as a re-benchmark leads to no difference.

=== head-50-nodes..8.x compared (5185b9518e91e..5185bad3711d6):

ct  : 259,866|259,866|0|0.0%
wt  : 943,793|943,769|-24|-0.0%
cpu : 940,059|940,060|1|0.0%
mu  : 24,751,760|24,752,568|808|0.0%
pmu : 25,306,760|25,307,816|1,056|0.0%

And this is with the patch from #6:

=== head-50-nodes..lazy-load--attribute--6 compared (5185b9518e91e..5185bb5ce59fb):

ct  : 259,866|258,688|-1,178|-0.5%
wt  : 943,793|938,141|-5,652|-0.6%
cpu : 940,059|940,059|0|0.0%
mu  : 24,751,760|24,730,840|-20,920|-0.1%
pmu : 25,306,760|25,280,264|-26,496|-0.1%

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

I think this looks good!

1178 function calls saved! If this again was not my patch, this would be RTBC.

Afterwards any "new Attribute(...)" (as followup) can be removed from _preprocess functions.

thedavidmeister’s picture

Status:Needs review» Reviewed & tested by the community

This looks RTBC to me too. It takes the Attribute() objects that are being set unconditionally in preprocess and moves them to the bottom of theme() - after both preprocess and process phases. It also makes the objects only be created if they're required to be rendered.

There's no documentation changes to review here, it's all just logic.

Wim Leers’s picture

Assuming there's test coverage for all this, this is indeed RTBC.

Also: THANK YOU. The current behavior is infuriating when you need to merge in more attributes: #1972514-2: Impossible to set attributes for all entities.

Only one nitpick:

+++ b/core/includes/theme.incundefined
@@ -1157,6 +1158,20 @@ function theme($hook, $variables = array()) {
+          // Create empty attributes

Missing trailing period.

Wim Leers’s picture

Status:Reviewed & tested by the community» Needs work
thedavidmeister’s picture

@Wim Leers - I don't think it's even possible to write tests to see what part of theme() the Attribute objects are being generated in, is it?

Fabianx’s picture

Status:Needs work» Reviewed & tested by the community
465 bytes
4.31 KB
PASSED: [[SimpleTest]]: [MySQL] 55,501 pass(es). View

RTBC per #8

I only changed the documentation nitpick ...

Test coverage should be sufficient from Attribute() patch itself.

catch’s picture

Status:Reviewed & tested by the community» Fixed

Looks great to me. Committed/pushed to 8.x.

steveoliver’s picture

Status:Fixed» Needs review
1.86 KB
FAILED: [[SimpleTest]]: [MySQL] 56,007 pass(es), 4 fail(s), and 4,538 exception(s). View

Any reason not to give theme functions the same treatment as templates? The attached patch would allow for the removal of the first three Attribute() instantiations from template_process_field(), for example.

Wim Leers’s picture

Status:Needs review» Fixed

#14: this issue was marked as fixed 10 days ago. Please create a new issue and refer to this one.

steveoliver’s picture

Sure. Discussion in IRC with Fabianx has resolved the issue for me, for now at least.

Status:Fixed» Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

webchick’s picture

Note: #2108771: Remove special cased title_attributes and content_attributes for Attribute creation was opened which proposes to back out this change for DX reasons.