Template handlers

Perch has a flexible tag-based templating language which can be augmented with custom tags. Template handlers can hook into each phase of template parsing to add custom functionality to templates. Template handlers are implemented with an app.

A template handler is a PHP class that enxtends the PerchAPI_TemplateHandler base class. It specifies a mask for the tags it handles, and then a method to be called for each phase of parsing.

When to use

You don’t need to use a template handler for basic content substitution - Perch will handle that for you. The case whereby you need a template handler is if you have tags that need to do something out of the ordinary. Examples would be

  1. Rendering tag pairs - that is a block with opening a closing tags that loops around the contents.
  2. Rendering tags outside the scope of your own app - handling a tag no matter if it appears in a blog template, a content template or wherever.

Regular use - just substituting content within your own app - does not need a custom template handler. Perch will take care of that as standard.

Registering your template handler

In your app’s runtime.php and admin.php files, register the name of your template handler class with:

PerchSystem::register_template_handler('MyApp_Template');

When templates are parsed, the MyApp_Template class will be instantiated, so make sure the file is either included or handled by your apps autoloader.

The template handler class

By convension, your template handler should be within your app’s folder and named MyApp_Template.class.php, according to your app’s naming scheme. This is not a technical limitation - if for some reason you needed multiple handlers, you can name them and register them as needed.

A boilerplate template handler class might look like the following.

class MyApp_Template extends PerchAPI_TemplateHandler
{
    public $tag_mask = 'students|student';

    public function render($vars, $html, PerchTemplate $Template)
    {
        return $html
    }

    public function render_runtime($html, PerchTemplate $Template)
    {
        return $html;
    }

}

Tag mask

The handler specifies a tag mask to signify which tags this handler is to be used for. The tags name are pipe-delimited. This is example, our templater hander declares the mask 'students|student' to work with perch:student tags:

<perch:student ...>

and also perch:students tags:

<perch:students></perch:students>

The tag mask is crucial to specify - without it the template engine won’t know about your tags and so they’ll get stripped out of the final output.

Phases of parsing

The template engine has two distinct parsing phases. The primary phase deals with the majority of substitutions and is considered deterministic. The given input will always result in the same output, and that output can be cached and used again freely.

The second phase is what we call runtime parsing, and is always performed at runtime, even if the previous phase has been cached. Runtime parsing is used when the result is dependant on environmental factors such as user input or session state.

Form tags are always parsed at runtime, as the state changes based on user input - at one load it might show an empty field, and a second load might display a validation message, for example. The Members app also renders exclusively during the runtime phase, as the results will be different depending on whether the user is currently logged in or not.

The primary parsing phase uses the render() method, and the runtime phase uses render_runtime(). Which to use depends on when you need your tags to be parsed. Keep in mind that while all tags could be parsed at runtime, to do so surrenders any opertunity for caching, so don’t be lazy, try to pick the best phase for each use case.

Rendering your tags

Templates are tag-based, and are passed to both render methods as a chunk of code featuring a mix of (typically) HTML and other template tags. Some of those tags might be yours and some might not. Your job is to find your tags, process them and return the resulting code. Each handler is called in turn until all the tags are processed.

Internally, Perch parses the code for tags using regular expressions. Fear not, you don’t have to do this yourself - we have all sorts of built-in functionality to do it for you.

Be light

An important principal to remember is that your handler will be called many times during the rendering of a page. Sometimes it will have work to do and sometimes not. A simple, light string check for the presence of your tag before you start can save you doing any unnecessary processing.

if (strpos($html, 'perch:student') !== false) {
    // our tag exists
}
return $html;

This can make additional template handlers very inexpensive to include, adding minimal overhead when not needed.

Replacing a single tag

If you want to replace a single tag using your own content, you can do that using the passed in $Template instance:

$my_content = ['first_name'=>'Joe'];
$html = $Template->replace_content_tags('student', $my_content, $html);

Replacing a tag pair

A tag pair (opening and closing tag with something inbetween) is used in Perch templates when you need to loop around a section of the template. Parsing them requires a bit more work. Take our example tag:

<perch:students>
    <li>
        <perch:student id="first_name">
    </li>
</perch:students>

Here we want to findn the perch:students tags so that we can loop around our set of students and write out their names.

$tag_type          = 'students';
$empty_opening_tag = true;
$index_in_group    = null;
$callback          = 'render_students';

$html = $this->parse_paired_tags($tag_type, $empty_opening_tag, $html, $vars, $index_in_group, $callback)

The callback method might look like this:

private function render_students($type, $opening_tag, $tag_contents, $exact_match, $html, $vars, $index_in_group=false)
{
    $replacement = 'The modified result';

    return str_replace($exact_match, $replacement, $html);
}

The difficult bit is what happens in between.

Working with a tag

In the callback method, one of the arguments passed is the opening tag of our tag pair. It’s handed to us as a string. If we wanted to examine it to look at its attributes, we can turn it into a PerchXMLTag like so:

$Tag = new PerchXMLTag($opening_tag);

You can find out more about PerchXMLTag in its reference section.

Subtemplating

In order to loop through the matched contents, the simplest way to deal with it is to treat it as a subtemplate. Load up a new instance of the template engine, and load the contents in as a string.

If $vars['students'] contained an array of the content we wanted to template:

$ItemTemplate = $API->get('Template');
$ItemTemplate->set_from_string($tag_contents, 'student');
$replacement = $ItemTemplate->render_group($vars['students'], true);