Problem/Motivation

There is currently duplication between l(), theme_link() and theme_more_link(). Attempting to consolidate through a common function (like url_is_active) that all three call introduces a performance drop due to l() being called hundreds of times and having an extra function call.

Proposed resolution

Add a (render=>FALSE) flag to the options supplied to l() that causes l() to return an array of element structure rather than a string. The structured output can then be used directly by preprocess functions for Templates without duplicating the logic of creating a link.

Remaining tasks

  1. Patch
  2. Documentation
  3. Tests

- #1922454: Gut l(), fix theme('link'), have a working framework for consistently generated links inside and outside of Twig
- #1961876: Convert theme_link() to Twig
- #1939098: Convert theme_more_link() to Twig
- #1986116: Improve performance by replacing very small and simple templates and theme function with "Markup Utility Functions"

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

shanethehat’s picture

Issue summary: View changes

improving summary

thedavidmeister’s picture

Issue tags: +Twig, +theme system cleanup
thedavidmeister’s picture

Issue summary: View changes

updated summary

Fabianx’s picture

Issue summary: View changes

Added proposed flag

Fabianx’s picture

Issue summary: View changes

clarification

shanethehat’s picture

Status: Active » Needs review
FileSize
1.75 KB

Not sure if I've got the array keys right, but this is a start that hopefully won't break anything.

thedavidmeister’s picture

Status: Needs review » Needs work

nitpick but i believe that in D8 for mixed return values where we know the different data types we're saying string|array instead of mixed.

Also, rather than try to make assumptions about what parts of the structure of l() the caller is looking for, can we just return $variables as-is post-sanitisation?

shanethehat’s picture

Status: Needs work » Needs review
FileSize
2.84 KB
2.42 KB

Making it return $variables means that the array needs to be updated with the operations l() performs.

thedavidmeister’s picture

Status: Needs review » Needs work

+ $variables['path'] = check_plain(url($variables['path'], $variables['options']));

I feel like rather than overwriting $variables['path'] we should add $variables['url'] and use/return that.

Looking good though :)

shanethehat’s picture

Just a thought, but assigning to an array is slightly slower than to a local variable. If we want to prioritise the string output and really get every bit of performance back, should we maybe prepare variables like it did before, and only write them back to $variables if render==TRUEFALSE? The performance difference is tiny though, so maybe it's not worth worrying about.

Speed claims based on: https://gist.github.com/shanethehat/5516588

Edit: had a 50% change of getting it right.

shanethehat’s picture

Status: Needs work » Needs review
FileSize
2.84 KB
1.21 KB

url key added

thedavidmeister’s picture

Issue tags: +needs profiling

Y'know what's weird? Accessing an array once and setting a variable twice is faster than setting an array once, ie:

  $array = array('a' => 'b');
  $ref = &$array['a'];
  $ref = 'c';

Is faster than:

  $array = array('a' => 'b');
  $array['a'] = 'c';

Regardless, yeah we should profile #7 before we go off micro-optimising setting variables in different ways. It looks RTBC to me already but without performance stats who knows?

thedavidmeister’s picture

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

@Fabianx asked for tests in IRC so I'm tagging.

Fabianx’s picture

Issue tags: -needs profiling

I think any micro-optimization is not worth it here:

200 links (first baseline vs. baseline); 2nd with #7 applied:

=== head-l-200-links..head-l-200 compared (5185b1712a8b6..5185b1d606b57):

ct  : 38,430|38,430|0|0.0%
wt  : 163,692|163,110|-582|-0.4%
cpu : 164,010|164,011|1|0.0%
mu  : 16,653,312|16,653,312|0|0.0%
pmu : 16,773,136|16,773,136|0|0.0%

http://www.lionsad.de/xhprof-kit/xhprof/xhprof_html/?run1=5185b1712a8b6&run2=5185b1d606b57&source=drupal-perf-fabianx&extra=head-l-200-links..head-l-200

=== head-l-200-links..structure-render-l--7 compared (5185b1712a8b6..5185b2059b5c4):

ct  : 38,430|38,430|0|0.0%
wt  : 163,692|163,484|-208|-0.1%
cpu : 164,010|164,010|0|0.0%
mu  : 16,653,312|16,654,808|1,496|0.0%
pmu : 16,773,136|16,774,560|1,424|0.0%

http://www.lionsad.de/xhprof-kit/xhprof/xhprof_html/?run1=5185b1712a8b6&run2=5185b2059b5c4&source=drupal-perf-fabianx&extra=head-l-200-links..structure-render-l--7

Little more memory used and no measurable difference (maybe even slightly faster) ...

=> RTBC (from a performance perspective)

Still needs tests

thedavidmeister’s picture

Assigned: Unassigned » thedavidmeister

writing tests..

thedavidmeister’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
6.52 KB
9.42 KB

Tests.

thedavidmeister’s picture

Assigned: thedavidmeister » Unassigned
Fabianx’s picture

Status: Needs review » Reviewed & tested by the community

RTBC per #10:

Has nice test coverage, makes it possible to use l() to return the structure to re-use for theme_more_link and theme_link, keeps things fast and makes markup-utility functions possible.

=> RTBC

shanethehat’s picture

Nice looking tests @thedavidmeister, thanks for stepping in with those.

c4rl’s picture

Status: Reviewed & tested by the community » Needs work

I find it problematic that this introduces a change to a common API function that returns a different variable type depending on the arguments *as an API.*

The only other place in PHP/Drupal where this really happens is things like strpos(), node_load()/entity_load(), and old-style iterators like mysql_fetch_object() to indicate some sort of exception.

I'd prefer to see this as a helper returning structure for all three l(), theme_link(), theme_more_link() etc, rather than a first-class API function.

joelpittet’s picture

@c4rl re #16 could you explain your 'helper' idea a bit more?

The problem you explained seems clear, though the solution does not.
A concrete example would probably be best because 'helper' has about as much meaning as 'block' or 'container' does. Also, I heard l() was a 'helper' from a few people... so for me that made things extra confusing.

thedavidmeister’s picture

Assigned: Unassigned » thedavidmeister

I'm having a look at this over the weekend. I think I have a good idea of what @c4rl is talking about in #16.

thedavidmeister’s picture

Assigned: thedavidmeister » Unassigned

I was playing around with this, and I wasn't sure that what I was doing was even useful until a patch is committed elsewhere to formally enable separate prepare/render phases in drupal_render() - essentially the whole "drillability" issue.

All I discovered is that rather than l() returning structured output, it is likely that #type link will have its current internal logic prepended to the logic inside l() inside some "drupal_prepare_link()" style function (excluding the final line that concatenates the link markup together) and that l() will stay the same as it is in HEAD with an extra comment along the lines of "This logic is the same as drupal_prepare_link() but is in-lined for performance reasons".

thedavidmeister’s picture

#2044105: Introduce #alters for \Drupal\Core\Render\Renderer::render() this could be relevant to #type 'link', the "optional structured data" thing could be just running the alter hooks for #type 'link'.

Call me crazy, but l() could one day just be a wrapper around a #type 'link' render array.

thedavidmeister’s picture

Issue summary: View changes

Updated issue summary.

star-szr’s picture

Issue summary: View changes

Just want to bump this and see if it's still relevant in the 'new world' of l() being changed so much.

joelpittet’s picture

Version: 8.0.x-dev » 9.x-dev
Status: Needs work » Postponed

Don't think we have the steam or need to get this in to 8.x.x so postponing to D9 or possibly closing as there may no longer be a need for this.

Fabianx’s picture

Status: Postponed » Closed (won't fix)

I think we close that, l() is dead ...

Version: 9.x-dev » 9.0.x-dev

The 9.0.x branch will open for development soon, and the placeholder 9.x branch should no longer be used. Only issues that require a new major version should be filed against 9.0.x (for example, removing deprecated code or updating dependency major versions). New developments and disruptive changes that are allowed in a minor version should be filed against 8.9.x, and significant new features will be moved to 9.1.x at committer discretion. For more information see the Allowed changes during the Drupal 8 and 9 release cycles and the Drupal 9.0.0 release plan.