[wp-trac] [WordPress Trac] #48175: Allow Template Resolution Algorithm to be Replaceable

WordPress Trac noreply at wordpress.org
Mon Sep 30 03:08:16 UTC 2019


#48175: Allow Template Resolution Algorithm to be Replaceable
-------------------------+-------------------------------------------------
 Reporter:               |      Owner:  (none)
  dhurlburtusa           |
     Type:  feature      |     Status:  new
  request                |
 Priority:  normal       |  Milestone:  Awaiting Review
Component:  Themes       |    Version:
 Severity:  normal       |   Keywords:  has-patch dev-feedback 2nd-opinion
  Focuses:               |  needs-unit-tests
-------------------------+-------------------------------------------------
 While I was writing a custom theme that doesn't use the results of the
 template hierarchy algorithm (THA), I noticed that a lot of work is done
 for nothing. That is, ultimately, several calls to PHP's `file_exists` is
 made inside `locate_template`. But I have a `template_include` filter that
 ignores the result of the template hierarchy algorithm.

 I was checking out the performance of the THA and found out that the calls
 to `file_exists` are relatively time intense. I wrapped some calls to
 `microtime` around the THA and found out that it takes about 1100 μs to
 run on my laptop with an SSD.

 So, I investigated how I could minimize the time in the THA knowing that I
 ultimately don't need the result. I discovered the
 `{$type}_template_hierarchy` and `{$type}_template` filter hooks. I used
 the `{$type}_template_hierarchy` filter to return an empty array which
 causes all the `file_exists` calls to be avoided. I also used a
 `{$type}_template` filter to quickly short circuit the THA. This allowed
 the execution time of the THA to go to about 35 μs. Much improved!

 Then I thought if there was a quicker way this could be done. So, I tried
 adding a filter called `skip_template_hierarchy` that returns false by
 default. When true is returned, then the entire THA is skipped. Therefore,
 false is passed to the `template_include` filter. In this filter is where
 I return the template based on my theme's template resolution algorithm.
 This made this section of code execute in about 5 μs. Even better.

 Then I thought whether there is a better way to do this, and I thought
 "what if we can make the template resolution algorithm (TRA) replaceable?"
 By default, the current THA would be used as the TRA. But a filter could
 be put into place that lets the user/developer choose a different TRA.

 So, I am writing a patch to add this functionality/feature. I am planning
 to name the filter `template_resolution_algorithm`. When the filter is
 applied, it will get `'resolve_template_hierarchy'` which will be a
 reference to a new function of the same name to be placed in `wp-
 includes/template.php`. This function is the default template resolution
 algorithm which of course resolves by using the THA we've all known for
 the longest time.

 After implementing this, I was surprised to see that it actually took
 about 6-7 μs to run. Not quite as fast as just skipping the THA with the
 `skip_template_hierarchy`.

 **Example**

 So, now I can use a custom template resolution algorithm like so:

 {{{
 // Add the following to the theme's function.php

 add_filter( 'resolve_template_hierarchy', function () {
         return function () {
                 $template = TEMPLATEPATH . '/templates/index.php';

                 return $template;
         };
 } );
 }}}

 The following works too:

 {{{
 function dh__resolve_theme_template () {
         $template = TEMPLATEPATH . '/templates/index.php';

         return $template;
 }

 add_filter( 'template_resolution_algorithm', function () {
         return 'dh__resolve_theme_template';
 } );
 }}}

 **Benefits**

 Obviously this can have performance benefits by being able to choose a
 different algorithm instead of running the THA and then ignoring its
 results. This would be ideal for implementing themes as single-page
 applications with minimal use of WP hooks. It would be useful if building
 a theme with an MVC-like architecture that utilizes a router mechanism.

 **Feedback**

 I'll upload my patch really soon. In the meantime, I am hoping I can get
 some feedback.

 Please note that I've only been doing PHP development for about 3 years
 off and on. Also, I've only been working with WP for a little over two
 years but only off and on. So, I can use some help making sure I am
 following PHP and WP conventions/idioms.

 But, I've been doing web development for 14 plus years. So, I am not a
 complete noob.

 **Testing**

 I can also use some guidance on where to add the unit tests for this
 particular feature. But before I write the tests, I'd like to get feedback
 first just in case this is something not even desired or maybe we come up
 with a better solution.

 I do see that there is a `tests/phpunit/tests/template.php` file to put
 `wp-includes/template.php` related tests in.

 Could someone who is familiar with the code base (core committers) point
 me at a good example of writing tests on filter hooks?

 I also need a little help with getting the unit tests to run. I've read
 the instructions at https://make.wordpress.org/core/handbook/testing
 /automated-testing/phpunit/ but they seem to be outdated, along with
 `tests/phpunit/README.md`. I tried the instructions in the project's
 `README.md`. They seem to work.

 The unit tests pass for commit 47643f7621..., but there are 6 new commits.
 The tests don't pass with these 6 new commits. I didn't try to narrow down
 which one is causing the error. Here are there errors:

 There were 12 errors:

 {{{
 1) Tests_Rel_Ugc::test_add_ugc
 Error: Call to undefined function wp_rel_ugc()

 /var/www/tests/phpunit/tests/formatting/WPRelUgc.php:14

 2) Tests_Rel_Ugc::test_convert_ugc
 Error: Call to undefined function wp_rel_ugc()

 /var/www/tests/phpunit/tests/formatting/WPRelUgc.php:23

 3) Tests_Rel_Ugc::test_wp_rel_ugc with data set #0 ('<a href="">Double
 Quotes</a>', '<a href="" rel="nofollow ugc"...es</a>')
 Error: Call to undefined function wp_rel_ugc()

 /var/www/tests/phpunit/tests/formatting/WPRelUgc.php:31

 4) Tests_Rel_Ugc::test_wp_rel_ugc with data set #1 ('<a
 href="https://wordpress.or...es</a>', '<a
 href="https://wordpress.or...es</a>')
 Error: Call to undefined function wp_rel_ugc()

 /var/www/tests/phpunit/tests/formatting/WPRelUgc.php:31

 5) Tests_Rel_Ugc::test_wp_rel_ugc with data set #2 ('<a
 href='https://wordpress.or...es</a>', '<a
 href='https://wordpress.or...es</a>')
 Error: Call to undefined function wp_rel_ugc()

 /var/www/tests/phpunit/tests/formatting/WPRelUgc.php:31

 6) Tests_Rel_Ugc::test_wp_rel_ugc with data set #3 ('<a
 href="https://wordpress.or...es</a>', '<a
 href="https://wordpress.or...es</a>')
 Error: Call to undefined function wp_rel_ugc()

 /var/www/tests/phpunit/tests/formatting/WPRelUgc.php:31

 7) Tests_Rel_Ugc::test_wp_rel_ugc with data set #4 ('<a title="Title"
 href="https:...es</a>', '<a title="Title" href="https:...es</a>')
 Error: Call to undefined function wp_rel_ugc()

 /var/www/tests/phpunit/tests/formatting/WPRelUgc.php:31

 8) Tests_Rel_Ugc::test_wp_rel_ugc with data set #5 ('<a data-someflag
 href="https:...es</a>', '<a data-someflag href="https:...es</a>')
 Error: Call to undefined function wp_rel_ugc()

 /var/www/tests/phpunit/tests/formatting/WPRelUgc.php:31

 9) Tests_Rel_Ugc::test_wp_rel_ugc with data set #6 ('<a  data-someflag
 title="Tit...ce</a>', '<a  data-someflag  title="Tit...ce</a>')
 Error: Call to undefined function wp_rel_ugc()

 /var/www/tests/phpunit/tests/formatting/WPRelUgc.php:31

 10) Tests_Rel_Ugc::test_wp_rel_ugc with data set #7 ('<a
 href="http://example.org/s...p)</a>', '<a
 href="http://example.org/s...p)</a>')
 Error: Call to undefined function wp_rel_ugc()

 /var/www/tests/phpunit/tests/formatting/WPRelUgc.php:31

 11) Tests_Rel_Ugc::test_wp_rel_ugc with data set #8 ('<a
 href="https://example.org/...s)</a>', '<a
 href="https://example.org/...s)</a>')
 Error: Call to undefined function wp_rel_ugc()

 /var/www/tests/phpunit/tests/formatting/WPRelUgc.php:31

 12) Tests_Rel_Ugc::test_append_ugc_with_valueless_attribute
 Error: Call to undefined function wp_rel_ugc()

 /var/www/tests/phpunit/tests/formatting/WPRelUgc.php:81

 ERRORS!
 Tests: 9960, Assertions: 44191, Errors: 12, Skipped: 11.
 }}}

 **Patch**

 Should I include changes to the `package-lock.json` even though I made no
 changes to the dependencies in `package.json`?

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/48175>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list