I’m developing a WordPress theme using a template engine. I want my code to be as much compatible as possible with WP core functionality.

Some context first

My first problem was to find a way to resolve the template starting from a WP query. I solved that one using a library of mine, Brain\Hierarchy.

Regarding get_template_part() and other functions that loads partials like get_header(), get_footer() and similar, it was pretty easy to write wrapper to template engine partial functionality.

The issue

My problem is now how to load comments template.

WordPress function comments_template() is a ~200 lines function that does a lot of things, which I want to do as well for maximum core compatibility.

However, as soon as I call comments_template(), a file is required, it is the first of:

  • the file in the constant COMMENTS_TEMPLATE, if defined
  • comments.php in theme folder, if found
  • /theme-compat/comments.php in WP includes folder as last resort fallback

In short, there’s no way to prevent the function to load a PHP file, which is not desirable for me, because I need to render my templates and not simply use require.

Current solution

At the moment, I’m shipping an empty comments.php file and I’m using 'comments_template' filter hook, to know which template WordPress wants to loads, and use feature from my template engine to load the template.

Something like this:

function engineCommentsTemplate($myEngine) {

    $toLoad = null; // this will hold the template path

    $tmplGetter = function($tmpl) use(&$toLoad) {
       $toLoad = $tmpl;

       return $tmpl;
    };

    // late priority to allow filters attached here to do their job
    add_filter('comments_template', $tmplGetter, PHP_INT_MAX);

    // this will load an empty comments.php file I ship in my theme
    comments_template();

    remove_filter('comments_template', $tmplGetter, PHP_INT_MAX);

    if (is_file($toLoad) && is_readable($toLoad)) {
       return $myEngine->render($toLoad);
    }

    return '';    
}

The question

This works, is core compatible, but… is there a way to make it work without having to ship an empty comments.php?

Because I don’t like it.

4 s
4

Not sure the following solution is better than the solution in OP, let’s just say is an alternative, probably more hackish, solution.

I think you can use a PHP exception to stop WordPress execution when 'comments_template' filter is applied.

You can use a custom exception class as a DTO to carry the template.

This is a draft for the excepion:

class CommentsTemplateException extends \Exception {

   protected $template;

   public static function forTemplate($template) {
     $instance = new static();
     $instance->template = $template;

     return $instance;
   }

   public function template() {
      return $this->template;
   }
}

With this exception class available, your function becomes:

function engineCommentsTemplate($myEngine) {

    $filter = function($template) {
       throw CommentsTemplateException::forTemplate($template);
    };  

    try {
       add_filter('comments_template', $filter, PHP_INT_MAX); 
       // this will throw the excption that makes `catch` block run
       comments_template();
    } catch(CommentsTemplateException $e) {
       return $myEngine->render($e->template());
    } finally {
       remove_filter('comments_template', $filter, PHP_INT_MAX);
    }
}

The finally block requires PHP 5.5+.

Works the same way, and doesn’t require an empty template.

Tags:

Leave a Reply

Your email address will not be published. Required fields are marked *