Trigger Node Template


Trigger Node is a template compiler for use with php 4 or 5, written by Malcolm Dew-Jones, licensed via the LGPL ("Lesser GPL"). The essential feature of Trigger Node is the strategy it uses to map regions of the template, called "components", into code within your application called the "logic".

What else do you need to know? A template is an XML file, but Trigger Node has features to help work with HTML. It compiles into php code, which it caches, so templates can run as fast as possible. It handles all the normal things like nesting, recursive data structures, variables, and template libraries, but unlike most template systems, Trigger Node has no control structures. Instead it provides a simple consistent method for mapping components within the template into logic provided by your application.



DOWNLOADING AND INSTALLING

Download http://members.shaw.ca/malcolm.dewjones/TriggerNodeTemplate/TriggerNodeTemplate-x.x.zip and unzip it. Read the README file for install instructions.


LOADING AND LAUNCHING A TEMPLATE

Programs that compile templates at runtime.

    require_once("tnt.php");
    tnt_load_template('my_template.htm','/path/to/cache/directory');
    
    # The name of the template class is same as the file name just loaded.
    # The name of the logic class is whatever you define it to be.

    $template = new my_template( new my_logic_class() );
    $template->start();

The cache directory is required only if you wish to use caching. The cache directory may or may not need to be writable by the userid running the php script. The name of the loaded template is based on the file name (so the file name must be usable as a php class name).

Using a non-writable cache directory. (Also see next section for using templates without any runtime support from tnt.)

You can use a non-writable cache directory as long as the templates are compiled at least once and the template source is not then updated. To use a non-writable cache directory, do one of the following:

  1. Temporarily make the directory writable. Then run the application, making sure each template is used at least once. Then make the directory read-only.
  2. Run the php routines under a non-standard userid that does have write access to the cache directory. Either do this from the command line, or via a non-standard web access that uses a different userid, or on a different server and then copy the files while retaining the time stamps.

HTML kludges, warnings, and cache control. tnt_load_template accepts four additional parameters not shown above, which control the use of html compatibility options (kludges) and warnings and ignoring the cache. All of these option are turned off by default. The value to pass in is true or false. NOTE: the most suspicious use of a non-tnt tag is "name=", but that tag cannot be warned since it is so common. Therefore the name of "name" may be changed in the future, probably to "part". (It ought to be "component" but that is too long for me to type!)


    tnt_load_template( file
                      ,cache_dir 
                      ,input_kludges    # xmlize <br>, &nbsp;, etc
                      ,output_kludges   # htmlize <br/> <hr/> etc
                      ,warnings         # suspicious non-tnt tags
                      ,always_compile   # pretend cache is empty
                     );

Using pre-compiled templates.

    require_once("your_template.html.ph"); 

    # The name of the template class was created at compile time,
    # this example assumes the name of the file has not been changed.
    # The name of the logic class is whatever you define it to be.

    $template = new my_template( new my_logic_class() );
    $template->start();

The pre-compiled template is just an ordinary php file and can be loaded like any other php file. The name of the template was determined at compile time. I suggest you use that name as the file name, but it is not required.

One way to pre-compile a template is to use a cache in development and then use the cached files in production. This is similar to what is described above, except that you can skip loading the tnt.php file entirely. Another technique is to use a compile utility (which may be provided as an example).


CREATING TEMPLATES -- COMPONENTS, TAG ATTRIBUTES, LOGIC HANDLERS

The structure of a template is controlled by various attributes (not tag names). A component is the term used by tnt for the regions of text identified by the attributes. The attributes are summarized here, and then described in more details below.

The template reproduces itself when run, except that attributes starting with tnt: are not reproduced in the output, and tags starting with tnt: are also not reproduced in the output.

If the tag name uses the tnt: prefix then the prefix is optional on the attributes.

Templates are XML files, but it is easy to use an HTML file as a template because HTML and XML are closely related. (TNT also has a couple of kludge options that may be of assistance for this.) Do the following...

The following attributes are recognized for use by tnt.

The following processing instructions are recognized for use by tnt.

Macros are snippets of php code that return values. A macro is tagged with {+ +}. These are not xml tags on purpose so that you can view the macro while designing the template with a tool like a browser.

Compiler Option Functions: There are a number of functions that can be called using the <?tnt processing command.


TRIVIAL EXAMPLE.

This example is complete but trivial. It shows the two files commonly involved in simple a tnt template project. The first is the template. The second is the php program. The php program serves two purposes, it runs your application (such as a web page) but it also provides the logic required by the template.

Trivial tnt project - two core files, and the resulting output.

trivial.htm
-----------

    <html>
    <head>
    <title> Trivial TNT Example.
    </title>
    </head>
    <body>
    
    <p tnt:name="part1($a,$b)" >
    This paragraph contains two variables, $a={+$a+}, and $b={+$b+}.
    </p>
    
    </body>
    </html>


trivial.php
-----------

    <?php
    require_once("tnt.php");
    tnt_load_template("trivial.htm","cache");
    
    class trivial_logic
    {
        function part1_logic(&$vobj)
        {
            $vobj->start('A','B');
        }
    }
    
    $t=new trivial();
    $t->tnt_set_logic(new trivial_logic() );
    $t->start();
    ?>


The output.
-----------

    <html>
    <head>
    <title> Trivial TNT Example.
    </title>
    </head>
    <body>
    
    <p>
    This paragraph contains two variables, $a=A, and $b=B.
    </p>
    
    </body>
    </html>


REFERENCE - ATTRIBUTES

tnt:name="[ NAME ][ ($p1,...) ]"

Create a component using the indicated name. If the name is empty or if this attribute is not specified then the component has no name. In that case the logic for the component must be specified.

A parameter list is optional. Parameters must be valid php variable names. The parameter list defines the parameters accepted by the component (in contrast to the argument list in the tnt:logic statement).

The parameter list may be included even when the name is empty so as to allow an anonymous component to declare the parameters it will be receiving from a logic handler.

The parameter variables can be accessed within the text of the component using the tnt macro syntax - e.g. {+$p1+}, or within php expressions in the argument lists within tnt:logic statements. Note however that parameter variables are not accessible within sub-components of a component (even though they are within the lexical scope of the component), they are only accessible within the top level of the component itself. See tnt:vars for fully lexically scoped template variables.

The name of the component is used to derive the default name of the logic handler for the component.

tnt:logic="[ NAME ][ ($arg1,...) ]"

Instruct the template to use the indicated logic handler method. If the name is empty or if this attribute is not specified then the name is derived from the name of the component. The actual name of the logic handler always has the string "_logic" appended. E.g. tnt:logic="X1" will invoke a method called X1_logic().

An argument list is optional. Arguments must be valid php expressions. The argument list provides the values output from the component into the logic handler (in contrast to the parameter list in the tnt:name statement).

The argument list is in the direct scope of the enclosing component. Therefore the expressions can make use of both the parameter variables of the parent, as well as the template variables that are in scope from any enclosing component. E.g. the following argument list could be valid for some templates

<ATAG tnt:logic="X1($p1,$this->tnt_get_var('J'))" >

tnt:child="NAME[ ($p1,...) ]"

Create a sub component within a component. The logic for the component can access the child as a method using the child's name. The child accepts parameters. If not explicitly defined then the parameters have the same names as the parent's parameters. The parameters of the child are not the same variables as the parent, even if the names are the same - the logic must pass in the appropriate values for the parameters even if the parameter names are the same as the parent of the child.

tnt:invoke="NAME[ ($arg1,...) ]"

Run the logic associated with a component defined elsewhere using tnt:name, passing in the argument values. The NAME must be the NAME defined by tnt:name. The arguments have the same rules as used by tnt:logic.

By default a component is "applied" at the location it is defined. (see tnt:apply). A component may also be invoked at another location in the template. Invoking a component calls a logic routine to handle the component. The component name and the logic handler name are related exactly as in the definition of a component. If the logic name is not the default name then you need to specify it using the "tnt:logic" attribute.

The invoke occurs just before the tag containing the invoke attribute. As with other tags, the tag itself is displayed or not based on the tnt: prefix in the tag name.

tnt:apply="true|false"

Prevent a named region from running by default.

By default a component is "applied" at the location it is defined. That is the same as tnt:apply="true". To define a component that is not run automatically in place can be accomplished in various ways, the most straight forward being to use tnt:apply="false" in the tag defining the component.

The default can be controlled as shown here (replace $apply_or_define with the literal text either 'apply' or 'define').

<?tnt $this->set_apply_or_define($apply_or_define) ?>

Some other alternatives are to map the component to logic that simply returns without running the component, or to put the component inside a container component that itself is not run automatically, or to put the component into a template which is loaded but not run.

tnt:globals="$v1,..."

Define access to global variables. To be accessible, php global variables must be declared. This statement preforms that function. The globals are accessible within macros within the template within the lexical scope of the tag where the tnt:globals are defined.

tnt:vars="$v1,..."

Define a set of lexically scoped variables accessible within the component or any subcomponent textually enclosed by it.

The variables must be valid php variable names, separated by commas. Notice that a variable name defined here requires the usual $ sign, but when accessed the $ sign must typically be left off of the name.

The following example macros show the techniques used to access a var variable. Assume tnt:vars="$x".

One, usable at any level in scope, {+$this->tnt_get_var('x')+} .

Two, usable within the direct scope of the parent, {+$this->x()+} or {+$this->x+} . These second two examples are of syntax that may be used in a larger scope than parameter variables, but commonly less than the full scope available via tnt_get_var, and are therefore shown really just for completeness.

These variables can also be accessed within the expressions of the tnt:logic argument lists if the tag at that stage is in scope.

tnt:call="NAME(args)"

Run any php subroutine from within the template.

The tnt:call attribute accepts a value of "NAME(args)". It is your responsibility to provide the correct names and parameters. The following examples may help.

THESE EXAMPLES ARE WRONG IN THIS VERSION


    Call a component's visual method directly.  In this case the object
    variables will not have any values, but the component parameters can
    be passed in (not shown here).

    <any-tag tnt:call="NAME::start())"> 

    Invoke a component via its logic.  Note the creation of the
    component object , and the passing of the container component
    into the new method  -- new NAME($this) --.

    <any-tag tnt:call="NAME_logic(new NAME($this))"> 

    Call some other php code if you wish...

    <any-tag tnt:call="my_function(1,2,3)"> 

The call occurs just before the tag containing the call attribute. As with other tags, the tag itself is displayed or not based on the tnt: prefix in the tag name.


REFERENCE - PROCESSING INSTRUCTIONS

<?tnt php-statements; ?>

Run PHP code during the compilation. This can run any php code, but in particular this is used to run the tnt compiler functions that control the compilation. One example is shown here. The functions are listed separately elsewhere in the reference.

   <?tnt $this->set_apply_or_define('define'); ?>

<?tnt:code php-statements; ?>

Insert PHP code as-is into an area of the compiled template where it can usefully be used to define logic methods for the template. This code is not run during the compilation, but instead becomes part of the compiled template.

<?php php-statements; ?>

Insert PHP code as-is into the template at the location of the processing instruction. Code can be inserted at any location within the template, subject to the XML rules of where the processing instructions can be placed. This code is not run during the compilation, but instead becomes part of the compiled template.


REFERENCE - MACROS AND ACCESSING VARIABLES

Macros

Macros are snippets of php code that return values. A macro is tagged with {+ +}. These are not xml tags on purpose so that you can view the macro while designing the template with a tool like a browser. Macros can be embedded in text outside of any tag, or within the values of attributes within tags.

The usual use of a macro is to embed the value of a variable, e.g. {+ $the_name +} {+$row['id']+}. Lexically scoped variables declared using tnt:vars can be accessed using $this->tnt_get_var('var_name').

Missing line breaks after macros: if your final output (i.e. when the template runs) is missing an expected line break right after a macro value, then that is because of an artifact of the way php interprets the compiled code. If this is a problem for your template then the solution is to ensure that at least one other character follows the macro tagging. The following representative sequence illustrates the problem and the solution. (The characters are spelled out, these are is not the actual bytes involved.)
problem: {+$my_value+} CR LF
solution: {+$my_value+} SPACE CR LF
Or arrange your template line breaks so that the macro tagging does not occur at the end of the lines.

$this->tnt_get_var('variable_name' [,'default-value'] )

To get the value of any object variable that is in scope use the tnt_get_var function using the syntax shown just above. A default-value parameter is optional. Object variables defined for the current component can be found using either the tnt_get_var function or directly by their own name i.e. $this->variable_name(). Object variables defined by a container have to be accessed using tnt_get_var. Parameter variables are accessed by their $name, not through tnt_get_var.


REFERENCE - COMPILER

tnt.php

This file provides a wrapper function for the compiler. I use it as-is, but it is also an example of how to use the compiler functions.

This file can be run from the command line to compile a template into the current directory. In that case the template is run, which will normally produce errors since no logic is attached, but is useful for testing the syntax of templates (and is also one way to pre-compile a template).

tnt_load_template()

See tnt.php for deeper details. This function, defined in tnt.php, is the normal way to load a template file into memory.

    tnt_load_template( file
                      ,cache_dir 
                      ,input_kludges    # xmlize <br>, &nbsp;, etc
                      ,output_kludges   # htmlize <br/> <hr/> etc
                      ,warnings         # suspicious non-tnt tags
                      ,always_compile   # pretend cache is empty
                     );

tnt-compiler.php

This file provides the tnt compiler class. This file is called by tnt.php as required, so this file is not normally accessed directly.

This file can be run from the command line to compile a template and display the result on standard output. This is useful both to test the template syntax, and also to examine the php code produced by the compiler when working with the templates.

tnt_compiler()

Create a new tnt compile object. This method is called by tnt.php as required, so this is not normally accessed directly.

load_from_file()

The compiler object has only one method to compile a template, and this is it. This method is called by tnt.php as required, so this is not normally accessed directly.


REFERENCE - COMPILER OPTIONS

$this->set_kludge_nbsp_translate($on_or_off)

Used to define HTML entities within the XML document. XML cannot use HTML entities unless they are correctly defined. When set to true, this option causes the compiler to add a <!DOCTYPE to the top of the document which defines HTML entities. This cannot be used if the document already has a <!DOCTYPE, in that case the existing <!DOCTYPE will need to declare any HTML entities that the template wishes to use.

This method must be set before compilation starts, so it is not very useful to call this method from within the <tnt? preprocessing directive.

$this->set_kludge_br_etc_tags_translate($on_or_off)

When set to true, this option causes the compiler to use str_replace to add a trailing slash to all br and hr tags. This is very kludgey. It is much better to use valid XML in the first place, in which all tags have corresponding end tags.

$this->set_reencode_chardata($on_or_off)

Control whether characters that need to be escaped within HTML (and XML) are encoded in the output, or left as-is.

$this->set_remove_nonhtml_end_tags($on_or_off)

Because the template is XML, all tags have end tags. But certain end tags are not supposed to appear in HTML. This compiler options controls whether the end tags for BR HR and INPUT are removed as the template is built.

$this->set_apply_or_define($apply_or_define)

Controls the default setting for the tnt:apply attribute.

$this->set_tnt_prefix($prefix)

This document mentions the tnt: prefix, but in fact the prefix is a variable. This compiler option control the prefix actually used.

The main purpose is to allow tnt templates to be used to build tnt templates. The first template generation would set a different prefix so that the template could contain tags and attributes that use the tnt: prefix without it being interpreted until a later stage.


Additional notes

Name spaces not used.

The xml parser is not set up to handle name spaces - the tnt prefix is a literal part of the tag name or attribute.

Use of "tnt" prefix

The tnt: prefix on attributes is not required if the tag name has the tnt: prefix, otherwise it is required. In the event that a tnt: tag has a conflict with both a tnt: and a non-tnt: attribute then the tnt: attribute takes precedence.

The tnt_ prefix on names is reserved for the tnt module, and should not be used on names defined within your program

File Name Of Template = Class Name Of Template

The template is a component. The name of the template component is the name of the template file, minus the path and any file name extension. In particular this means the file name must be usable as a php class name.

tnt:globals set by child (but not vars)

"tnt:globals" can also be set within the same tag containing a "tnt:child" attribute if not already defined earlier. This allows the globals to be defined for the entire template (which has no definition tag where they can be set).

However this cannot be done for "tnt:vars". If you wish to use object variables for the entire template then you need to define a top level component to contain them. For example <html tnt:name="template" tnt:vars="$the_template_wide_variable">. The component name must not clash with the default template name (i.e. the file name). The name "template" is probably also bad because it's likely to clash with other things.

Use of object references.

Apparently php 4 may have certain non-obvious problems with object references. The compiler uses object references, but neither tnt.php nor the compiled templates use them. So, if you wish to avoid object references in your application then make sure your templates are either cached or pre-compiled, because that way the compiler will not be loaded and so no references will be used by the templating.

BUGS

Probably. Unusable combinations of tnt attributes are not warned. Some things maybe aren't mentioned in the documentation. Others? - nothing serious known so far.

AUTHOR, LICENSE, COPYRIGHT

License for Trigger Node Template: TNT is released under the LGPL (Lesser GPL). Click here for more info on the LGPL.

VERSION 1.0 $RCSfile: TriggerNodeTemplate.htm $
$Date: 2010/02/17 02:05:35 $