blob: 28e95559d9b0f56c9701769cc5f03de8f90c885e [file] [log] [blame]
eZ components - Template component
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. contents:: Table of Contents
Introduction
============
The template component provides a manageable way to separate
application logic from presentation data. The application logic is the PHP code
of your application, including the call to the Template component. Presentation
data are the template files.
The separation of application logic and presentation data is easier to
maintain and allows different people to work on separate parts. Another
advantage is that the Template language is more suitable for non programmers and
web designers. The language is designed to be easier to use and contains more
expressive constructs for repetitive tasks in the presentation data.
Class overview
==============
The following list sums up the most important API classes:
ezcTemplate
This class provides the main API for processing templates. This class
compiles the template to PHP code, executes it, and returns the result.
ezcTemplateConfiguration
This class configures the Template engine. Settings like: where to find the
source templates, where to store the compiled templates, the type of
context to use, are stored in this class.
ezcTemplateVariableCollection
The variables that should be send to the template or retrieved from the
template are stored in the object from this class.
More information about these classes can be found in the documentation of the
class itself.
Template component
==================
This section describes how to use the template component in your PHP
application. How to write the templates itself is described in the next
section.
Getting started
---------------
The simplest example we can come up with is writing "Hello world" to your
standard output. Basically it consists of two steps:
The first step is to create a text file "hello_world.ezt" that contains
only one line::
Hello world
and store it in the directory where your application sources reside.
The next step is to copy the following PHP script, and check if the application
works.
.. include:: tutorial_simple.php
:literal:
If you run your application, it should print "Hello world". Otherwise
check that:
- The base class can be found. Check your 'include\_path' in the PHP.ini
settings, and see if the components are included.
- The template "hello\_world.ezt" can be found. Write the absolute
path to your hello\_world template in the 'process' method.
- The Template engine can write to the current directory. By default it tries
to create the directory compiled_templates with the permissions of the
running PHP process. The next section explains how another output
directory can be specified.
Configuring the Template engine
-------------------------------
Templates are not always stored in your local directory and neither do you
want to store the compiled templates among your source files. Therefore we
have to change the configuration:
.. include:: tutorial_configuration.php
:literal:
If you try to copy/paste and run this example, you'll probably get the
following output::
Fatal error: Uncaught exception 'ezcTemplateFileNotFoundException' with message
'The requested template file </usr/share/templates/hello_world.ezt> does not exist.'
This error shows that the Template engine looks in the "/usr/share/templates" directory
for the templates. If the *hello_world.ezt* template was present at
*/usr/share/templates/* then the Template engine tries to write to the
*/tmp/compiled_templates/* directory. Make sure that this directory is writable
by the current running PHP process. Under Linux the following code makes the
directory accessible for anyone::
mkdir /tmp/compiled_templates
chmod a+rwx /tmp/compiled_templates
The ezcTemplate class calls the getInstance() method from the ezcTemplateConfiguration
class and retrieves the "default" configuration. It is possible to set multiple
configurations, which is demonstrated in the next example:
.. include:: tutorial_multi_configuration.php
:literal:
Running this example will produce the following output::
The requested template file <html/hello_world.ezt> does not exist.
The requested template file <printer/hello_world.ezt> does not exist.
The requested template file <pdf/hello_world.ezt> does not exist.
As demonstrated, the Template configuration can be set in the 'configuration' property,
given as second parameter in the process method, or use the 'default'
getInstance configuration.
Lazy initialization
-------------------
Lazy initialization is a mechanism to load and configure a component, only
when it is really used in your application. This mechanism saves time for
parsing the classes and configuration, when the component is not used at all
during one request. You can find a description how you can use it for your
own components and how it works in the `ezcBase tutorial`__. The keyword for
the template component is *ezcInitTemplateConfiguration*.
__ introduction_Base.html#lazy-initialization
.. include:: tutorial_lazy_initialization.php
:literal:
This examples shows how to configure the template component like shown in the
first example. The main difference is, that we roll out the configuration to
an own class, and define a callback using ezcBaseInit::setCallback to this
class, which will be called with a template configuration object as first
parameter on the first instantiation of the template component.
ezcBaseInit::setCallback accepts as a first parameter a component specific key,
which lets the component later request the right configuration callback. The
second parameter is the name of the class to perform the static callback on.
This class must implement the ezcBaseConfigurationInitializer class.
Each component's lazy initialization calls the static method configureObject()
on the referenced class.
When the template component is finally instantiated in line 19 of the example,
the configureObject() method will be called with an ezcTemplateConfiguration
instance used by the Template engine, which can be modified by the method.
Send and receive template variables
-----------------------------------
More often than not a PHP application sends variables to the Template engine.
Commonly those variables are displayed. It is also possible to retrieve
variables from a template. The next example sends two variables to a template.
The values are added and placed in a new variable that is returned. The code
below is the PHP code needed to send the variables to the template.
.. include:: tutorial_variable_send_receive.php
:literal:
The template code is as follows:
.. include:: tutorial_variable_send_receive.ezt
:literal:
As you can expect the result is::
Answer: 5
Template locations
------------------
Since version 1.2 it is possible to send a location object instead of a
*location* parameter of the *process* method. A location object is an object
that implements the ezcTemplateLocation interface. The interface is as
follows::
interface ezcTemplateLocation
{
public function getPath();
}
The Template engine calls the *getPath* method on the object implementing this
interface when needed. The *getPath* method returns a
string specifying the template to execute. If the return value is a relative
path, if it does not start with a slash, then the *templatePath* from the
configuration is prepended.
The next example executes the *hello\_world.ezt* template via a location
object::
class MyLocation implements ezcTemplateLocation
{
public function getPath()
{
return "hello_world.ezt";
}
}
$t = new ezcTemplate();
$t->process( new MyLocation() );
It is also possible to provide a callback for every time a template's location
is requested. Instead of supplying an object to the process() method of the
ezcTemplate object like we do above we configure the callback with the template
system's configuration::
$c = ezcTemplateConfiguration::getInstance( 'templates' );
$c->locator = new PathResolver();
The PathResolver class implements the ezcTemplateLocator interface and is
responsible for translating the paths that processed by the template engine. In
the example below it simply prepends "overridden/" to the path. The
*templatePath* from the configuration is prepended. The implementation::
class PathResolver implements ezcTemplateLocator
{
public function translatePath( $path )
{
return 'overridden/'. $path;
}
}
For every path that now passes through the template engine, "overridden/" is
prepended. Which means that the following code actually processes
"overridden/test1.ezt"::
$template = new ezcTemplate();
$out = $template->process( 'test1.ezt', $c );
Template syntax
===============
This section explains the syntax that can be used in the template itself.
Basic syntax
------------
The Template engine considers statements embedded in curly braces '{ .. }' as
code. Everything outside a block is parsed as text. The following example
shows a template that does a simple calculation::
5 times 3 equals: { 5*3 }
This example consists of two parts. The first part is the text up to the
opening curly brace. The second part is the code between the braces. Text
outside braces will always be printed. The code inside braces will be executed.
If the code does not modify a variable then the output of the executed code
will also be printed. The example will of course print::
5 times 3 equals: 15
Curly braces separate text from template code. Therefore these braces cannot be
used in the text directly. To use curly braces there are several
possibilities. The most common are to use escape characters or use the literal
tag.
Escape characters
`````````````````
A character can be escaped with a backslash (\\). In the text, there are three
groups of sequences that can be escaped. Those are:
- Open and close curly braces: '\\{' turns into '{' and '\\}' turns into '}'.
- The escape character, backslash: '\\\\' turns into '\\'.
- All newline character combinations: '\\[LF]', '\\[CR]' and '\\[CR][LF]' all
turn into '' (nothing).
The next example shows how to escape those characters::
Draw line: \{ (4, 10), (3, 5) \}
Game path: C:\\Program files\\games\\
Multiple \
lines \
becomes one
The output of the template is without the backslash and the characters appear
normally::
Draw line: { (4, 10), (3, 5) }
Game path: C:\Program files\games\
Multiple lines becomes one
All other uses of the escape character '\\' result in the escape character
being printed. This includes '\\' *just* before an EOF.
Literal tags
````````````
Literal tags are used when the text needs to be processed as it is (literally). The
curly braces don't need to be escaped and escape characters can be used.
Usually javascript is surrounded by literal tags in the templates. The curly
braces don't need to be escaped.
The next example demonstrates how the escaped characters from the previous example can be
omitted with literal tags: ::
{literal}
Draw line: { (4, 10), (3, 5) }
Game path: C:\Program files\games\
{/literal}
Comments
--------
Comments are text blocks which are stripped away from the output when the
template is compiled and executed. The Template engine supports three types
of comments:
- Code comments start and end with, respectively, '{\*' and '\*}' tags.
- Multi-line in-line comment, starts with '/\*' and ends with '\*/'.
- Single-line in-line comment that starts with '//' and reads until the end of
the line or until a closing curly brace '}'.
Code comments
`````````````
Code comments start and end with:'{\*' and '\*}' tags, respectively.
Everything inside those tags is considered as comment::
{* Variable $i alternates the value 0 and 1 *}
{$i = 1 - $i}
{* {if $i == 0}
{$i = 1}
{else}
{$i = 0}
{/if}
*}
Writing only *{$i = 1 - $i}* would have had the same output.
Multi-line in-line comment
``````````````````````````
Multi-line in-line comments are created using \/\* and \*\/. Use this comment to
comment a part of a line or comment multiple lines::
{var $a = 1, /*$b = 2, */ $c = 3 }
{$a = 2, /*
$b = 3,
$c = 4
*/}
If the result after removing the in-line comments is an empty block then the
block will be ignored. The next example has an empty block that won't be
visible in the output::
{ /* var $a = 1 */ }
And is of course the same as writing: *{ }*. The empty block will be ignored.
Single-line in-line comment
```````````````````````````
Single-line in-line comments are created using two slashes: \/\/ . Use this
to start a comment which continues to the end of the line or until the closing
curly brace '}'::
{var $a = 1, // $b = 2,
$c = 3}
{$a = 4 //, $b = 5, $c = 6}
This example declares the variables *$a* and *$c* and assigns in the second
statement the value 4 to variable *$a*.
Primitive types
---------------
The Template language supports several primitive types:
:Boolean: Expresses the truth value, which is either 'true' or 'false'.
:Integer: Is a number of the set: { .., -1, 0, 1, .. }
:Float: Is a real (floating point) number.
:String: A series of characters which are enclosed between single or double quotes.
:Array: Contains a set of primitive types.
:Object: A PHP object (imported via the user application).
Boolean
```````
The boolean type contains either the value: *true* or the value
*false*. The next example set the *$isValid* variable to *true*. ::
{var $isValid = true}
Some of the operators return boolean values. An example is the '==' operator.
The Expressions_ section discusses the operators in more detail. The next
example uses the '==' operator::
{var $isValid = ($number == 6) }
It checks first whether the variable *$number* is equal to the value 6. The
result is either *true* or *false*. The result is assigned
to the variable *$isValid*.
Integer
```````
Integers are specified in a decimal notation only. To use octals or hexadimals
the number needs to be converted with the appropriate function. Examples are::
{2}
{4}
{math_hex_to_dec("1F")}
See the methods: math_bin_to_dec, math_hex_to_dec, math_oct_to_dec, math_dec_to_bin,
ath_dec_to_hex, and math_dec_to_oct for more information about those
conversions.
Float
`````
Floating point numbers are values that contain a real value. Some examples::
{1.0}
{-100.234214}
{3.14}
It is also possible to express an exponent in the float. The exponent is marked
with the character 'e' or 'E' followed by one or more digits::
{1.0e3 // 1000 }
{2e4 // 20000 }
{1e-2 // 0.01 }
{0.1e-2 // 0.001 }
{-3.1e2 // -310 }
String
``````
The string consist of a series of characters enclosed between single or double
quotes::
{'a string' // Using single quotes }
{"hello world" // Using double quotes }
In the string we use the backslash (\\) as escape character. For the single
quoted string the escape characters are:
+-------------+-------------+
| **String** | **Output** |
+-------------+-------------+
| \' | ' |
+-------------+-------------+
| \\ \\ | \\ |
+-------------+-------------+
| \\ | \\ |
+-------------+-------------+
Examples of the single quoted strings are::
{'This string contains a \'quotes\' and backslashes (\\).'}
{'A single \ works also.'}
{'Characters like \n, \t, ", {, }, $, etc can be used without problems'}
The double quoted string allows more special characters than the single quoted
string. Most useful escape characters are probably the variables and newlines
that can be included in the string. The escape characters for the double quoted
strings are:
+-------------+-------------------+
| **String** | **Output** |
+-------------+-------------------+
| \" | " |
+-------------+-------------------+
| \\ \\ | \\ |
+-------------+-------------------+
| \\ | \\ |
+-------------+-------------------+
| \\n | <newline> |
+-------------+-------------------+
| \\t | <tab> |
+-------------+-------------------+
| $ | $ |
+-------------+-------------------+
| \\r | <carriage return> |
+-------------+-------------------+
The next example inserts newlines in the string::
{"Hello\nHello"}
The output of the template above is::
Hello
Hello
Some other examples of using single and double quoted strings::
{" a \"quoted\" string "}
{'Newlines are added with the \\n command.'}
{'\tThis string starts with a tab (\\t).'}
Array
`````
The array is just like in PHP an ordered map. It can be used as an array or as
a table that maps values to keys. There are two ways to create an array. The
first method has the following syntax::
array( [ key => ] value, [ key2 => ] value2, ... )
The parts between brackets are optional. So the *key* can be omitted. In that
case, it would simply create an array. First value has the index
0, the next value has index 1, and so on. The next example creates an array
which consists of 3 elements::
{var $names = array( "Bernard", "Manny", "Fran" )}
The array values, and in this case the names, can be accessed::
{$names[0] // Outputs "Bernard"}
{$names[2] // Outputs "Fran"}
{$names[3] // Is not allowed }
The array with *keys* maps the key to a value::
{var $personInfo = array( "first_name" => "Bernard", "last_name" => "Black" ) }
To access the information::
{$personInfo["first_name"] // Outputs "Bernard"}
{$personInfo["last_name"] // Outputs "Black"}
The second method to create an array is to use the *..* (dot-dot) operator ::
<number1>..<number2>
The operator creates an array that contains the numbers from *number1* to
*number2*. The next example creates an array that contains the numbers: 3, 4, 5, 6,
and 7.
{var $nrs = 3..7 }
{$nrs[0] // Outputs 3}
This method is especially useful to use in a foreach loop. The following
example loops 10 times, and prints the number from 1 until 10::
{foreach 1..10 as $i}
Number: {$i}
{/foreach}
The Foreach_ section describes this loop in more detail.
Object
``````
Objects are only available when they are sent from the user application. It is
not possible to create an object inside the Template language. The template
language restricts the accessibilities from the object only to its properties;
thus function calls are not permitted. The next example is a part of an user
application that sends an object to a template::
<?php
class MyClass
{
public function __get( $name )
{
return "Hello $name";
}
public function __set( $name, $value )
{
throw new Exception ("Setting $name is not allowed");
}
}
$t = new ezcTemplate();
$t->send->obj = new MyClass; // Create an object and assign it to "obj"
$t->process("my_template.ezt");
?>
The class in the example above has two 'magic' PHP functions. Fetching any
property is allowed, whereas setting a property value throws an Exception. The
template code that belongs to the user application::
{use $obj // Import the "obj" }
{$obj->Bernard // Calls the __get method on the object which returns "Hello Bernard"}
{$obj->Bernard = "Fran" // Calls the __set method that throws an exception}
More information about importing objects is described in the `External variable
declaration (use)`_ section.
Variables
---------
A variable can only be used after it has been declared. A
declaration defines a unique variable name that will be available from now
until the end of the template. The next example declares a local variable::
{* Declare the variable *}
{var $the_answer_to_life_the_universe_and_everything}
{* Assign it to 42 *}
{$the_answer_to_life_the_universe_and_everything = 42}
Switching the assignment with the declaration results in a compiler error.
Variables in the syntax languages are represented by a dollar sign followed by
the name of the variable. The variable name is case-sensitive. A valid variable
name starts with a letter or underscore, followed by any number of letters,
numbers, or underscores::
{* valid declarations *}
{var $abcdEFG}
{var $_hello_}
{var $hello12}
{* invalid declarations *}
{var $12monkeys}
{var $dumb&dumber *}
The Template language has three types of variable declaration: *var*, *use* and
*cycle*. *Var* declares a local variable as used in the previous
example, *use* imports an external variable, and *cycle* declares a cycle
variable.
Local variable declaration (var)
````````````````````````````````
The declaration of a local variable creates a variable that has only a value
inside the current template.
The syntax to declare a local variable is::
{var $<unique_name> [ = <value> ] [ , $<unique_name2> [ = <value> ] ] }
One or multiple variables can be declared with one *var* statement. A value can
be assigned to the variable name, directly. If no value is given to the
variable, then it will get the default value *null*.
The next example declares four variables at the same time::
{var $a = 2, $b = "Hello World", $c = true, $d}
All variables are initialized to a value. (The last variable is initialized to
*null*.)
External variable declaration (use)
```````````````````````````````````
The declaration of an external variable creates a variable that usually has a
value from outside the current template. Outside the current template is, for
example, the application that compiles and executes the template.
The syntax for this type of variable
is as follows::
{use $<unique_name> [ = <value> ] [ , $<unique_name2> [ = <value> ] ] }
Besides the *use* tag it is the same syntax as a local variable declartion.
The variable will be imported from the user application or another template and
is now available in the current template. If the declared *use* variable is not
sent to the current template an exception will be raised **unless** the
declared variable is initialized to a value. ::
{use $a, $b = 2}
Variable *$b* will be assigned to the value *2* if the variable is not sent to
the template. Variable *$a* will raise an Exception when the variable is not
sent.
The next example shows a part of the user application::
$t = new ezcTemplate();
// Send some information to the Template
$t->send->item1 = "Toaster";
$t->send->item2 = "Manny";
// Process the template and print it.
echo $t->process();
The variables $item1 and $item2 are now available in the template if they are
declared with *use*::
{use $item1 = "unknown", $item2 = "unknown"}
Use {$item1} on {$item2}
Cycles
``````
A cycle is a special type of variable that contains a set of values. The cycle
variable must be assigned to an array. Values are retrieved one array element at
the time. Specific control structures are available to cycle
through the set of values. The syntax for the cycle is::
{cycle $<unique_name> [ = <array_value> ] [ , $<unique_name2> [ = <array_value> ] ] }
An array must be assigned to the cycle variable. Otherwise the template
compiler *may* give a compiler error. (Sometimes it is not possible to know at
compile time whether the assignment is an array or not.)
The next example declares a cycle, assigns a value, and retrieves the first and
second value::
{cycle $rgb = array( "red", "green", "blue" ) }
{$rgb // Print "red"}
{$rgb // Print "red"}
{increment $rgb // Go to the next element}
{$rgb // Print "green"}
See the Increment_, Decrement_ and Reset_ section for more information.
Scopes
``````
Variable declarations must be done at the highest scope of the template.
Declaring a variable in a lower scope will result in a compiler error.
Scopes are usually opened and closed with a start and end template block.
Template blocks that open a new scope are: {if .. }{/if}, {while ..} {/while},
{foreach ..} {/foreach}, {switch .. } {/switch}, etc. The next example
demonstrates this scoping::
{var $a = 2 // Correct }
{if 2 == 3}
{* This opens a new scope *}
{var $b // ERROR! }
{/if}
Returning variables
```````````````````
Templates itself can also return variables to the user application or other
templates. The return statement defines the variables that need to be
returned::
{return ( <expression> as <variable> | <variable> ) [, ( <expression> as <variable> | <variable> ), ... ] }
The return statement returns one or multiple variables. The caller of the
template can retrieve the return values. Consider the following template::
{* Return 6! and "Hello world" *}
{var $fac6 = 6 * 5 * 4 * 3 * 2 }
{return $fac6, "Hello world" as $helloWorld}
The user application can retrieve these values via the receive property of the
template. See the next example for a demonstration::
$t = new ezcTemplate();
// Process the template.
$t->process();
// Retrieve the values from the return.
$fac6 = $t->receive->fac6;
$hw = $t->receive->helloWorld;
// Output Hello world:
echo $hw;
More information about returning variables is described in the Include_ section.
Capturing content into variables
````````````````````````````````
Sometimes it's useful to capture a specific part of a template in a variable
for reuse. This you can do with the capture statement::
{var $var}
{capture $var}
Add some content.
{/capture}
Output it a few more times:
{$var}
{$var}
Be aware that you still have to declare the variable with the {var} block.
Expressions
-----------
An expression is anything you write in a template block that doesn't start with
a special tag. But expressions are also used in template blocks with a tag and
gives merely a value. PHP quotes the expression as "Anything that gives a
value", that is also true for the template syntax.
Expressions can be a single *operand* that returns a value. It is also possible
to combine multiple operands with *operators*. The value returned depends
on the used operands and operators.
The operands are:
- Boolean_
- Integer_
- Float_
- String_
- Array_
- Variables_
- Functions_
The operands Boolean, Integer, Float, String, and Array are described in the
`Primitive types`_ section. Variables are described in the Variables_ section. Functions
will be described in the Functions_ section.
Variables and functions are considered as operands since they return one
value. The type of this return value is also a: Boolean, Integer, Float,
String, or Array.
An operand returns a single value. Some examples are::
{5}
{"Hello"}
{str_len("Hello")}
The values returned are, respectively: 5, Hello, and 5.
Operators are things that you feed with one or multiple values and will result
another value. Examples of operators are adding two numbers together with the
plus (+) operator. This operator expects two integers or floats (in any
combination) and returns either a integer of float. Another example is the
equal operator (==). It expects two types operands of the same type and
returns a Boolean.
Arithmetic operators
````````````````````
The arithmetic operators can be used on all expressions which return a
numerical value. The operator itself returns also a numeric value. The table
below describes the available arithmetic operators:
============== ===========
Negation -$o
Ignored +$o
Addition $ol + $or
Subtraction $ol - $or
Multiplication $ol * $or
Division $ol / $or
Modulus $ol % $or
============== ===========
Note: The negation in this table is the arithmetic negation. Don't confuse this
with the logic negation.
The unary plus operator can also be used, but it doesn't affect the value. It's
usually used to clarify that it's not a negative value.
Examples::
{ 2 + 5 // Returns value 7}
{ 2 - 5 }
{ 4 + 3 * 2 // Due to the operator precedence, first the *
// will be evaluated and thereafter the +. }
{var $a = 2}
{var $b = -$a}
Comparison operators
````````````````````
All comparison operators return a boolean value. The left-hand side and
right-hand side should be of the same type. The available comparison operators
are:
===================== ============
Equal $ol == $or
Identical $ol === $or
Not equal $ol != $or
Not identical $ol !== $or
Less than $ol < $or
Greater than $ol > $or
Less than or equal $ol <= $or
Greater than or equal $ol >= $or
===================== ============
Examples::
{ 4 == 5 }
{ 2 <= 5 }
{ true != false }
{ 4 == 5 == 6 }
The last example compares 4 with 5. This returns the boolean *false*. After the
first step the comparison is::
{ false == 6 }
The number 6 will be changed into a boolean. Every number except zero will get
the boolean value *true*. Basically the last step of the comparison is::
{ false == true }
And this return the boolean value *false*.
Logical operators
`````````````````
The logical operators are used on all expressions which return a boolean
value. The operator returns also a boolean:
=== ==========
Not ! $o
And $ol && $or
Or $ol || $or
=== ==========
Some usage examples::
{ true || false }
{ $a == 5 && $a != 7 }
{ $a || $b }
Assignments
-----------
Assignments set a value to a variable. If the variable does already contain a
value, it will be overwritten. As explained in the Variables_ section the
variable must be declared first. Notice that an assignment can be used in the
declaration itself.
The syntax language has two types of assignments. The former type assigns a new
value to the variable::
{ <variable> = <expression> }
The expression is evaluated and assigned to the variable. The next example
assigns the value 4 to the variable *$myVar*::
{var $myVar}
{ $myVar = 3 + 5 / 5 }
The latter assignment type updates the existing value with a modifier. There are
multiple operators available:
============== ===========
Addition $var += $or
Subtraction $var -= $or
Multiplication $var \*= $or
Division $var /= $or
Modulus $var %= $or
Pre increment ++$var
Pre decrement --$var
Post increment $var++
Post decrement $var--
============== ===========
The Addition, Subtraction, Multiplication, Division, and Modulus assignment
operators do an arithmetic calculation between the left (variable) and the
right operand (expression) and assigns the value in the left operand
(variable) again. Notice that the variable must already contain a value.
The increment operators increment the number of the current variable by one.
The decrement operators do the opposite and decrement the value of the current
variable by one. There is no difference between the pre and post operators,
and both are present for convenience purposes.
Examples::
{var $myVar = 5 }
{$myVar += 5 // $myVar has the value 10 }
{$myVar++ // is the same as $myVar += 1 }
{$myVar *= 10 // Multiply with 10}
{--$myVar // Same as: $myVar -= 1 }
Functions
---------
The Template language has lots of built-in functions. These
functions are categorized in a few groups: String, Array, Regular expression,
Type information, and Arithmetic (math) functions. The functions are explained
in the appendix.
A function call has the following syntax::
<function_name> ( [ Parameter1 [, Parameter2, ... ] ] )
An example::
{var $res = str_compare( "Hello", "world" ) }
{$res // prints the result}
All template functions follow these rules:
- Function output is always returned. No variables are changed via references.
- If the function contains a "haystack" and a "needle" then the first
parameter will be the "haystack". The "needle parameter is a parameter that
operates on another parameter, the "haystack".
Contexts
--------
The purpose of the context is to simplify the development of the templates and
make the templates more secure. The context defines the purpose of the template
output. For example the XHTML context should be set for the templates that
generate (X)HTML output.
When a template is processed and executed, it runs in a specific context. The
context is set to XHTML by default. The
next example shows a part of a user application that specifies the XHTML
context::
$config = ezcTemplateConfiguration::getInstance();
$config->context = new ezcTemplateXhtmlContext();
$t = new ezcTemplate();
$t->process( "hello_world.ezt" );
Configuring the Template engine is discussed in the tutorial in more detail.
The available context are described in the next subsections.
XHTML Context
`````````````
The XHTML Context escapes the HTML characters from the output from the
expression blocks. Output outside the blocks are not escaped. See the next
example::
{var $lt = "3 < 5"}
<b>{$lt}</b>
This template contains the HTML character '<' in an expression block. This
character will be escaped. The output of the template is::
<b>3 &lt 5</b>
The escape characters of the XHTML context are:
========= ===========
Character Translation
========= ===========
& &amp;
" &quot;
< &lt;
> &gt;
========= ===========
If the HTML characters in the expression block should not be escaped then the
"raw" block should be used. The next example demonstrates a "raw" block::
{var $myBoldText = "<b>Hello world</b>"}
{raw $myBoldText}
This template outputs::
<b>Hello world</b>
See the Raw_ section for more information.
Additional required escaping
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Even inside XHTML there are still different output contexts, which might
require different escaping. For example when using variables inside JavaScript
the default escaping is *not sufficient*, but additional characters are
required to be escaped, like shown in the following example. If the following
template is called with the ``$input = '\' ); alert( \' XSS Alert ';``, it will
show an JavaScript alert box, even with XHTML context escaping::
{use $input}
<body onLoad="JavaScript: doSomething( '{$input}' );">
...
To work around this issue you can either write a custom template function which
wraps around the PHP function ``addslashes()`` or do the following in your
template::
{use $input}
<body onLoad="JavaScript: doSomething( '{
str_find_replace( "'", "\\'",
str_find_replace( '"', '\\"',
str_find_replace( '\\', '\\\\', $input )
)
)}' );">
...
This should be safe afterwards. Remember that there might be other contexts in
your XHTML files, like other client side scripting languages, which again
require different additional output escaping.
No Context
``````````
The No Context does not escape any characters. Usefull for text output and
testing purposes.
Control structures
------------------
Control structures are elements which help you control the flow of the code,
either by doing conditional statements or by repeating certain actions.
Increment
`````````
The increment construct sets the cycle variable to the next value in the cycle
array::
{cycle $rgb = array("red", "green", "blue" )}
{$rgb // Print "red" }
{increment $rgb}
{$rgb // Print "green" }
If the end of the cycle array is reached, it will jump again to the first
value of the array.
Decrement
`````````
The decrement construct sets the cycle variable to the previous value in the cycle
array::
{cycle $rgb = array("red", "green", "blue" )}
{$rgb // Print "red" }
{decrement $rgb}
{$rgb // Print "blue" }
If the begin of the cycle array is reached, it will jump again to the last
value of the array.
Reset
`````
The reset construct sets the cycle variable to the first value in the cycle
array::
{cycle $rgb = array("red", "green", "blue" )}
{$rgb // Print "red" }
{increment $rgb}
{$rgb // Print "blue" }
{reset $rgb}
{$rgb // Print "red" }
If, else, elseif
````````````````
The *if* construct allows conditional execution of code fragments. The
structure is::
{if <expression> }
<code>
{/if}
If the <expression> evaluates to true, the <code> fragment will be executed.
For instance the following code will show "$a is less than $b" if $a is
less than $b::
{if $a < $b }
$a is less than $b.
{/if}
The *else* construct can be used in an *if* statement. It contains a code
fragment that will be executed if the *expression* evaluates to false::
{if expression }
code1
{else}
code2
{/if}
The *code2* is executed when the expression is false. Otherwise *code1* is
executed.
The *elseif* construct can be used in an *if* statement. It adds an extra
expression and code block that will be evaluated and executed when the
(previous) expression from the *if* statement does not evaluate to true::
{if $weekday == 0}
Monday
{elseif $weekday == 1}
Tuesday
{elseif $weekday == 2}
Wednesday
{else}
Thursday, Friday, Saturday, or Sunday.
{/if}
Writing an *elseif* statement is actually the same as writing them with
separate *if* and *else* statements. The previous example could be written as::
{if $weekday == 0}
Monday
{else}
{if $weekday == 1}
Tuesday
{else}
{if $weekday == 2}
Wednesday
{else}
Thursday, Friday, Saturday, or Sunday.
{/if}
{/if}
{/if}
As you can see, this makes the code harder to read.
Switch, Case, Default
`````````````````````
The switch construct is quite similar to multiple *if* and *elseif* statements.
The syntax is as follows::
{switch <expression>}
<case1> [ case2 [ case3, .. ] ]
[ default ]
{/switch}
A *case* block has the following syntax::
{case <literal> [, <literal>, ..]}
<code>
{/case}
And the *default* is as follows::
{default}
<code>
{/default}
The switch statement expects an expression. This expression will then be
compared with the literal values from the *case* statements. The comparison
starts and goes to the next literal as the values from the <expression> and
<literal> are not equal. The <code> from the first match will be executed and
the rest of the cases are skipped.
If none of the cases match, then the default block will be executed. See the
example switch statement below::
{switch $weekDay}
{case 0} Monday {/case}
{case 1} Tuesday {/case}
{case 2} Wednesday {/case}
{case 3, 4, 5, 6}
Thursday, Friday, Saturday, or Sunday
{/case}
{default}
The $weekDay should be a number between 0 and 6.
{/default}
{/switch}
This switch converts the weekday number to a name.
Foreach
```````
A very important construct in the Template language is the *foreach*. The
foreach is a loop structure that iterates over an array. Since this loop
is used so often in the templates, it has several convenience tags build in.
First we'll show the basic syntax::
{foreach <array> as <value> }
<code>
{/foreach}
{foreach <array> as <key> => <value> }
<code>
{/foreach}
Two foreach structures are shown. The first structure takes each element from
the *array*> and assigns it to the variable *<value*>. The second structure does
the same, except that the key of the array is assigned to the variable *key*.
An example for each structure::
{var $rgb = array( "red", "green", "blue" ) }
{foreach $rgb as $color}
The color is: {$color}
{/foreach}
{foreach $rgb as $key => $color}
Array key {$key} contains the color: {$color}
{/foreach}
The output for this example will be::
The color is: red
The color is: green
The color is: blue
Array key 0 contains the color: red
Array key 1 contains the color: green
Array key 2 contains the color: blue
With this syntax we can easily loop any number, and is more convenient than
using the while_ structure. This is demonstrated next::
{foreach 1..10 as $i}
Iteration {$i}
{/foreach}
This works because the *1..10* statement creates an array with the
values from 1 until 10.
Often a foreach is used to create some kind of table or list. Several extensions
are made available to ease the development:
:Cycles: Increments or decrements a cycle variable.
:Offset: Start the loop at a given offset.
:Limit: Limits the number of iterations.
The foreach loop can have an increment and decrement tag. These tags increment and/or decrement an
cycle value, and work exactly the same as the Increment_ and Decrement_ control
structures. The syntax is almost the same in the foreach::
[increment <variable1> [, variable2, ... ] ]
[decrement <variable1> [, variable2, ... ] ]
These tags are the same as the Increment_ and Decrement_ control structures.
This tag is added at the end of the foreach construct. The next example
increments the cycle value in every iteration of the loop::
{cycle $blackAndWhite = array( '#00000', '#FFFFFF' )}
{foreach 1..5 as $value increment $blackAndWhite}
<font color="{$blackAndWhite}">Number: {$value}</font>
{/foreach}
This loop outputs the following code::
<font color="#000000">Number: 1</font>
<font color="#FFFFFF">Number: 2</font>
<font color="#000000">Number: 3</font>
<font color="#FFFFFF">Number: 4</font>
<font color="#000000">Number: 5</font>
The offset and limit code constructs are specified after the cycle increment or
decrement tag. The offset and limit constructs are extremely useful for
splitting a long table or list over multiple page views. The next example
demonstrates this::
{use $hugeArray = array(), $offset = 0}
{foreach $hugeArray as $tableEntry offset $offset limit 100}
{* Show the information from the $tableEntry *}
{foreach}
See the `External variable declaration (use)`_ section for more
information. The loop will start at the $offset and won't show the previous
elements. The maximum iterations of the loop is 100. Another example shows
the numbers from 50 until 100::
{var $hugeArray = 1..1000}
{foreach $hugeArray as $value offset 50 limit 50}
{$value}
{/foreach}
While
`````
The while loop loops over a code fragment as long as the expression in the
while evaluates to true. The syntax of the while loop is as follows::
{while <expression>}
<code>
{/while}
Usually the expression evaluates whether a counter reaches a certain number. In
the *code* the counter is increased or decreased::
{var $i = 0}
{while $i < 10}
The number is: {$i}.
{$i++}
{/while}
This example prints the numbers from 0 until 9. If you write a while loop, make
sure that the loop eventually ends. The next example demonstrates another while
loop. This loop increments a value from a Cycle. Compare how the same example
can be done with a Foreach_::
{cycle $blackAndWhite = array( '#00000', '#FFFFFF' )}
{var $i = 1}
{while $i <= 5 }
<font color="{$blackAndWhite}">Number: {$i}</font>
{$i++}
{increment $blackAndWhite}
{/while}
Delimiter
`````````
The delimiter can be used (only) inside a loop to do every given iteration a specific
action. The syntax is as follows::
{delimiter [modulo <expression> [is <expression>]]}
<code>
{/delimiter}
The "module...<expression>" part can be omitted and by default the delimiter
will be inserted between every iteration of the loop. If a modulo is used, the
"is <expression>" part of the delimiter can be omitted and will be interpreted
as "is 0".
The delimiter will always be executed between two iterations of the loop. In
the next example between every name a comma is inserted::
{var $names = array( 'Bernard', 'Fran', 'Manny' )}
{foreach $names as $name}
{$name}
{delimiter}, {/delimiter}
{/foreach}
The next example demonstrates creates a matrix with 4 rows and 4 columns. The
delimiter closes the current row and opens a new one every forth column as the
delimiter is only added when "internal counter modulo 4" equals 0. The
"internal counter" simply counts the number of executed iterations::
{var $columns = 4}
<table>
<tr>
{foreach 1..16 as $nr}
<td>{$nr}</td>
{delimiter modulo $columns}
</tr><tr>
{/delimiter}
{/foreach}
</tr>
Continue
````````
The continue statement is used within the looping structures to skip the rest of the current loop
iteration and continue execution at the condition evaluation and then the beginning of the next
iteration. If a delimiter is available in the looping structure then this
delimiter will be added. Use the Skip_ statement to skip also the delimiter.
The next example show the numbers 1 to 5 separated with a comma. After the
numbers 1, 2, and 3 is the token # appended. As the example shows, the
numbers higher than 3 will not execute the rest of the foreach anymore. The
delimiter is added anyway::
{foreach 1..5 as $i}
{delimiter}
,
{/delimiter}
{$i}
{if $i > 3} {continue} {/if}
#
{/foreach}
The output of the template is::
1 #,
2 #,
3 #,
4 ,
5
Skip
````
The skip is the same as a continue, except that the delimiter in the loop will
be skipped also. The next example show the numbers 1 to 5. The number 1, 2,
and 3 have the token # appended and are separated with a comma.
As the example shows, the numbers higher than 3 will not execute the rest of the
foreach anymore and the delimiter is not appended::
{foreach 1..5 as $i}
{delimiter}
,
{/delimiter}
{$i}
{if $i > 3} {skip} {/if}
#
{/foreach}
And outputs::
1 #,
2 #,
3 #,
4
5
Break
`````
The break ends execution of the current foreach or while structure. The example
below prints the numbers 1 and 2. Due to the break, the rest of the loop is
skipped::
{foreach 1..10 as $i}
{$i}
{if $i == 2} {break} {/if}
{/foreach}
Include
```````
The include calls other templates which will be executed within the current
template. Variables can be passed on and retrieved from the included template.
The generic template could then be used at multiple places and is configurable
via the given variables.
The syntax of the include is as follows::
{include <template_name | location_object >
[ send <send_variables> ]
[ receive <receive_variables> ]
The file that needs to be included is either specified with a string
(template\_name) or with a location object. The location object is explained in
the section `Template locations`_.
The <send\_variables> has this syntax::
<expression> as <variable> | <variable>
And the <receive\_variables> has almost the same syntax as the
<send\_variables>::
<variable> as <variable> | <variable>
A next example will clarify the syntax a bit more::
{include "calc_a_plus_b.ezt"
send 2 as $a,
5 as $b
receive $c as $sum }
The included template calculates the sum of the given variables $a and $b, and
returns the answer in variable $c. The expressions "2" and "5" are assigned to
the variables $a and $b of the included template. The value of the (returned)
variable $c is directly assigned to the variable $sum. The variable $sum does
not need to be declared first.
The template "calc\_a\_plus\_b.ezt" is::
{use $a = false, $b = false}
{if $a === false || $b === false}
Variable $a or $b has an incorrect value.
{/if}
{return $a + $b as $c}
The next templates includes the "calc\_a\_plus\_b.ezt" but does not use the
expression part of the include::
{var $a = 2, $b = 5}
{include "calc_a_plus_b.ezt"
send $a, $b
receive $c }
{var $sum = $c}
Raw
```
The raw construct outputs raw information and is not affected by the Contexts_.
For example, the XHTML context is set and an expression contains HTML
characters (which should be sent to the output) then use the "raw" block::
{var $myBoldText = "<b>Hello world</b>"}
{raw $myBoldText}
This template outputs::
<b>Hello world</b>
Translation Constructs
----------------------
Translation constructs allow you to embed translatable strings in your
templates. Every construct requires at least a string and a context, the
context can however be set by default to be valid for subsequent translatable
string entries.
tr_context
``````````
Is used to set a default translation context. All tr_ entries below this
construct will then use this translation context if none is specified
explicitly. Information on contexts can be found in the `Qt Linguist format
specification`__.
This construct is used like::
{tr_context "admin/forget_password"}
Each template can have multiple tr_context statements. The default context that
is set with this construct is valid until the next one is encountered.
__ Translation_linguist-format.html#contexts
tr
``
The tr construct defines a translatable string, the simplest form of this
construct is::
{tr "String to translate"}
This will only work however, if there is a default context set with
`tr_context`_. In case there is none set, you have to specify it yourself with
the "context" parameter. The following two templates are equivalent::
{tr_context "admin/forget_password"}
{tr "String to translate"}
and::
{tr "String to translate" context "admin/forget_password"}
When a translator translates a text, it is sometimes useful to have slightly
more information than just the context and the string itself. The tr construct
therefore allows to add a comment to the translatable string with the "comment"
parameter. This has no effect on how the tr construct is interpreted however.
An example::
{tr "Login" context "user/login" comment "Text before login input box"}
In many occasions, the translatable strings allow positional or named
parameters to allow for changing the order of arguments. For example
the English string "Search for 'appelmoes' returned 3 matches" can be
translated in Dutch as: "Er zijn 3 items gevonden bij het zoeken naar
'appelmoes'". A simple concatenation mechanism of multiple translatable strings
would no longer work. The tr construct supports parameterized strings in two
different ways: with numerical replacement identifiers (such as %1 and %2) and
with associative identifiers (such as %search_string and %matches). The
following example illustrates how this is done in the simplest possible way::
{tr "Search for '%1' returned '%2' matches" vars 'appelmoes', 3}
If no key is specified for variables, like in the above example, they are
automatically given numbers, starting by 1. It is also possible to add specific
positions to variables, like::
{tr "Search for '%1' returned '%2' matches" vars 2 => 'appelmoes', 1 => 3}
This is perhaps not so useful for positional parameters like here, but it is
necessary for named parameters as illustrated in the example here::
{tr "Search for '%what' returned '%matchcount' matches" vars 'what' => 'appelmoes', 'matchcount' => 3}
It is of course also possible to mix those two cases::
{tr "Search for '%1' returned '%matchcount' matches" vars 'matchcount' => 3, 'appelmoes'}
or::
{tr "Search for '%1' returned '%matchcount' matches" vars 'appelmoes', 'matchcount' => 3}
Variables without any key, are always given positional identifiers in
sequential order. When doing so, variables with a named key are ignored, while
variables with a numerical key reset the next number in the auto-numbering
sequence to the number of the key. The following example shows that::
{tr "%1 %2 %3 %4 context "test" vars 3 => 'three', 'four', 1 => 'one', 'two'}
For information on how to setup translations for your templates, please refer
to the section `Translations`_.
Extensions
==========
This section explains how you can extend your Template language. Extending the
Template language gives you the opportunity to add more application specific
functionality. Some examples of Template language extensions are:
- Adding a method to fetch information from a data structure. This data
structure could be a single or a group of objects, a database, an array, etc.
- Creating an HTML table using your own customized template blocks.
- Doing an application specific calculation.
Currently, there are two ways of adding custom functionality to the template.
The methods are named: **custom blocks** and **custom functions**. Together we call
them **custom extensions**.
First, we'll explain how to create a custom block. With the knowledge of these
custom blocks, we explain - much shorter - the custom functions.
Custom blocks
-------------
Custom blocks are extra functionality that could be used inside the template
language. These blocks are implemented in PHP. The next section explains how an
custom block can be used. The sections thereafter discuss how to add a new
custom block.
Custom blocks in the Template language
``````````````````````````````````````
Custom blocks add specific behavior to the Template language. The most basic
structure of a custom block - without any parameters - is::
'{' <name> '}'
The block starts and ends with curly braces. The *<name>* identifies the custom
block. Optionally, a custom block has also a closing block::
'{' '/' <name> '}'
The name must be the same as the start block, but starts with a forward slash ('/'). The
text between the open and close tag is considered as input data. Whether a
custom block should have a close tag is up to the developer.
The next example demonstrates a custom block with an open and close tag that
capitalizes the text in between::
{capitalize}
Guybrush Threepwood.
{/capitalize}
Another example that uses a custom block without a close tag::
{capitalize "Guybrush Threepwood."}
Whether or not a custom block has a close tag, it handles the parameters the
same. The structure of a custom block is as follows::
'{' block_name [start_expression] [ <param_name> ['='] <value> ... ] '}'
Basically, it describes that a custom block can have:
- One optional expression which is not bound to a parameter name.
- Zero or more named parameters with a value. Between the parameter name and
value is an optional equal sign.
Some example blocks that use parameters::
{link "eZ systems" to "http://ez.no"}
{table border=true bgcolor="red"}
{debug}
{calculate 5 plus 5}
{join array( 'Hello', "world" ) with "_"}
{header style="bold"}
Hello world
{/header}
Note that the values used in the examples are just ordinary expressions.
Therefore it is possible to write::
{link "eZ" . " systems" to 'http'. "://" . "ez.no"}
{calculate 2 + 3 plus 1 * 2 + 3}
Again, the optional and required parameters are up to the developer.
Adding a custom block
`````````````````````
New custom blocks are added to the Template engine with the
ezcTemplateConfiguration::addExtension() method. This method expects the class
name which implements the new custom block. The next example adds a custom
block to the Template engine which is implemented in MyClass::
$config = ezcTemplateConfiguration::getInstance();
$config->addExtension( "MyClass" );
The custom block class should implement the ezcTemplateCustomBlock interface.
This interface has only one method getCustomBlockDefinition(). The class
implementing this method should return an ezcTemplateCustomBlockDefinition
object from a given block name, if possible.
Custom block definition
```````````````````````
The custom block definition describes how the Template engine should interpret
the custom block. The information is provided by returning an instance of the
ezcTemplateCustomBlockDefinition::
class ezcTemplateCustomBlockDefinition extends ezcTemplateCustomExtension
{
public $class;
public $method;
public $hasCloseTag;
public $startExpressionName;
public $optionalParameters = array();
public $requiredParameters = array();
public $isStatic;
}
The member variables that should be set in this class are:
:class: String value that specifies the (static) class that implements the function to be executed.
:method: String value that specifies the (static) method that should be run.
:hasCloseTag: Boolean value that specifies whether the class has an open and close tag or only a open tag.
:startExpressionName: The first parameter of a custom block without a name is
called the start expression. If the custom block should have a start
expression then this variable specifies a name for it. The name should reappear
in either the optionalParameters or the requiredParameters.
:optionalParameters: Optional named parameters for this custom block.
:requiredParameters: Required named parameters for this custom block.
:excessParameters: When this is set to true, additional parameters are also
available in the implementation of the custom block as elements of the
$parameters array.
:isStatic: The custom block is only called during compilation. At run time the
static output from the custom block at compile time is displayed.
The following custom block creates a hyper-link from a name and an URL::
{link "name" to "url" [title "title"]}
This custom block has a:
- Required unnamed parameter that contains the name.
- Required named parameter "to" that contains the URL.
- Optional named parameter "title" that contains the title.
Except for the class and the method name the CustomBlockDefinition should look
like::
$def = new ezcTemplateCustomBlockDefinition;
$def->class = "MyLink";
$def->method = "linkMethod";
$def->hasCloseTag = false;
$def->startExpressionName = "from";
$def->optionalParameters = array( "title" );
$def->requiredParameters = array( "from", "to" );
The CB definition should be made available to the Template engine via a class
implementing the ezcTemplateCustomBlock interface::
interface ezcTemplateCustomBlock
{
public static function getCustomBlockDefinition( $name );
}
The class implementing the getCustomBlockDefinition() method should
return an ezcTemplateCustomBlockDefinition for the given block name $name.
The next code demonstrates a class that implements the interface::
class MyLink implements ezcTemplateCustomBlock
{
public static function getCustomBlockDefinition( $name )
{
switch ($name )
{
case "link":
$def = new ezcTemplateCustomBlockDefinition;
$def->class = "MyLink";
//
// Create definition
//
return $def;
}
return false;
}
}
Implementing the custom block
`````````````````````````````
In the CB definition is specified which class and what method should produce the
custom block output. A typical implementation to create a hyper-link would be::
public static function link( $parameters )
{
$title = "";
if( isset( $parameters["title"] ) )
{
$title = "title='". $parameters["title"] ."'";
}
return "<a href='".$parameters["to"]."' $title>".$parameters["from"]."</a>"
}
The method implementing the custom block returns a string that will be
inserted in the template output. The custom block parameters are provided in
the first parameter as an array. The method itself must check whether the
optional parameters are sent or not. Required parameters are always available
as the template compiler throws a compiler exception if these parameters are
absent.
The custom blocks that do have a close tag need a method that accepts two
parameters. For example the following custom block has a close tag::
{capitalize}
Guybrush Threepwood
{/capitalize}
The method that implements the custom block::
public static function capitalize( $parameters, $text )
{
return strtoupper( $text );
}
The $parameters parameter is never used. The text between the open and close
tag of the custom block is always passed in the second parameter.
Example
```````
The 'calc' custom block calculates the sum of the given values. The sum is
calculated from the initial value plus or minus the values added to the "plus"
and or "minus" parameter. Some usage examples are::
{calc 5 plus 3}
{calc 5 plus array(2,5,2) minus 2}
{calc 2 minus 5}
The custom block could be implemented like::
class CalcCustomBlock implements ezcTemplateCustomBlock
{
public static function getCustomBlockDefinition( $name )
{
switch ($name )
{
case "calc":
$def = new ezcTemplateCustomBlockDefinition;
$def->class = __CLASS__;
$def->method = "calculate";
$def->hasCloseTag = false;
$def->startExpressionName = "init";
$def->requiredParameters = array("init");
$def->optionalParameters = array("plus", "minus");
return $def;
}
return false;
}
public static function calculate( $params )
{
$result = $params["init"];
if ( isset( $params["plus"] ) )
{
if( is_array( $params["plus"] ) )
{
foreach( $params["plus"] as $value )
{
$result += $value;
}
}
else
{
$result += $params["plus"];
}
}
if ( isset( $params["minus"] ) )
{
if( is_array( $params["minus"] ) )
{
foreach( $params["minus"] as $value )
{
$result -= $value;
}
}
else
{
$result -= $params["minus"];
}
}
return $result;
}
}
This new custom block needs to be assigned to the Template configuration::
$config = ezcTemplateConfiguration::getInstance();
$config->addExtension( "CalcCustomBlock" );
And of course the template can be run like::
$t = new ezcTemplate();
$t->process( "my_template.ezt" );
Custom functions
----------------
This section explains how the custom function can be implemented. Custom
blocks and custom functions are very similar.
Custom functions in the Template language
`````````````````````````````````````````
Custom functions are extra functions that can be used in the Template
expressions. Some examples are::
Greatest common divisor: {gcd(8, 12)}
{sin( $angle ) * 2}
{matrix_multiply( $m1, $m2)}
{fetch_from_database("items", 2)}
Basically, the second line in the example runs a custom function and multiplies
the result with two. This demonstrates that the difference between a custom
block and a custom function. Custom functions can be used within an
expression. Custom blocks are directly generating an output result.
Adding a new custom function
````````````````````````````
As with the custom block, custom functions are added to the Template engine
by the ezcTemplateConfiguration::addExtension() method. The class name given to
this method should implement the ezcTemplateCustomFunction interface. Notice
that this is the same as adding a custom block. ::
$config = ezcTemplateConfiguration::getInstance();
$config->addExtension( "MyClass" );
The example above adds *MyClass* to the ezcTemplateConfiguration. MyClass
implements the getCustomFunctionDefinition() from the ezcTemplateCustomFunction
interface. This function returns an ezcTemplateCustomFunctionDefinition object::
class ezcTemplateCustomFunctionDefinition extends ezcTemplateCustomExtension
{
public $class;
public $method;
public $sendTemplateObject = false;
// Deprecated:
// public $parameters = array();
}
The $class and $method parameter are the same as for custom blocks. The
$parameters variable is obsolete with version 1.2. It used to specify
the required and optional parameters for the custom function.
- Required parameters are named strings.
- Optional parameters are named strings enclosed with square brackets. These
parameters should be specified after the required parameters.
In version 1.2 and up it uses reflection to find the required and optional
parameters. Due some bugs in the reflection classes it works properly in PHP
version 5.2 and up.
It is also possible to accept a variable number of (extra) arguments. For this
you need to set the *variableArgumentList* property of the
ezcTemplateCustomFunctionDefinition.
The variable *$sendTemplateObject* adds the possibility to send the current
template object to the custom function.
As with functions in PHP the order of the unnamed parameters matter. The next example
demonstrates a complete custom function implementation::
class MyClass implements ezcTemplateCustomFunction
{
public static function getCustomFunctionDefinition( $name )
{
switch ($name )
{
case "func":
$def = new ezcTemplateCustomFunctionDefinition();
$def->class = "MyClass";
$def->method = "customFunction";
// Deprecated:
// $def->parameters = array( "firstParam", "secondParam", "[thirdParam]");
return $def;
}
return false;
}
public static function customFunction( $firstParam, $secondParam, $thirdParam = "isOptional" )
{
// Implementation.
return "string";
}
}
The custom parameters specified in the definition block should also be present
in the *customFunction* implementation. Three parameters are specified, so the
customFunction implementation should have three parameters. The last parameter
in the parameter definition is specified as optional, so should the last
parameter in customFunction be optional.
The interface of the ezcTemplateCustomFunction requires to implement the
getCustomFunctionDefinition() method::
interface ezcTemplateCustomFunction
{
public static function getCustomFunctionDefinition( $name );
}
Named parameters
````````````````
New in version 1.2 of the Template engine are named parameters. The advantage
of named parameters is that the order of the parameters are no longer important
and that parameters can be omitted that otherwise was not possible. The next
example shows a common custom function retrieving items from the database::
// fetch_item ( select = "*", start = 0, limit = false, orderBy = "id", reverseOrder = false )
{ fetch_item("*", 0, false, "name") } // Fetch everything but order by 'name'
This example fetches everything from the items table and sorts it on "name".
Without named parameters the problem is that only the second last parameter
needs to be changed and therefore all the previous parameters need to be given.
Named parameters are written in the form *"keyword = value"*. The keyword is
the name of the parameter. The equal sign is used as separator because it is not
possible to do any assignments within expressions.
Using named parameters the code from the previous example is reduced to::
// fetch_item ( select = "*", start = 0, limit = false, orderBy = "id", reverseOrder = false )
{ fetch_item( orderBy = "name") } // Fetch everything but order by 'name'
Besides that the code is shorter, it is also more descriptive.
Sending template objects
````````````````````````
In a few cases the template object is needed in the custom function
implementation. With access to the template object, the configuration and other
template settings could be retrieved and used.
If the variable *sendTemplateObject* is set to *true*, the first parameter in
the function contains the template object. The following
The next example adds the custom function *templatePath*::
class MyClass implements ezcTemplateCustomFunction
{
public static function getCustomFunctionDefinition( $name )
{
switch ($name )
{
case "templatePath":
$def = new ezcTemplateCustomFunctionDefinition;
$def->class = "MyClass";
$def->method = "templatePath";
$def->sendTemplateObject = true;
return $def;
}
return false;
}
public static function templatePath( $templateObj )
{
return $templateObj->configuration->templatePath;
}
}
Caching
=======
The purpose of the template cache is to speed up the processing time of a
template by storing its output to a file. This file is known as the
*cache file*. When the same template is requested later on, the cached contents
are displayed without processing the template first. The time consuming
instructions, like computations and requests to a database, can then be reduced
or omitted.
Caching can truly reduce the processing time of the templates, but caching needs
to be handled with care. The developer needs to:
- Add caching at the correct, time consuming, spots in the template.
- Make sure that the cache is expired and renewed in time.
- Clean up the unused cache files.
Each cached template has a bit of overhead in comparison with an uncached
template. The extra work is to check if the cache file exists and if it's
valid, optionally regenerate the cache, and execute the cache. If the cache
file needs to be regenerated too often, the template is probably processed
quicker without caching at all. The same applies for small, non-time consuming
templates.
Another issue the developer should keep in mind is that the cache needs to be
expired and renewed in time. Usually a template cache is valid for a limited
time. This can be a timeout (e.g. after 2 hours) or a trigger (an object
changes) that expires the cache. The triggers and timeouts need to be
available in the template.
The last issue is that unused cache files need to be discarded. Without a
proper cache clean up mechanism the system will eventually flood with unused
cache files. It is the developer's responsibility to remove those files.
Configuration
-------------
The template cache is enabled by default and activated when cache blocks are
present in the templates. The template configuration,
*ezcTemplateConfiguration* controls the locations where the cache files
are stored and the mechanism that cleans up the unused cache files.
The configuration has the following properties relevant to caching:
- *cachedTemplatesPath*: The location where the cached templates should be
stored. The *compiledPath* and the *cachedTemplatePath* properties together
form the complete cache path.
- *cacheManager*: This property is assigned to a cache manager object that
keeps track of the generated cache files and is responsible for its clean up.
- *disableCache*: Disables all caching, useful during development.
The next code demonstrates an example configuration: ::
$config = ezcTemplateConfiguration::getInstance();
$config->templatePath = '/mydir/templates/';
$config->compilePath = '/mydir/templates_bin/';
$config->cachedTemplatesPath = "CACHE";
$config->disableCache = false;
$config->cacheManager = new DatabaseCacheManager();
$template = new ezcTemplate();
$template->process( "hello_world.ezt" );
The engine will process the following template: ::
"/mydir/templates/hello_world.ezt"
The compiled template and the cached template will then be stored in: ::
"/mydir/templates_bin/compiled_templates/"
"/mydir/templates_bin/CACHE/"
The 'compiled_templates' is a default setting. The cache manager that will be
used is the *DatabaseCacheManager*. Details about the cache manager are
explained later.
Cache tags
----------
There are two methods to enable caching: *cache_block* and *cache_template*.
The *cache_block* enables caching between the open and close tag::
{"dynamic"}
{cache_block}
{foreach 1..10 as $i}
{$i}
{/foreach}
{/cache_block}
{"dynamic"}
This example caches the *foreach*-loop. The strings that contains *"dynamic"*
are outside the cache block; thus not cached.
The *cache_template* starts caching after this tag until the end of the
template. In the next example everything is cached except the string that
contains *"dynamic"*::
{"dynamic"}
{cache_template}
{foreach 1..10 as $i}
{$i}
{/foreach}
{"cached"}
Sometimes a large block needs to be cached with the exception of a small block
of template code. Take for instance a relatively static page with the
current time in one corner. The *dynamic* block is a block that can only be
used inside a *cache_block* or after the *cache_template*. The code inside the
*dynamic* block is not cached and will always be executed. The next example
uses the *dynamic* block to display the current time: ::
{cache_template}
The current time is:
{dynamic}
{date_format_timestamp("H:i:s")}
{/dynamic}
Note that the example above is only for demonstration purposes. The template is
so simple that it is probably quicker to use no caching at all.
Cache renewal
-------------
Usually cached templates are valid for a limited time. A certain page is known
to be valid for some hours or until a value changes in the database. The
Template engine supports three mechanisms to expire the cache:
*cache-keys*, *time-to-live* (TTL), and a *cache manager*.
The cache-keys and TTL are both parameters of the cache syntax. These
parameters check whether the cache is still valid and if needed create a
new version.
The cache manager needs to be programmed manually. It gives a
better control over the cache and can also remove old cache files.
Cache keys
``````````
Both *cache_block* and *cache_template* have a *keys* parameter. This
parameter can be used to define the uniqueness of a cache block. For every
combination of different values of the parameter, a separate file is stored on
the disk, containing the output of the cache block. The *keys* parameter can be
an expression but is usually limited to a variable or function call. The next
example creates new cache files when the user ID changes: ::
{use $user_id}
{cache_block keys $user_id}
Show user information.
{/cache_block}
{cache_block keys getUserId()}
Show user information, again.
{/cache_block}
The first cache_block relies on the imported *$user_id*. The second
*cache_block* uses a custom function that returns the ID.
The given cache key can be of any type. If the type is a scalar (boolean,
integer, float, or string) then the value is compared as a string.
For arrays, it's compared with the checksum of the dumped structure.
For objects, the comparison consists of two steps. First will be checked if
the object has the *cacheKey* method. If this method exists on the object then
this value will be used. Otherwise the checksum of the object dump will be
used.
The example below uses an user object as cache key. The name property of this
object will be shown: ::
{use $userObj}
{cache_template keys $userObj}
{$userObj->name}
The user object can be implemented like: ::
class User
{
public $firstName;
public $lastName;
public $name;
public $id;
public function cacheKey()
{
return $this->id;
}
public function __construct( $firstName, $lastName, $id = 1 )
{
$this->firstName = $firstName;
$this->lastName = $lastName;
$this->name = $firstName . " " . $lastName;
$this->id = $id;
}
}
And the application calling the template is done like this: ::
$t = new ezcTemplate();
$t->send->userObj = new User( "Bernard", "Black" );
echo $t->process( "my_template.ezt" );
Time-to-live
````````````
The *ttl* parameter makes it possible to specify how long a cache block should
live in number of seconds. The cache is removed after the timeout. Note that
this behavior is slightly different than the *cache-key*. The *cache-key*
creates a new version of the cache but does not remove any cache files.
The next example creates a cache block that is valid for two hours::
{cache_block ttl 2*60*60}
News that changes not too often.
{/cache_block}
Via a custom function the TTL can be used to renew the cache at certain
predefined timestamps: ::
{cache_block ttl getRemainingTime(minute=0, hour=6)}
Updated each morning at 6 o'clock
{/cache_block}
The custom function *getRemainingTime* calculates the remaining seconds until
it is 6 o'clock in the morning.
Cache manager
-------------
In the previous sections it's already hinted about the cache manager. The cache
manager is a slightly complex but powerful mechanism to control the cached
pages. The cache manager is an interface that needs to be implemented and
assigned to the Template engine. The implementation gives the application the
possibilities to:
- Mark expired cache files based on application criteria.
- Remove old cache files.
- Keep track of the cache dependencies.
The solutions above need to be implemented from the cache manager interface.
This implementation allows the developer to make an application-dependent
solution.
The cache manager interface
```````````````````````````
The user application and the Template engine communicate via method calls on
the cache manager implementation. The cache manager interface has the following
methods: ::
interface ezcTemplateCacheManager
{
public function startCaching( $template, $templatePath, $cachePath, $cacheKeys );
public function stopCaching();
public function isValid( $template, $templateName, $cacheName );
public function register( $name, $value );
public function update( $name, $value );
public function includeTemplate( $template, $templatePath );
public function cleanExpired();
}
The Template engine calls the following methods on the interface. Usually those
methods are not called from the user application.
- *startCaching*: When a cache file is going to be created.
- *stopCaching*: After the cache file is created.
- *isValid*: To check whether the given cache file is still valid.
- *includeTemplate*: Notify the cache manager that a sub template is included.
The remaining methods from the interface are called from the user application:
- *register*: Register a value that is related to the cache file currently
created.
- *update*: A registered value is updated. This **may** indicate that a cache
file is outdated.
- *cleanExpired*: Removes the expired cache files.
Typically the cache creation procedure is as follows. This procedure gives
insight how the cache manager can be used:
1. The user application calls the method: *$t->process("my_template.ezt")*.
2. The template *"my_template.ezt"* caches parts of the page.
3. If a cache file exists for the requested template, the Template component
calls *CacheManager::isValid()* to see whether the cache file is still
correct. If it is then the cache contents can be returned. Otherwise a new
cache file will be created and returned.
4. When a cache file is going to be created, the *CacheManager::startCaching()*
method is called. The Cache Manager is now informed that all following
function calls in the application are used inside the cache.
5. Application code returning data which may be modified in the future should
be registered to the cache manager. This is done via the
*CacheManager::register()* method.
6. The *CacheManager::includeTemplate()* is called when another template is
included.
7. The *CacheManager::stopCaching()* is called when the cache file is created.
8. The user application should call the *CacheManager::update()* method when a
change in the database or application may affect any of the caches.
Keeping track of the values used in the caches is probably best stored in a
database. Using the file system is also possible but makes things more
complicated.
Database storage
````````````````
The database is a good medium to keep track of all the generated caches. It can
quickly mark lots of cache files as expired. The real removal of the cache files
from the file system can then be done when the server load is low.
In which database tables the cache information is stored depends on the
implementation of the cache manager. In our example we will use two tables:
*cache_files* and *cache_values*. The table *cache_files* has at least the
following fields:
+---------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+===============+==================+======+=====+=========+================+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
+---------------+------------------+------+-----+---------+----------------+
| cache | varchar(255) | NO | MUL | | |
+---------------+------------------+------+-----+---------+----------------+
| expired | tinyint(4) | NO | | 0 | |
+---------------+------------------+------+-----+---------+----------------+
The fields store:
:id: Is the unique cache identifier.
:cache: The relative path to the cache file.
:expired: Has the value 0 if the cache is valid, otherwise 1.
The table *cache_values* has the following fields:
+-------------+------------------+------+-----+---------+-------+
| **Field** | Type | Null | Key | Default | Extra |
+=============+==================+======+=====+=========+=======+
| cache_id | int(10) unsigned | NO | PRI | | |
+-------------+------------------+------+-----+---------+-------+
| name | varchar(50) | NO | PRI | | |
+-------------+------------------+------+-----+---------+-------+
| value | varchar(255) | NO | PRI | | |
+-------------+------------------+------+-----+---------+-------+
The fields store:
:cache_id: The relation to *cache_files.id*.
:name: Name of the value that is stored.
:value: The value that belongs to the name.
Another table can also store the cache keys that belong to the cache. The
structure of this table is similar to the table *cache_values*.
Database fetch
``````````````
Often a database result needs to be shown in a template. Two techniques are
commonly used:
- Do the database queries inside the application. Send the result to the
template.
- Implement a custom function or block that can retrieve database information
inside the template.
The first solution has arguably a better separation between application
and layout code. The second solution has the main advantage that it is easier
to optimize. In this section we will continue with the second form.
The following template code shows the user information of a single user. The
template uses a custom function *db_fetch* that retrieves the user information
as an object: ::
{use $userID}
{cache_template keys $userID}
{var $userObj = db_fetch(object="user", id=$userID)}
Name: {$userObj->name} <br/>
Name: {$userObj->address} <br/>
Email:{$userObj->email}<br/>
The caching system creates a separate cache file for each *userID*. This
cache file is valid, until information changes. If *userID* 1 changes
its e-mail address, the Template engine will still use the old cache file. The
solution for this problem is that the application informs the Template engine
via the Cache Manager about this information update.
When the template is run for the first time, it calls the following methods in
order:
1. *startCaching( $template, $templatePath, $cachePath, $cacheKeys )*. The
current template will be cached. The $cachePath should be stored in the
*cache_files* database. The following code could be an implementation of
the *startCaching* method: ::
public function startCaching( $template, $templatePath, $cachePath, $cacheKeys )
{
// Get the current database handler.
$db = ezcDbInstance::get();
// Get the current cache ID, if it does exist.
$q = $db->prepare( "SELECT id FROM cache_files WHERE cache = :cache" );
$q->bindValue( ":cache", $cachePath );
$q->execute();
// One result and make sure the query is terminated.
$r = $q->fetchAll();
if ( sizeof( $r ) > 0 ) // Do we have any results?
{
$id = $r[0]["id"];
// Renew the cache_file.
$s = $db->prepare( "UPDATE cache_files SET expired=0 WHERE id = :id" );
$s->bindValue( ":id", $id );
$s->execute();
}
else
{
// Insert the new cache file
$q = $db->prepare( "INSERT INTO cache_files VALUES( '', :cache, '', 0)" );
$q->bindValue( ":cache", $cachePath );
$q->execute();
$id = $db->lastInsertId();
}
// Mark that the template is currently creating a cache.
$this->isCaching = true;
$this->template_id = $id;
}
The code checks whether the cache file is already registered. If the cache file
is available then it sets the *expired* status to 0. Otherwise it adds the
cache to the table, with a new id and *expired* set to 0.
The *cache_files* table contains something like:
== ================================== =======
id cache expired
== ================================== =======
01 compiles/cache/user.ezt-9dsafa0 0
02 compiles/cache/user.ezt-84fdvj2 0
== ================================== =======
2. *register( $name, $value )*. This function is called by the user
application **during** cache creation. In our example the fetch function
which returns the user object should call the *register* function. ::
public static function fetch( $object, $id )
{
$db = ezcDbInstance::get();
$s = $db->createSelectQuery();
$s->select( "*" )->from( $object )->where( $s->expr->eq( "id", $id ) );
$statement = $s->prepare();
$statement->execute();
$result = $statement->fetchAll();
// Inform the cache manager
ezcTemplateConfiguration::getInstance()->cacheManager->register( $object, $result[0]["id"] );
}
An example implementation of the *register* method is as follows: ::
public function register( $name, $value )
{
if ( $this->isCaching )
{
$db = ezcDbInstance::get();
$s = $db->prepare( "REPLACE INTO cache_values VALUES ( :id, :name, :value )" );
$s->bindValue( ":id", $this->template_id );
$s->bindValue( ":name", $name );
$s->bindValue( ":value", $value );
$s->execute();
}
}
After registration the *cache_values* table contains something like:
== ============= =======
id name value
== ============= =======
01 user 1
02 user 2
== ============= =======
Via the *cache_files* table, we know that the user ID 1 is bound to the
cache file 'compiles/cache/user.ezt-9dsafa0' and it is not expired.
3. *stopCaching()*. The method is called when the caching is done. The
only thing that needs to be done, is to set *isCaching* to *false*. ::
public function stopCaching()
{
$this->isCaching = false;
}
Right now, the cache file is created. The database contains the user IDs
requested during the cache creation. Since the cache file is available, the
Template engine will also call the *isValid()* method on the next request. If
this method returns *true* then the values used in the cache are unaltered.
Let's continue with the example and assume that the user application does
change the data of one user.
4. *update( $name, $value )*. The user application calls this method when a
value changes that is possibly used inside the templates. The snippet below
modifies the user name. ::
public static function updateUser( $id )
{
$db = ezcDbInstance::get();
$q = $db->createUpdateQuery();
$q->update( "user" )->set( "email", $q->bindValue( "bernard@example.com" ) )->where( $q->expr->eq( 'id', $id ) );
$st = $q->prepare();
$st->execute();
// Update the users.
ezcTemplateConfiguration::getInstance()->cacheManager->update( "user", $id );
}
Most important is the last line which updates the user with the ID $id. The
*CacheManager::update* method can be implemented like: ::
public function update( $name, $value )
{
$db = ezcDbInstance::get();
$qry = "UPDATE cache_files, cache_values SET cache_files.expired=1 ".
"WHERE cache_files.id = cache_values.cache_id AND
cache_values.name = :name AND cache_values.value = :value";
$s = $db->prepare( $qry );
$s->bindValue( ":name", $name );
$s->bindValue( ":value", $value );
$s->execute();
}
This method expires all the cache files that have the matching cache
values: $name and $value. If we assume that user with ID 1 changes the e-mail
address, the *cache_files* table is:
== ================================== =======
id cache expired
== ================================== =======
01 compiles/cache/user.ezt-9dsafa0 1
02 compiles/cache/user.ezt-84fdvj2 0
== ================================== =======
And the *cache_values* table remains the same:
== ============= =======
id name value
== ============= =======
01 user 1
02 user 2
== ============= =======
5. *isValid( $template, $templateName, $cacheName )*. The Template engine calls
this method to check whether the cache is still valid. See an example
implementation below::
public function isValid( $template, $templateName, $cacheName )
{
$db = ezcDbInstance::get();
// Check whether the cache is registered and if it's expired.
$q = $db->prepare( "SELECT id, expired FROM cache_files WHERE cache = :cache" );
$q->bindValue( ":cache", $cacheName );
$q->execute();
$r = $q->fetchAll(); // Expect 0 or 1 result
if ( count( $r ) == 0 || $r[0]["expired"] == 1 )
{
return false;
}
return true;
}
When the cache file does not exist or when the cache file is marked as
*expired* the function returns *false*, otherwise *true*. The Template
engine regenerates the cache if this method returns *false*. Otherwise the
cache is displayed.
In this section we have discussed how to expire the cache when values in the
database change. The implementation of the cache manager is limited to this
problem for now. The following sections explain other problems that can be solved
using the cache manager.
Loose coupling
``````````````
In the previous section we explained that the cache manager should be informed
when data used in the templates is requested or altered. This requirement
enforces strong dependencies, everywhere in the application code are function
calls to the cache manager.
The previous section uses the following two example functions inside the user
application to update the cache manager::
public static function fetch( $object, $id )
{
$db = ezcDbInstance::get();
$s = $db->createSelectQuery();
$s->select( "*" )->from( $object )->where( $s->expr->eq( "id", $id ) );
$statement = $s->prepare();
$statement->execute();
$result = $statement->fetchAll();
// Inform the cache manager
ezcTemplateConfiguration::getInstance()->cacheManager->register( $object, $result[0]["id"] );
}
public static function updateUser( $id )
{
$db = ezcDbInstance::get();
$q = $db->createUpdateQuery();
$q->update( "user" )->set( "email", $q->bindValue( "bernard@example.com" ) )->where( $q->expr->eq( 'id', $id ) );
$st = $q->prepare();
$st->execute();
// Update the users.
ezcTemplateConfiguration::getInstance()->cacheManager->update( "user", $id );
}
The call to the *register* and *update* methods are making dependencies with
the cache manager. These method calls can be replaced with a signal from the
SignalSlot component. See the following example code::
public static function fetch( $object, $id )
{
$db = ezcDbInstance::get();
// ...
$result = $statement->fetchAll();
if( $object == "user")
{
User::signals()->emit( "fetched", "user", $results[0]["id"] );
}
}
public static function updateUser( $id )
{
$db = ezcDbInstance::get();
// ...
$st->execute();
User::signals()->emit( "updated", "user", $id );
}
The emitted signal describes what just has happened. E.g. There is a user
updated with ID number $id. This signal can be used not only for the cache
manager but also for e.g. logging.
The *User::signals()* method returns an ezcSignalCollection that belongs to the
User objects. A signal is sent everytime a user is read or changed. The
*signals* method can be implemented as follows::
public static function signals()
{
if( self::$signals == null )
{
self::$signals = new ezcSignalCollection( __CLASS__ );
}
return self::$signals;
}
The next step is to connect the signals to the cache manager. The connection
should be done at a place before the Template is processed. For example in the
*index.php* file. Creating the connections can be done like::
$tc = ezcTemplateConfiguration::getInstance();
User::signals()->connect( "fetched", array( $tc->cacheManager, "register" ) );
User::signals()->connect( "updated", array( $tc->cacheManager, "update" ) );
The signal *fetched* from the *User* object is connected to the *register*
method from the cache manager. And the *update* signal is connected to the
*update* method from the cache manager.
Including templates
```````````````````
Assume there are two templates: A and B. Template A is cached and includes
template B. Below is the code of template A: ::
{cache_template}
{include "B"}
Template B prints only: ::
Hello world
After template A is executed a cache file is created. This cache file contains
the same data as template B. Everything will work fine, until the developer
changes template B.
Template B is altered to: ::
Goodbye world
Template A uses the cache and therefore the modification of template B is not
checked. Template A will still print "Hello world".
The cache manager can solve the issue described above with the cost of some
overhead.
The cache manager implemented in the section `Database fetch`_ should be
modified at three points:
1. The method *includeTemplate* should be implemented. The *includeTemplate*
is called everytime a cached template includes another template. The
following code stores the include template in the *cache_values* table. ::
public function includeTemplate( $template, $templatePath )
{
if ( $this->depth >= 0 )
{
$db = ezcDbInstance::get();
$id = $this->keys[$this->depth]["cache_id"];
$q = $db->prepare( "REPLACE INTO cache_values VALUES(:id, :name, :value)" );
$q->bindValue( ":id", $id );
$q->bindValue( ":name", "include" );
$q->bindValue( ":value", $templatePath );
$q->execute();
}
}
2. The *startCaching* method should also store the template currently
processed. The following code should be added in the body of the 'else'
statement: ::
$q = $db->prepare( "REPLACE INTO cache_values VALUES(:id, :name, :value)" );
$q->bindValue( ":id", $id );
$q->bindValue( ":name", "include" );
$q->bindValue( ":value", $templatePath );
$q->execute();
The current template includes 'itself'. This makes it easier to expire the
cache.
3. The *isValid* method should be extended that it also checks all the
modification times of the included cache files: ::
$q = $db->prepare( "SELECT * FROM cache_values WHERE name = 'include' AND cache_id = :id" );
$q->bindValue( ":id", $r[0]["id"] );
$q->execute();
$r = $q->fetchAll();
foreach ( $r as $a )
{
if ( filemtime( $a["value"] ) > filemtime( $cacheName ) )
{
return false;
}
}
return true;
The code retrieves all the included cache files and checks if an included
cache file is newer than the requested cache file.
The complete code of the include mechanism is documented in the cache manager
interface.
Translations
============
With the `tr_context`_ and tr_ template constructs you can add translatable
strings to you application. For them to work you have to take a few steps.
First of all, you need to make sure that you have the TemplateTranslationTiein
and Translation_ components installed as well. The TranslationTemplate
component provides the glue between the Template component and the Translation
component.
Then you have to configure the template system with the correct configuration for
translations::
<?php
// Create a normal template configuration and create the translation configuration
$tc = new ezcTemplateConfiguration;
$tc->translation = ezcTemplateTranslationConfiguration::getInstance();
// Create a translation manager, and assign it to the template translation
// configuration
$backend = new ezcTranslationTsBackend( dirname( __FILE__ ). '/translations' );
$backend->setOptions( array( 'format' => '[LOCALE].xml' ) );
$manager = new ezcTranslationManager( $backend );
$tc->translation->manager = $manager;
// Set the locale to use
$tc->translation->locale = $locale;
?>
.. _Translation: introduction_Translation.html
More information on how to configure the ezcTranslationManager can be found in
the documentation of the Translation_ component. Of course, the template configuration
can also be made through `Lazy initialization`_.
After the template system knows about the translations, it will use the
Translation component's mechanism to fetch translations.
More information
================
For more information, see the:
* `Template functions`_
* `EBNF`_
* `API documentation`_
.. _`Template functions`: ../Template/functions.html
.. _`EBNF`: ../Template/EBNF.html
.. _`API documentation`: ../Template/phpdoc/classtrees.html
..
Local Variables:
mode: rst
fill-column: 79
End:
vim: et syn=rst tw=79