Caching Template Library

From GeeklogWiki
Revision as of 02:09, 28 February 2014 by Tom (talk | contribs) (TEMPLATE VARIABLE NAMING CONVENTION)

Jump to: navigation, search

Introduction

The Caching Template Library is the template engine in Geeklog. A template engine facilitates a manageable way to separate application logic and content from its presentation. This allows the programmer to focus specifically on the application and the template designer to focus on the presentation. It also allows site administrators to easily manipulate the look and feel of their site without having to know the application or become a programmer.

The benefits of the Caching Template Library are that it adds the following features to Geeklog. These features benefit both the site administrator and the plugin developer.

  • Compiles templates to PHP code for enhanced page load speeds.
  • Adds logic processing to the templates.
  • Ability to specify multiple locations to search for templates.

Compiles Templates to PHP code

One of the unique aspects about the Caching Template Library is template compiling. This means the Caching Template Library reads the template files and creates PHP scripts from them. Once they are created, they are executed from then on. Therefore there is no costly template file parsing for each request. Each template can take full advantage of the PHP compiler and cache solutions such as eAccelerator, ionCube mmCache or Zend Accelerator to name a few. Some anecdotal experience with performance testing can be found in this forum message.

Logic Processing

One design goal of the Caching Template Library is the separation of application logic and presentation logic. This means templates can certainly contain logic under the condition that it is for presentation only. Things such as checking if a specific variable is set and adjusting the display appropriately is a good example of presentation logic. Also, if you desire no logic in your templates you certainly can do so by boiling the content down to text and variables only.

Multiple Template Source Paths

There are many templates files within the Geeklog system, plus all the template files that plugins use as well. If you want to change the look and feel of a site, generally only a small number of template files are actually modified. Having the ability to specify multiple templates paths allows you to have a base location for templates, and then simply copy the ones you wish to modify to an alternative location (usually the /custom folder in the same directory.) This reduces the overall disk storage needed on the server, and also provides a method to quickly see which templates have been modified. It also means that your modified template files will not be overwritten during a software upgrade.

Benefits

Benefits of the Caching Template Library include:

  • It is extremely fast.
  • It is efficient since the PHP parser does the dirty work.
  • No template parsing overhead, only compiles once.
  • It is smart about recompiling only the template files that have changed.
  • The {!if}..{!else}..{!endif} constructs are passed to the PHP parser, so the {!if…} expression syntax can be as simple or as complex an evaluation as you like.
  • It is possible to embed PHP code right in your template files, although this may not be needed (nor recommended) since the engine is so customizable.
  • Built-in caching support.
  • Multiple template sources.

Overall, the Caching Template Library brings a significant amount of value and features to a Geeklog site.

Developer Information

TEMPLATE VARIABLE NAMING CONVENTION

The template variables that are replace by this template class in Geeklog theme files (files with the extension thtml) must follow a strict naming convention. This class considers a template variable to be any alphanumeric characters, underscores and dashes that fall within open and closed curly brackets.

{template_variable}

If any other type of character is found within curly brackets then the text is not considered a template variable and therefor doesn’t follow the template class unknowns setting of remove, comment or keep. The text will just be left as is.

There are a few special cases where a '$' and ':' may be used in a template variable. This includes the use of the $LANG[foo] array and the Variable Manipulation Modifiers {:u, :s, :t###).

Caching_Template_Library:TEMPLATE_OPTIONS test

VERSION CHECKS

You can tell that the caching template library is installed and what version of the library is running by checking if TEMPLATE_VERSION is defined using the PHP function defined().

TEMPLATE_OPTIONS

At the top of the file is an array of TEMPLATE_OPTIONS. These options are global options for all templates created by Geeklog. They are:

'path_cache' (required)

This option points to the cache directory. If you don't like the name “layout_cache”, this is where you change it. By default it is equal to “$_CONF['path_data']”.”layout_cache/”.

'path_prefixes' (required)

This is a list of ALL paths under which templates may be found. The cached name of the template file is based on the path of the template file. These array entries are used to strip off the redundant portions of the path. The order they appear here is important because once a match is found other prefixes will not be checked. Basically they should be included in this entry with the longest paths going first.

By default, the options are:

  • the root of your themes directories ($_CONF['path_html']/layout).
  • the root of your plugins directory ($_CONF['path']/plugins).
  • the root of your server ('/') Do not remove this one!!

In a hosted environment, you might want to add your account's home directory before the last entry.

'unknowns' (optional)

Sets the default unknown handler. If it isn't set, default unknowns become 'remove', as usual. The other options are 'comment' and 'keep'.

'force_unknowns' (optional)

Sets the unknown handler regardless of the calling code's settings. This is useful for debugging a set of templates as you only have to modify the template class in one place.

'default_vars' (optional)

This is a list of template variables that is set for every instance of the template object. The most obvious use for this is stuff like $_CONF['site_url']. This way the theme author doesn't have to rely on the dev team remembering to include such variables in every template.

The default values are:

  • 'site_url' ⇒ $_CONF['site_url']
  • 'site_admin_url' ⇒ $_CONF['site_admin_url']
  • 'layout_url' ⇒ $_CONF['layout_url']
  • 'XHTML' ⇒ XHTML

'incl_phpself_header' (optional)

This boolean option is used to control the inclusion of anti-spoofing text in the resulting cache files. It defaults to true. If your cache directories can be accessed by remote browser (because you cannot create files outside your webroot) you must set this value to true or risk a security issue with your cache files.

When true, the following is added to the top of every cached template file with filename replaced with the current filename:

<?php if (strpos($_SERVER['PHP_SELF'], basename($filename)) !== false) {
    die ('This file can not be used on its own.');
} ?>

'cache_by_language' (optional)

This boolean variable determines whether or not to create unique cache files per language instance. When cache_by_language is on, a directory is created under the data/layout_cache directory for each language enabled and accessed on your website. Templates that take advantage of the Automatic Language file variables features are described below. These variables are replaced when the cache file is created instead of dynamically each time the cache is hit.

Unless you have tight filesystem restrictions, you should set this variable to true to maximize your potential system speed up.

'hook' (optional)

This advanced feature is designed for use by developers and theme makers. It is described more fully elsewhere in this documentation. It provides ways for a developer to hook calls to various methods on the Template class in order to modify the generated output without modifying the code creating the output.

TEMPLATE->set_root()

The set_root method now accepts an array of root directories. When files are added to the class using set_file, the files are checked against the array of root directories in order. The first path listed overrides subsequent paths. So the most common use for multiple roots is in plugins that might be themed. The theme directory should come first followed by the plugin's template directory. That way the theme's file take precedence over the plugin's default templates.

Plugins typically create templates using a function to guess at the correct path:

        function calendar_templatePath ($path = '')
        {
            global $_CONF;
 
            if (empty ($path)) {
                $layout_path = $_CONF['path_layout'] . 'calendar';
            } else {
                $layout_path = $_CONF['path_layout'] . 'calendar/' . $path;
            }
 
            if (is_dir ($layout_path)) {
                $retval = $layout_path;
            } else {
                $retval = $_CONF['path'] . 'plugins/calendar/templates';
                if (!empty ($path)) {
                    $retval .= '/' . $path;
                }
            }
 
            return $retval;
        }
 
        $template = new Template(calendar_templatePath('additional/path'));

This is potentially a problem if the theme doesn't copy over all the files needed by set_file. Now this is possible:

        $template = new Template(
                        array($_CONF['path_layout'].'calendar/additional/path',
                              $_CONF['path'].'plugins/calendar/templates/additional/path'
                             )
                  );

or

        function calendar_templatePath($path = '')
        {
            global $_CONF;
 
            $layout_path = $_CONF['path_layout'] . 'calendar';
            $plugin_path = $_CONF['path'] . 'plugins/calendar/templates';
            if (!empty($path)) {
                $layout_path .= '/' . $path;
                $plugin_path .= '/' . $path;
            }
 
            return Array($layout_path, $plugin_path);
        }
 
        $template = new Template(calendar_templatePath('additional/path'));

Another benefit of this is that if you only want to modify a subset of a plugin's templates for your theme, you only need to put the subset of files in the theme directory. Having multiple root directories means any files not in the themes directory are taken from the default directory.

Replacement Variable Manipulation Modifiers

Modifiers can be applied to replacement variables using the format {variable:m} where variable is a normal replacement variable and m is the modifier. Multiple modifiers can be applied in series {variable:u:s}. The following modifiers currently exist:

  •  :u Call urlencode on the variable before output.
  •  :s Call htmlspecialchars on the variable before output.
  •  :t### Truncates the variable to ### characters.

Using these options should be done with care, making sure the calling code hasn't already applied an output filter to the data. Likewise, these calls should not be used on any variable containing HTML.

Automatic Language File Variables

Add any text you want to the template while still following the Geeklog text internationaliztion guidelines. Any variable in the form {$LANG_abc[xx]} will lookup the index in the stated language array. So {$LANG_ADMIN['save']} will be replaced by the same text as that placed on all the Save buttons in the system. Template files are no longer limited to the text labels delivered by the coder. Text from the language variable is automatically passed through htmlspecialchars before output. Language variables cannot be used in simple action variables. Advanced action variables can use them.

// code like this
  $T->set_var('lang_username',$LANG_USER['name']);
  $T->set_var('username', $username);

// becomes
  $T->set_var('username', $username);

// and the template
<tr><td>{lang_username}:</td><td>{username}</td></tr>

// becomes
<tr><td>{$LANG_USER[name]}:</td><td>{username}</td></tr>

There are plans to do research at some future date into which is faster, setting a variable from a LANG array, or putting the LANG array reference directly into the template.

Logic Processing

One of the biggest benefits of the Template Caching Library is the ability to place logic into the templates. This section assumes you know how to use the old template library.

Usage

The template library uses variable substitution in template files to create output. The new library adds action variables to the mix. All template constructs are contained within braces: {variable}. The simplest construct is the variable as just shown. Actions are also contained within braces and the first character of an action is an exclaimation point: {!action parameters}. Actions usually do not output anything to the final output. Instead they control what is output around them. In these cases, the action will create a block within the template contained between two actions. The second action is usually the same as the first with the word 'end' prepended: {!endaction}.

CTL v2.2 introduced advanced actions. These have the format {!!action parameter !!}. The space and exclamation point are required. Advanced actions allow you to place more complicated values in the parameter field. Instead of assuming parameters are template variables, advanced action parameters that reference template variables must include the braces just like they do in other parts of the template. So while a simple if might look like this{!if var}, the advanced if can look like this{!!if {var} == 'a' || {var} == 'b' !!}. Advanced actions are closed by standard {!endaction} constructs. Complex conditions can also contain calls to any global function:

{!!if COM_isAnonUser({uid}) !!}

Actions

Simple Actions

Action Description
{!if variable}

{!endif}

The !if action shows the contained block if the variable evaluates to true.
{!else} This action must appear between !if and !endif. It breaks the !if block into true and false halves. The block from !if to !else contains the text displayed if the condition is true. The block between !else and !endif contains the text displayed if the condition is false.
{!elseif variable} This combines else and if together to allow multiple possible conditions to be evaluated.

Looping Actions

Action Description
{!while variable}

{!endwhile}

Similar to if, the contained block is displayed repeatedly as long as variable is true. If the variable starts out false the block is never displayed.
{!loop variable}

{!endloop}

Creates a variable called variable__loopvar. The contained block is executed 'variable' times with varialbe__loopvar counting from 1 to 'variable' for each iteration. If variable is less than zero, counting goes downward starting at -1. variable__loopvar is deleted (unset) when the loop exits.
{!break} Exits a loop prematurely. The rest of the block is not processed and processing continues after the !endwhile or !endloop.
{!continue} Ends a loop block prematurely. The rest of the block is not processed and processing continues for the next iteration of the loop.

Other Actions

Action Description
{!inc variable} The variable is incremented by 1. If it is a non-numeric string, the value '1' is placed in it.
{!dec variable} The variable is decremented by 1. If it is a non-numeric string, the value '-1' is placed in it.
{!inc+echo variable} As !inc and the number is displayed.
{!dec+echo variable} As !dec and the number is displayed.
{!set variable value} Assign the variable the listed value. This construct is not well error checked so use with extreme caution .
{!unset variable} Removes the variable from the template's internal variable list.

Advanced Actions

Action Description
{!!if condition !!} The condition can contain any template construct that does not end with !}.
{!!while condition !!} Works like the simple {!while var} but can take any complex condition.
{!!global var,… !!} Pulls the global vars into the cached PHP output.
{!!echo condition !!} Echo's the complex condition to the cached PHP output.
{!!set var condition !!} Works like the simple {!set var value} but can take any complex condition.
{!!autotag ... !!} Works like so {!!autotag story:welcome !!} and allows you to set an autotag in a template file.

Comments

You can include comments in the template files that do not appear in the cached PHP file by enclosing the comment within {# and #} symbols. This is useful for explaining why you have some weird construct in your code without cluttering the cached template with lots of HTML comments.

Examples

In this (contrived) example, the only template variable is 'count' and it is set to 3:

{# This is an example of a comment #}
{!loop count}
{count__loopvar} of {count}: {!inc+echo total}
{!!if count__loopvar == "2" !!}{!inc count}{!endif}<br>
{!endloop}

If the template is parsed twice without resetting count between parses, the output looks like this:

1 of 3: 1
2 of 3: 2
3 of 4: 3
4 of 4: 4
1 of 4: 5
2 of 4: 6
3 of 5: 7
4 of 5: 8
5 of 5: 9

This example is Geeklog specific to help illustrate how you might use even the simplest !if construct:

if ( $_USER['uid'] >= 2 ) {  // user is logged in...
    $T->set_var('onlyloggedinusers', 'Some feature only for logged in users');
}

In the template, you can now check whether the user is logged in by checking for the existence of the {onlyloggedinusers} variable:

<div id="pageheader">
  Show the page header
</div>
{!if onlyloggedinusers}
  <div class="boldtext">Logged in users can do more here, thanks for logging in!</div>
{!else}
  <div class="boldtext">If you login, you can do more!</div>
{!endif}

This is a simple example, but it does illustrate the capability and the power. No longer in the PHP code do you need to handle the non-logged in case.

PHP Code in a Template Files

This feature allows you to embed PHP code directly in templates. To embed PHP code directly in a template, use the following example:

<div class="welcomeanddate-text">
  <span class="gl_user-menu-right">
<?php global $_USER, $_CONF; if (isset($_USER['uid']) && $_USER['uid'] > 1) { ?>
    <a href="{site_url}/usersettings.php?mode=edit">{$LANG01[48]}</a><br{xhtml}>
    <a href="{site_url}/users.php?mode=logout">{$LANG01[35]}</a>
<?php } else {
if ($_CONF['disable_new_user_registration']==0) {?>
   <a href="{site_url}/users.php?mode=new">{$LANG04[27]}</a><br{xhtml}>
<?php } ?>
   <a href="{site_url}/users.php?mode=login">{$LANG01[58]}</a>
<?php } ?>

Notice how all PHP is surrounded by <?php php_code_here  ?>

Here is another example that will return the template variable 'title' from the template class (if it is set).

<?php
    echo $this->get_var('title');
?>

Troubleshooting

The most common problem you will run into with the template cache is forgetting they are there. If you go into a .thtml file and make a change and it isn't showing up, you may need to go delete the cached file as the algorithm to overwrite them only works when the file dates are correct. Some FTP systems and web consoles screw up the file times of uploaded files preventing the class from updating the cached PHP file. To ease this process, there is an entry in the Admin Command & Control screen labeled Clear Template Cache.