[wp-hackers] Addition to template-loader.php

Mike Schinkel mikeschinkel at newclarity.net
Sat Apr 4 22:54:50 GMT 2009


Yes I know it doesn't seem right at first glance but it is correct logic (ignoring what I've since discovered which I'll address below.) 

The fact that null does not equal false is exactly why I used it.  However, the other things I learned make this a moot point. So I realized as I was writing my reply that it would be good to have a way to write for backward compatibility, i.e that the newly written plugin could fall back to using "exit" for earlier versions of WordPress. My first try was to pass "true", like this:

   if (apply_filters('template_redirect',true)===false) {
   } else if ( is_robots() ) {

Then in my plugin I could write code like this:

   function my_template_redirect($args) {
      if ($_SERVER['REQUEST_URI']=='/foobar/') {
	 echo 'Hello world!  Bye.';
	 if (is_null($args))
	    return false;

I soon realized that none of the internal hooks calling template_redirect return any values so my "true" gets overwritten with null before I can see it. So I decided to set a constant in template-loader.php so I can test it in my template_redirect (kludgy yes, but...):

   function my_template_redirect() {
      if ($_SERVER['REQUEST_URI']=='/foobar/') {
         $GLOBALS['wp_query']->is_404 = false;
         echo 'Hello world!  Bye.';
         if (!defined('TEMPLATE_REDIRECT_IS_FILTER'))
            return false;
HOWEVER, I then realize that by now 404 has been set and nocache_headers() has been called in /wp-includes/classes.php with this code:

   function handle_404() {
      global $wp_query;

      if ( (0 == count($wp_query->posts)) && !is_404() && !is_search() && ( $this->did_permalink || (!empty($_SERVER['QUERY_STRING']) && (false === strpos($_SERVER['REQUEST_URI'], '?'))) ) ) {
         // Don't 404 for these queries if they matched an object.
         if ( ( is_tag() || is_category() || is_author() ) && $wp_query->get_queried_object() ) {
            if ( !is_404() )
               status_header( 200 );
         status_header( 404 );
      } elseif ( !is_404() ) {
         status_header( 200 );

Then I thought it would be possible to get around the 404 within a template_redirect hook by calling status_header(200) and setting the cache headers correctly and by setting $GLOBALS['wp_query']->is_404 that is very kludgy. Even so, as I start testing I realized that 
redirect_canonical() which is triggered in core on the 'template_redirect' action, and it returns false so as I'm looking at this I'm starting to think there is no robust way to make 'template_redirect' be backward and forward compatible by convert it into a filter. 

This brings me back to my original assertion which is that we really needs another way to handle custom URLs, and to answer Westi's question, "No, the template_redirect hook does not meet the needs because by then it is too late, 404 and nocache headers have been set." We need to discover the custom URL somewhere before handle_404() decides that a request is a 404. 

This forced me to start digging deep to find the best way to accomplish this.  I found many ways it could be done but I wanted to change as little code as possible and introduce no side-affects. I came up with the following but would be happy to see an alternative that would produce the same results:

-- Add var custom_url_func to /wp-includes/query.php
-- Add is_custom_url(), set_custom_url_func($func), & get_custom_url_func() to /wp-includes/query.php
-- Add a test for is_custom_url() in /wp-includes/classes.php in the handle_404() function and if true it will bypass other 404 tests.
-- Add the follow code in /wp-includes/template-loader.php just before the call to do_action('template_redirect');

   if (is_custom_url() && $func = get_custom_url_func()) {
      $continue = call_user_func($func);
      if (!$continue) return;

With these changes to core the following code allows a plugin or theme developer (almost) full control of the URL by hooking 'parse_query' and calling set_custom_url_func('my_custom_url_func'), i.e.:

   function my_parse_query() {
      if ($_SERVER['REQUEST_URI']=='/foobar/')
   function my_foobar_func() {
      echo 'Hello FooBar!  Bye.';

Granted, this might not be the best way to accomplish this. For example, using a new filter might be a better way to accomplish this but I was trying not to add additional filter processing overhead assuming that might be a concern. I'm not attached to this particular solution, I just want to be able to get proper control of custom URLs that don't fit any of the existing patterns that have been baked into WordPress core and ideally I don't want it to have to be uber-complex for the plugin or theme developer either.

I've submitted a patch to Trac for this, ticket #9460: http://core.trac.wordpress.org/ticket/9460

-Mike Schinkel
Custom Wordpress Plugins

----- Original Message -----
From: "Ryan McCue" <lists at rotorised.com>
To: wp-hackers at lists.automattic.com
Sent: Saturday, April 4, 2009 11:23:12 AM GMT -05:00 US/Canada Eastern
Subject: Re: [wp-hackers] Addition to template-loader.php

Mike Schinkel wrote:
> Given my learning more about template_redirect here's what I'd ask for
> instead which should be backward compatible. Add the following code in place
> of "if ( is_robots() )":
>        if (apply_filters('template_redirect',null)===false) {
>                return;
>        } else if ( is_robots() ) {

If there are no filters for 'template_redirect', null does not equal
false with a type-sensitive comparison. You want to specify false in the
place of null, or use a non-sensitive comparison.

Ryan McCue

wp-hackers mailing list
wp-hackers at lists.automattic.com

More information about the wp-hackers mailing list