Make WordPress Core

Opened 14 years ago

Closed 14 years ago

Last modified 5 years ago

#16128 closed feature request (worksforme)

WordPress Template System Override

Reported by: pearsonified's profile pearsonified Owned by:
Milestone: Priority: normal
Severity: normal Version: 3.1
Component: Themes Keywords: has-patch 2nd-opinion close
Focuses: Cc:

Description

As it's currently constructed (in version 3.0.4), the WordPress template loader (wp-includes/template-loader.php) is relatively inflexible. No matter how a theme operates, it's forced through the series of if-statements and template "fetches" defined in lines 23–41 of template-loader.php.

Fortunately, by making very simple modifications to this file, we can accommodate themes/frameworks/template systems that may have different (and, ideally, more efficient) ways of determining which template to show and how to show it.

This is sensible because developers only need the contents of $wp_query in order to determine which template to show. While the current workings of the WP template system include a lot of "convenience" functions, these unnecessary function calls could be skipped entirely by relying upon the $wp_query->is_[x] variables.

My proposal for a template system override is quite simple, and it introduces one new constant and one new hook into the core. The constant, WP_THEME_TEMPLATE, is a boolean value that theme developers should place in their functions.php file and set to TRUE only if they have defined their own template system. The hook, theme_template, is the "tie-in" point for the developer's template system.

The template-loader.php file must be modified to accommodate the constant and hook described above. Fortunately, this can be done in 4 lines of code, as evidenced by (new) lines 22–24 and line 48 in the attached template-loader.php file.

I like this template system solution because it does not affect any current themes, developers, or development practices. Despite this, it opens to door to innovation and places the dev community in a position to come up with mind-blowing new ways to handle template/theme functionality.

Attachments (2)

template-loader.php (2.0 KB) - added by pearsonified 14 years ago.
template-loader.php.diff (3.2 KB) - added by nathanrice 14 years ago.

Download all attachments as: .zip

Change History (20)

#1 @dd32
14 years ago

  • Keywords has-patch 2nd-opinion added; themes templates removed
  • Milestone changed from Awaiting Review to Future Release
  • Type changed from enhancement to feature request

Similar requests have been asked before; so i'm marking this as Future Release; 2nd-opinion.

#2 @nathanrice
14 years ago

I like the idea ... theme authors really should have a little more control in the template loading arena, if for no other reason than to extend (rather than replace) the current system.

However, instead of using a constant, let's take advantage of the theme support functionality. Adding theme-specific functionality is a perfect use for those functions.

In my patch, the code checks to see if the theme supports 'theme-templates'. Probably needs a better naming scheme, but this one will do for testing purposes, and can be renamed later. All that needs to be done to activate custom theme templating is to add the following to a theme's functions.php file:

add_theme_support('theme-templates');

Patch attached ...

Last edited 14 years ago by nathanrice (previous) (diff)

#3 follow-up: @duck_
14 years ago

Why can't template_redirect be used for this?

#4 in reply to: ↑ 3 ; follow-up: @nathanrice
14 years ago

Replying to duck_:

Why can't template_redirect be used for this?

The idea is to circumvent the standard template loader. I'm not sure where template_redirect fires (either before or after the template loader runs) but the idea is to stop the standard template loader from running if there is an alternate loader present.

#5 @nathanrice
14 years ago

  • Cc nathanrice added

#6 in reply to: ↑ 4 @greuben
14 years ago

Replying to nathanrice:

Replying to duck_:

Why can't template_redirect be used for this?

The idea is to circumvent the standard template loader. I'm not sure where template_redirect fires (either before or after the template loader runs) but the idea is to stop the standard template loader from running if there is an alternate loader present.

Current theme supports? constants? Sounds scary to me.

Okay let's say if you have used current_theme_supports 'theme-templates' or 'WP_THEME_TEMPLATE', what do you do with the action theme_template? check for same is_* and load files with different name??

You can either use 'template_redirect' or 'template_include' action to load whatever template you want.

Or did I get you wrong?

#7 follow-up: @nacin
14 years ago

  • Keywords close added

Small picture: There's no need to keep an action under lock and key unless a theme supports it.

Big picture: template_redirect is the *perfect* hook here. It's designed to be used to include a template and die. If you want to override the template hierarchy entirely, then you already have this power.

#8 in reply to: ↑ 7 ; follow-up: @pearsonified
14 years ago

Nacin, the problem with not changing anything is that you cannot prevent the rest of the template-loader.php file from running, as Nathan mentioned in his response to duck_ above.

Any thoughts on that end of things?

#9 in reply to: ↑ 8 ; follow-up: @duck_
14 years ago

Replying to pearsonified:

Nacin, the problem with not changing anything is that you cannot prevent the rest of the template-loader.php file from running, as Nathan mentioned in his response to duck_ above.

Any thoughts on that end of things?

"include a template and die"

untested:

add_action( 'template_redirect', 'this_is_a_silly_example' );
function this_is_a_silly_example() {
    if ( is_single() ) {
        include( TEMPLATEPATH . '/path/to/my/template.php' );
        exit;
    }
}

#10 in reply to: ↑ 9 ; follow-up: @pearsonified
14 years ago

Replying to duck_:

Replying to pearsonified:

Nacin, the problem with not changing anything is that you cannot prevent the rest of the template-loader.php file from running, as Nathan mentioned in his response to duck_ above.

Any thoughts on that end of things?

"include a template and die"

untested:

add_action( 'template_redirect', 'this_is_a_silly_example' );
function this_is_a_silly_example() {
    if ( is_single() ) {
        include( TEMPLATEPATH . '/path/to/my/template.php' );
        exit;
    }
}

IMO, exits and breaks (except when used in switch statements) are not elegant, and generally speaking, you can find ways to write workable code without the need to force anything in this manner.

#11 in reply to: ↑ 10 @pearsonified
14 years ago

Replying to pearsonified:

Replying to duck_:

Replying to pearsonified:

Nacin, the problem with not changing anything is that you cannot prevent the rest of the template-loader.php file from running, as Nathan mentioned in his response to duck_ above.

Any thoughts on that end of things?

"include a template and die"

untested:

add_action( 'template_redirect', 'this_is_a_silly_example' );
function this_is_a_silly_example() {
    if ( is_single() ) {
        include( TEMPLATEPATH . '/path/to/my/template.php' );
        exit;
    }
}

IMO, exits and breaks (except when used in switch statements) are not elegant, and generally speaking, you can find ways to write workable code without the need to force anything in this manner.

The theme_template proposal eliminates the need to do this, which is one of the (many) things that I find attractive about it.

#12 follow-up: @nacin
14 years ago

  • Milestone Future Release deleted
  • Resolution set to worksforme
  • Status changed from new to closed

Replying to pearsonified:

Nacin, the problem with not changing anything is that you cannot prevent the rest of the template-loader.php file from running, as Nathan mentioned in his response to duck_ above.

Any thoughts on that end of things?

Yep. I hinted to it in my last comment, but -- nothing else runs after template loader. So just exit or die when you're done to kill the script. Including the die is pretty standard whenever you need to use template_redirect to load a template, because whenever a plugin wants to override that, they'll need to stop the rest of the template hierarchy from firing. This is standard practice in plugins, and it really isn't that inelegant at all. Again, nothing else ever fires after the template loader -- imagine a die() at the end of the file already. You're just short-circuiting it.

Make sense? I better understand the problem and use case that you and Nathan laid out, and I'm confident this is the exact hook you're looking for, so I'm closing this as worksforme.

Cheers :-)

#13 in reply to: ↑ 12 @pearsonified
14 years ago

Replying to nacin:

Replying to pearsonified:

Nacin, the problem with not changing anything is that you cannot prevent the rest of the template-loader.php file from running, as Nathan mentioned in his response to duck_ above.

Any thoughts on that end of things?

Yep. I hinted to it in my last comment, but -- nothing else runs after template loader. So just exit or die when you're done to kill the script. Including the die is pretty standard whenever you need to use template_redirect to load a template, because whenever a plugin wants to override that, they'll need to stop the rest of the template hierarchy from firing. This is standard practice in plugins, and it really isn't that inelegant at all. Again, nothing else ever fires after the template loader -- imagine a die() at the end of the file already. You're just short-circuiting it.

Make sense? I better understand the problem and use case that you and Nathan laid out, and I'm confident this is the exact hook you're looking for, so I'm closing this as worksforme.

Cheers :-)

Andrew, thanks for the response!

I suppose the bigger question here is this: The template system is designed to work one specific way, but it can be hax0red to work differently with the template_redirect hook. Instead of requiring a hook for modification, why not treat all template systems the exact same by firing them at one, consistent time? If the developer chooses which system to show, I don't see why this would be a problem for anyone.

From a more general standpoint, I'm suggesting that any template system should be considered to be of type X, regardless of whether it's the standard WP template system or a new one that no one has built yet. However, under the current (unchanged) construct, you have two template system types: X and Y, where Y is any template type that is fired from the template_redirect hook.

The coder in me sincerely wants to consolidate these different types into one, unified structure. IMO, it's the right thing to do.

#14 follow-ups: @scribu
14 years ago

Agree with closing. The current system for short-circuiting the default template loader is pretty neat IMO.

The only problem is that template_redirect is also used for other things, which should run before die().

The posts_selection action should be advertised more.

#15 in reply to: ↑ 14 @nathanrice
14 years ago

Replying to nacin:

Yep. I hinted to it in my last comment, but -- nothing else runs after template loader. So just exit or die when you're done to kill the script.

I see what you mean. I didn't realize the template loader was the last (I mean LAST) thing to run.

#16 in reply to: ↑ 14 @nathanrice
14 years ago

Replying to scribu:

The only problem is that template_redirect is also used for other things, which should run before die().

If you hook to template_redirect and need it to be the LAST thing to execute, you're right. Lots of plugins use that hook. I guess using a 100 priority is safe enough :-)

#18 @dhurlburtusa
5 years ago

I see how a template developer could use template_redirect to use their own template resolution algorithm and then exit/die but they are going to have issues with WP core and plugins because of different action priorities.

Also, using an action named template_redirect implies an HTTP redirect -- not a way to handle certain requests and exit. Using it for other reasons is really a misuse of the action hook.

Also, the "short-circuiting" code that follows the template_redirect action would likely need to be duplicated in the theme developer's template_redirect action.

if ( 'HEAD' === $_SERVER['REQUEST_METHOD'] && apply_filters( 'exit_on_http_head', true ) ) {
	exit();
}

// Process feeds and trackbacks even if not using themes.
if ( is_robots() ) :
	do_action( 'do_robots' );
	return;
elseif ( is_feed() ) :
	do_feed();
	return;
elseif ( is_trackback() ) :
	include( ABSPATH . 'wp-trackback.php' );
	return;
endif;

As a theme developer, it would be nice to not have to keep these short-circuits in sync with WP core.

If you are just trying to choose a different algorithm for resolving a page template, then having that ability where the current template hierarchy algorithm is done (after the "short-circuiting"/redirect code) would be ideal.

Several very popular plugins I have examined use template_redirect for short-circuiting the request for whatever their reason. None of them have been doing it to choose a different page template algorithm.

For example, WordFence uses it to short-circuit (not a redirect but an exit) with a priority of 1000.
Yoast uses it for several reasons:

  • Do a redirect for site-maps using priority 0.
  • Do a redirect to an attachment using priority 1.
  • Do a redirect for replytocom using priority 1.
  • To add a response header using priority 10.
  • And for another reason not important to this discussion.

WordPress core uses it for several reasons:

  • Old slug redirect using priority 10.
  • Perform a canonical redirect using priority 10.
  • A redirect in multi-site, MU, using priority 10
  • Redirect to admin URL or login URL when non-canonical URL used using priority 1000.

So, what priority does a theme developer choose to ensure WP core and the installed plugins get a chance to handle their redirects when appropriate? Would it be safe to choose PHP_INT_MAX? Maybe.

What is being done with the template_redirect action sounds very similar to middleware in Express apps (this is a Node.js thing for those that don't know) or ServletFilters in J2EE (a Java server thing for those that don't know).

Some Food for Thought

Basically a chain of actions/tasks is set up. The first link in the chain gets an opportunity to tweak or completely handle the request. If it doesn't complete the request, then there is a mechanism that allows the next link to run.

So, basically template-loader.php, is a list of actions with resolving a theme page template to include as the last action in the chain (assuming wp_using_themes() is true).

The suggestion/patch in #48175 allows one to either use the current template hierarchy algorithm or replace it with their own. It provides a function that encapsulates the template hierarchy algorithm, which could still be used in the custom template resolution algorithm depending on the request.

Note: See TracTickets for help on using tickets.