Class: Template

Template is a template compiler and rendering engine. It looks for templates via the pattern apps/{app}/views/{file}.html where the template is passed as 'app/file'. Failing that, it looks for layouts/{file}.html and finally layouts/default.html. It then creates a PHP version of the template and caches it to cache/{app}-{template}.php, so the cache folder must be writeable. Auto-refreshes cached versions when the originals change.

As a result, templates can include any PHP, along with tags of the form:

{{ body }}

And blocks of the form:

{% foreach pages %}
    {{ loop_index }} - {{ loop_value }}
{% end %}

{% if some_val %}
    {{ some_val }}
{% end %}

Note the use of loop_index and loop_value, which are defined for you inside foreach loops by the template engine, or you can specify your own key and value names:

{% for pages as key, page %}
    {{ key }} - {{ page }}
{% end %}

You can also test for more complex conditions, for example:

{% if ! some_val %}
    No value.
{% end %}

{% if some_val == 'some value' %}
    Value: {{ some_val }}
{% end %}

Note that endif and endforeach are valid as well as end, if you prefer, for the sake of clarity.

Here's one more example of how to loop through an array of arrays:

{% foreach my_list %}
    {% foreach loop_value %}
        {{ loop_index }}. {{ loop_value }}<br />
    {% end %}
{% end %}

To break up your template into smaller parts, you can use the inc tag to include one template from inside another. For example:

{% inc header %}

This will include the contents of layouts/header.html into the current template, with the same data passed to it as the main template file.

You can also specify subfolders in this way, to better organize your templates into themes. If you have a theme named layouts/mytheme then you can include a header.html template within it via:

{% inc mytheme/header %}

Note that this will first look for apps/mytheme/views/header.html, which would be the most frequently desired behaviour, and second it will look for layouts/mytheme/header.html, so be sure to name your themes with unique names that do not conflict with the names of apps.

Usage in PHP

To call a template, use:

<?php

echo $tpl->render ('base', array ('foo' => 'bar'));

?>

Note that arrays passed to templates are converted to objects, and objects are left as-is.

Globals

In addition to the fields in the data array passed to render(), you can also call global objects and class methods as follows from within if and foreach blocks as well as variable substitutions:

Call User::constant_value:

{{ User::constant_value }}

Call $GLOBALS['user']->name:

{{ user.name }}

Call a function:

{{ DB::shift ('select * from foo') }}

In an if block:

{% if User::is_valid () %}

{% if user.name != '' %}

In a foreach:

{% foreach Object::some_method () %}

Calling a superglobal:

{{ $_POST.value }}

Note that these must come at the beginning of a statement, not anywhere else within it. The replacement mechanism is very simplistic.

Embedding handlers

You can use special {! app/handler !} tags to embed handlers directly into your templates. These are the equivalent of calling:

{{ controller.run ('app/handler') }}

You can also pass them an array of data using a shorthand like a url:

{! app/handler?param=value&another=value2 !}

Or you can precompile them once when the template is compiled so the output of the handler is hard-coded into the template at compile time like this:

{# app/handler?param=value #}

Filters

Filtering is supported, and htmlspecialchars() is the default filter unless another is specified or 'none' is supplied via:

{{ body|none }}

Any valid function can be a filter, and filters can be chained, executing in the following order:

{{ body|strtoupper|strtolower }}

This evaluates to:

<?php echo strtolower (strtoupper ($data->body)); ?>

You can also set additional parameters to a filter as follows:

{{ timestamp|date ('F j', %s) }}

String translations

You can use the following tag format to mark strings for translation into the current visitor's language:

{" Text here "}

This will be replaced with a call to:

__ ('Text here')

Properties

public $charset = 'UTF-8'

The character encoding.

public $cache_folder = 'cache'

The cache location.

public $layouts_folder = 'layouts'

The layouts location.

public $view_folders = 'apps/%s/views/%s'

The app view locations.

public $default_layout = 'default'

Default layout filename.

public $file_extension = 'html'

File extension.

public $controller

The controller object used to run includes. The controller can be any object that satisfies the following interface:

interface AbstractController {
    public function run ($uri, $data = array ());
}

Methods

public __construct ($charset = 'UTF-8', $controller = false)

Constructor method sets the charset and receives a controller object.

public render ($template, $data = array ())

Render a template with the given data. Generate the PHP template if necessary.

public render_string ($template, $data = array (), $file_prefix = '_string_')

Render a template from a string with the given data. Generates the PHP and caches it.

public render_preview ($template, $data = array ())

Render a template string for preview purposes. Generates a temporary cached version but unlinks it immediately after use.

public parse_template ($val)

Replace values from template as string into PHP code equivalents. Note that this method never receives the original data sent to the template, so it can't accidentally embed user data into the PHP code, eliminating the possibility of exposing a security hole.

public replace_vars ($regs)

Replace variables of the form:

{{ some_var }}

Also applies filters, which can take the following forms:

{{ some_var }}                          # defaults to Template::sanitize()
{{ some_var|none }}                     # no filter
{{ some_var|strtoupper }}               # filters are php functions
{{ some_var|strrev|strtolower }}            # filters can be chained
{{ some_var|my_function }}              # calling a custom function
{{ some_var|date (%s, "F j, Y") }}      # use %s for multiple-parameter functions

public replace_includes ($regs)

Replace {! app/handler?param=value !} with calls to Controller::run(). You can also substitute sub-expressions for values using [] tags, like this: {! app/handler?param=[varname] !}

public hard_codes ($regs)

Replace {# app/handler?param=value #} with the hard-coded output from a call to Controller::run(). Note that you cannot use sub-expressions here like you can with the dynamic {! app/handler !} calls.

public run_includes ($val)

Run any includes and include their output in the return value. Primarily for page body in the admin app. This only evaluates {! app/handler !} style tags.

public replace_strings ($regs)

Replace strings with calls to __ () for multilingual sites. Translatable strings take the following form using either double or single quotes:

{" some text here "}
{' some text here '}

public static sanitize ($val, $charset = 'UTF-8')

Sanitize a value for safe output, helping to prevent XSS attacks. Please note that this method can still be insecure if used in an unquoted portion of an HTML tag, for example:

Don't do this:

<a href="/example" {{ some_var }}>click me</a>

But this is okay:

<a href="/example" id="{{ some_var }}">click me</a>

And so is this:

<span id="some-var">{{ some_var }}</span>

In the first case, if the string were to contain something like onclick=alert(document.cookie) then your visitors would be exposed to the malicious JavaScript.

The key is to know where your data comes from, and to act accordingly. Not all cases of the first example are necessarily a security hole, but it should only be used if you know the source and have validated your data beforehand.

public static quotes ($val)

Convert quotes to HTML entities for form input values. Note: This should only be done for trusted data, as it does not prevent XSS attacks.

Usage as a template filter:

{{ text|quotes }}

public static autolink ($text)

Replace links in text with html links. For use as a template filter via:

{{ text|autolink }}

Note that you will likely want to sanitize your output too:

{{ text|sanitize|autolink }}

Or even add line breaks too:

{{ text|sanitize|autolink|nl2br }}

Based on: http://stackoverflow.com/a/1959073/1092725

private static add_parent_id ($data)

Clones and adds a parent_template_id value to the template data.

public replace_blocks ($regs)

Replace foreach and if blocks. Handles the following forms:

{% foreach some_list %}
{% endforeach %}

{% foreach some_list as key, item %}
{% endforeach %}

{% for some_list %}
{% endfor %}

{% for some_list as item %}
{% endfor %}

{% if statement %}
{% elseif statement %}
{% else %}
{% endif %}

Note: for and endfor are aliases of foreach and endforeach. You can also use {% end %} as an alias for {% endforeach %}, {% endfor %} or {% endif %}.

If an as clause isn't specified, the current loop index is available via {{ loop_index }}~ and the current loop value is available via{{ loop_value }}`.

If an as clause is specified without a key, the current loop index is available via {{ loop_index }}.