phpTenjin User's Guide

last update: $Date: 2008-02-22 23:39:32 +0900 (Fri, 22 Feb 2008) $

Release: 0.0.2

Table of Contents:

Introduction

Overview

phpTenjin is a very fast and lightweight template engine based on embedded PHP. You can embed PHP statements and expressions into your text file. phpTenjin converts it into PHP script and evaluate it.

The following is an example of phpTenjin.

File 'ex.phtml':
Hello {==$name=}!
<ul>
<?php foreach ($items as $item) { ?>
 <li>{=$item=}</li>
<?php } ?>
</ul>

Here is the notation:

  • <?php ... ?> represents embedded PHP statements.
  • {== ... =} represents embedded PHP expression.
  • {= ... =} represents embedded escaped (sanitized) PHP expression.
Result of covertion into PHP script:
$ phptenjin -s ex.phtml
<?php echo 'Hello ', $name, '!
<ul>
'; foreach ($items as $item) { 
echo ' <li>', htmlspecialchars($item), '</li>
'; } 
echo '</ul>
';
?>
Output of execution with context data:
$ phptenjin -c '$name="World"; $items=array("<AAA>","B&B","\"CCC\"");' ex.phtml
Hello World!
<ul>
 <li>&lt;AAA&gt;</li>
 <li>B&amp;B</li>
 <li>&quot;CCC&quot;</li>
</ul>
Example of PHP script
<?php
require_once 'Tenjin.php';
$engine = new Tenjin_Engine();
$context = array('name'=>'World', 'items'=>array('<AAA>', 'B&B', '"CCC"'));
$output = $engine->render('ex.phtml', $context);
echo($output);
?>

Features

phpTenjin has the following features:

  • phpTenjin runs very fast. It works about twice as fast as Smarty.
  • phpTenjin doesn't break HTML design because it uses XML Processing Instructions (PI) as embedded notation for PHP statements.
  • phpTenjin is secure because it supports escaping expression value by default.
  • phpTenjin is small and lightweight. It is very easy to include phpTenjin into your application.
  • phpTenjin supports auto caching of converted PHP code.
  • phpTenjin supports partial template and layout template. These are very useful especially for web application.
  • phpTenjin supports partial capturing of template.
  • phpTenjin can load YAML file as context data. Using phpTenjin, it is able to separate template file and data file.

Comparison with other solutions

phpTenjin has advantages compared with other solutions (including other language solutions):

  • Easy to design -- JSP, ePerl, or eRuby breaks HTML design because they use '<% ... %>' as embedded notation which is not valid in HTML. phpTenjin doesn't break HTML desgin because it uses Processing Instructions (PI) as embedded notation which is valid in HTML.
  • Easy to write -- In PHP, it is a little bother to write embedded expression because the notation '<?php echo $value; ?>' is a little long, and very bother to embed escaped expression because '<?php echo htmlspecialchars($value); ?>' is very long. In phpTenjin, these are very easy to write because embedded expression notations ('#{value}' and '${value}') are very short.
  • Easy to learn -- Some template systems are hard to learn because they are large, highly functinal, and based on non-PHP syntax. phpTenjin is very easy to learn if you already know PHP language because it is very small and you can embed any PHP code into HTML template.

Benchmark

Benchmark script is contained in phpTenjin archive. The following is an example of benchmark.

$ cd pltenjin-X.X.X/benchmark
$ php -v
PHP 5.2.0 (cli) (built: Jan 29 2007 18:35:33) 
Copyright (c) 1997-2006 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2006 Zend Technologies
$ php bench.php -n 10000
*** ntimes=10000
                          user        sys      total       real
tenjin                  4.2300     1.1600     5.3900     5.4362
tenjin_reuse            3.1600     0.4800     3.6400     3.6688
smarty                 10.1100     0.7300    10.8400    10.9260
smarty_reuse            9.5100     0.6900    10.2100    10.2996
php                     3.0300     0.4900     3.5200     3.5620

Installation

  1. Just copy 'lib/Tenjin.php' and 'bin/pltenjin' into your proper directory.
    $ sudo cp lib/Tenjin.php /usr/local/lib/php
    $ sudo cp bin/pltenjin /usr/local/bin
    
  2. (Optional) Install Syck library for YAML parsing.

Designer's Guide

This section shows how to use phpTenjin for designer.

If you want to know how to use phpTenjin in your program, see Developer's Guide section.

Notation

The following is the notation of phpTenjin.

  • '<?php ... ?>' represents embedded PHP statement.
  • '{== ... =}' represents embedded PHP expression.
  • '{= ... =}' represents embedded PHP expression which is to be escaped (for example, '& < > "' is escaped to '&amp; &lt; &gt; &quot;').
File 'example1.phtml':
<table>
  <tbody>
<?php $items = array('<foo>', 'bar&bar', '"baz"'); ?>
<?php foreach ($items as $item) { ?>
    <tr>
      <td>{==$item=}</td>
      <td>{=$item=}</td>
    </tr>
<?php } ?>
  <tbody>
</table>

The following is the result of executing 'example1.phtml'.

Result:
$ phptenjin example1.phtml
<table>
  <tbody>
    <tr>
      <td><foo></td>
      <td>&lt;foo&gt;</td>
    </tr>
    <tr>
      <td>bar&bar</td>
      <td>bar&amp;bar</td>
    </tr>
    <tr>
      <td>"baz"</td>
      <td>&quot;baz&quot;</td>
    </tr>
  <tbody>
</table>

phpTenjin's error reporting may be not useful because it doesn't report filename on which error happended in default(*1). The following is an error example and it shows that errored filename is not reported.

$ phptenjin ex2.phtml

Parse error: syntax error, unexpected T_ECHO in /usr/local/lib/php/Tenjin.php(497) : eval()'d code on line 2

If you specify --cache option, phptenjin will report errored filename (and also temporary cache file will be created)(*2).

$ phptenjin --cache ex2.phtml

Parse error: syntax error, unexpected T_ECHO in /home/tmp/ex2.phtml.cache on line 2
(*1)
This is a restriction of phpTenjin because eval() of PHP can't take filename as argument.
(*2)
If --cache is specified, PHP's function include() is called instead of eval().

Convert into PHP Code

Command-line option '-s' converts embedded files into PHP code.

Result:
$ phptenjin -s example1.phtml
<?php echo '<table>
  <tbody>
'; $items = array('<foo>', 'bar&bar', '"baz"'); 
 foreach ($items as $item) { 
echo '    <tr>
      <td>', $item, '</td>
      <td>', htmlspecialchars($item), '</td>
    </tr>
'; } 
echo '  <tbody>
</table>
';
?>
  • Variable is a array. Command-line option --escapefunc=func makes phptenjin to use func() instead of escape().
  • Newline character ("\n" or "\r\n") is automatically detected by phpTenjin.

'<?php' is called as preamble and '?>' is called as postamble. Command-line option '-b' removes preamble and postamble.

File 'example2.phtml'
<?php $list = array(1, 2, 3); ?>
<?php for ($list as $item) { ?>
<p>{==$item=}</p>
<?php } ?>
Result:
$ phptenjin -s example2.phtml
<?php  $list = array(1, 2, 3); 
 for ($list as $item) { 
echo '<p>', $item, '</p>
'; } 
?>
$ phptenjin -sb example2.phtml
 $list = array(1, 2, 3); 
 for ($list as $item) { 
echo '<p>', $item, '</p>
'; } 

Command-line option '-S' also show converted PHP code but it doesn't print text part. This is useful to check PHP code for debugging.

Result:
$ phptenjin -S example1.phtml
<?php 

 $items = array('<foo>', 'bar&bar', '"baz"'); 
 foreach ($items as $item) { 

          $item; 
          htmlspecialchars($item); 

 } 


?>

In addition, the following command-line options are available.

-N
Add line number.
-X
Delete expressions.
-C
Remove empty lines (compact-mode).
-U
Compress empty lines to a line (uniq-mode).
Result:
$ phptenjin -SUNX example1.phtml
    1:  <?php 

    3:   $items = array('<foo>', 'bar&bar', '"baz"'); 
    4:   foreach ($items as $item) { 

    6:            $item; 
    7:            htmlspecialchars($item); 

    9:   } 

   12:  ?>

Syntax Checking

Command-line option '-z' checks syntax error in embedded PHP code.

File example3.phtml:
<ul>
<?php for ($items as $item) { ?>
  <li>{=$item=}</li>
<?php } ?>
</ul>
Result:
$ phptenjin -z example3.phtml
[NG] example3.phtml
Parse error: syntax error, unexpected T_AS, expecting ';' in example3.phtml on line 2
Errors parsing example3.phtml

Command-line option '-z' is more convenient than 'phptenjin -s file | php -l' because the former can take several filenames.

Command-line option '-q' (quiet-mode) prints nothing if it has no errors.

Context Data File

phpTenjin allows you to specify context data by YAML file or PHP script.

File 'example4.phtml':
<p>
  {=$text=}
  {==$num=}
  {==$flag=}
</p>

<?php foreach ($items as $item) { ?>
<p>{=$item=}</p>
<?php } ?>

<?php foreach ($hash as $key=>$val) { ?>
<p>{=$key=} = {=$val=}</p>
<?php } ?>
File 'datafile.yaml':
text:   foo
num:    3.14
flag:   yes
items:
  - foo
  - bar
  - baz
hash:
  x: 1
  y: 2
Result:
$ phptenjin -f datafile.yaml example4.phtml
<p>
  foo
  3.14
  1
</p>

<p>foo</p>
<p>bar</p>
<p>baz</p>

<p>x = 1</p>
<p>y = 2</p>
File 'datafile.php':
$text  =  "foo";
$num   =  3.14;
$flag  =  true;
$items =  array("foo", "bar", "baz");
$hash  =  array('x'=>1, 'y'=>2);
Result:
$ phptenjin -f datafile.php example4.phtml
<p>
  foo
  3.14
  1
</p>

<p>foo</p>
<p>bar</p>
<p>baz</p>

<p>x = 1</p>
<p>y = 2</p>

You must install Syck library if you want to use YAML-format context data file.

Command-line Context Data

Command-line option '-c' specifies context data in YAML format or PHP code.

File 'example5.phtml':
text:  {==$text=}
items:
<?php foreach ($items as $item) { ?>
  - {==$item=}
<?php } ?>
hash:
<?php foreach ($hash as $key=>$val) { ?>
  {==$key=}: {==$val=}
<?php } ?>
Result of context data in php code:
$ phptenjin -c '$text="foo"; $items=array("a","b","c"); $hash=array("x"=>1,"y"=>2);' example5.phtml
text:  foo
items:
  - a
  - b
  - c
hash:
  x: 1
  y: 2
Result of context data in yaml format:
$ phptenjin -c '{text: foo, items: [a, b, c], hash: {x: 1, y: 2} }' example5.phtml
text:  foo
items:
  - a
  - b
  - c
hash:
  x: 1
  y: 2

You must install Syck library at first if you want to specify context data in YAML format.

Nested Template

Template can include other templates. Included templates can also include other templates.

The following function is available to include other templates.

import(template_name)
Include other template.
File 'example6.phtml':
<html>
  <body>

    <div id="sidemenu">
<?php import('sidemenu.phtml'); ?>
    </div>

    <div id="main-content">
<?php foreach ($items as $item) { ?>
      <p>{=$item=}</p>
<?php } ?>
    </div>

    <div id="footer">
<?php import('footer.phtml'); ?>
    </div>

  </body>
</table>
File 'sidemenu.phtml':
<ul>
<?php foreach ($menu as $item) { ?>
  <li><a href="{=$item['url']=}">{=$item['name']=}</a></li>
<?php } ?>
</ul>
File 'footer.phtml':
<hr />
<address>
  <a href="mailto:{=$webmaster_email=}">{=$webmaster_email=}</a>
</address>
File 'contextdata.php':
$items = array('<FOO>', '&BAR', '"BAZ"');
$webmaster_email = 'webmaster@example.com';
$menu = array(
          array('name'=>'Top',   'url'=>'/'),
          array('name'=>'Products',   'url'=>'/prod'),
          array('name'=>'Support',   'url'=>'/support'),
        );
Result:
$ phptenjin -f contextdata.php example6.phtml
<html>
  <body>

    <div id="sidemenu">
<ul>
  <li><a href="/">Top</a></li>
  <li><a href="/prod">Products</a></li>
  <li><a href="/support">Support</a></li>
</ul>
    </div>

    <div id="main-content">
      <p>&lt;FOO&gt;</p>
      <p>&amp;BAR</p>
      <p>&quot;BAZ&quot;</p>
    </div>

    <div id="footer">
<hr />
<address>
  <a href="mailto:webmaster@example.com">webmaster@example.com</a>
</address>
    </div>

  </body>
</table>

'import()' can take template filename (ex. 'user_main.phtml') or template short name (ex. ':main'). Template short name represents a template in short notation. It starts with colon (':').

To make template short name available, command-line option '--prefix' and '--postfix' are required. For example, 'include("user_main.phtml")' can be described as 'include(":main")' when '--prefix="user_"' and '--postfix=".phtml"' are specified in command-line.

Layout Template

Command-line option '--layout=templatename' specifies layout template name.

For example, 'exmample6.phtml' template in the previous section can be divided into layout file 'layout6.phtml' and content file 'content6.phtml'. Variable '$_content' in layout template represents the result of content file.

File 'layout6.phtml':
<html>
  <body>

    <div id="sidemenu">
<?php import('sidemenu.phtml'); ?>
    </div>

    <div id="main-content">
{==$_content=}
    </div>

    <div id="footer">
<?php import('footer.phtml'); ?>
    </div>

  </body>
</table>
File 'content6.phtml':
<?php foreach ($items as $item) { ?>
  <p>{=$item=}</p>
<?php } ?>
Result:
$ phptenjin -f contextdata.php --layout=layout6.phtml content6.phtml
<html>
  <body>

    <div id="sidemenu">
<ul>
  <li><a href="/">Top</a></li>
  <li><a href="/prod">Products</a></li>
  <li><a href="/support">Support</a></li>
</ul>
    </div>

    <div id="main-content">
  <p>&lt;FOO&gt;</p>
  <p>&amp;BAR</p>
  <p>&quot;BAZ&quot;</p>

    </div>

    <div id="footer">
<hr />
<address>
  <a href="mailto:webmaster@example.com">webmaster@example.com</a>
</address>
    </div>

  </body>
</table>

Target template and layout template don't share local variables. It means that local variables set in a template are not available in layout template.

If you want variables set in a temlate to be available in layout template, you should use '$_context' array.

File 'layout7.phtml':
...
<h1>{=$title=}</h1>

<div id="main-content">
{==$_content=}
<div>

<a href="{=$url=}">Next page</a>
...
File 'content7.phtml':
<?php $_context['title'] = 'Document Title'; ?>
<?php $_context['url']   = '/next/page'; ?>
<table>
  ...content...
</table>
Result:
$ phptenjin --layout=layout7.phtml content7.phtml
...
<h1>Document Title</h1>

<div id="main-content">
<table>
  ...content...
</table>

<div>

<a href="/next/page">Next page</a>
...

Using '$_context["_layout"]', it is able to specify layout template name in each template file. If you assigned FALSE to '$_context["_layout"]', no layout template is used.

File 'content8.phtml':
<?php $_context['_layout'] = ':layout8_xhtml'; ?>
<h1>Hello World!</h1>
File 'layout8_html.phtml':
<html>
  <body>
{==$_content=}
  </body>
</html>
File 'layout8_xhtml.phtml':
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <body>
{==$_content=}
  </body>
</html>
Result: ':layout8_html' is specified in command-line option but ':layout8_xhtml' is used
$ phptenjin --postfix='.phtml' --layout=':layout8_html' content8.phtml
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <body>
<h1>Hello World!</h1>

  </body>
</html>

Capturing

It is able to capture any part of template.

File 'example9.phtml':
<?php $_context['title'] = 'Capture Test'; ?>
<html>
  <body>

<?php start_capture('content_part'); ?>
    <ul>
<?php for ($i = 0; $i <= 2; $i++) { ?>
      <li>i = {==$i=}</li>
<?php } ?>
    </ul>
<?php stop_capture() ?>

<?php start_capture('footer_part'); ?>
    <div class="footer">copyright&copy; 2007 kuwata-lab.com</div>
<?php stop_capture(); ?>

  </body>
</html>

Captured strings are accessable as local variables. For example, you can get captured string as a variable 'content_part' in the above example.

A template can contain several capturing. It is not able to nest capturing.

In layout file, it is able to use strings captured in templates.

File 'layout9.phtml':
<html lang="en">
  <head>
    <title>{=$title=}</title>
  </head>
  <body>

    <!-- HEADER -->
<?php start_placeholder('header_part'); ?>
    <h1>{=$title=}</h1>
<?php stop_placeholder(); ?>
    <!-- /HEADER -->

    <!-- CONTENT -->
{==$content_part=}
    <!-- /CONTENT -->

    <!-- FOOTER -->
<?php start_placeholder('footer_part'); ?>
    <hr />
    <address>webmaster@localhost</address>
<?php stop_placeholder(); ?>
    <!-- /FOOTER -->

  </body>
</html>

'start_placeholder("name") ... stop_placeholder()' is quivarent to the following.

<?php if ($_context['name']) { ?>
{==$_context['name']=}
<?php } else { ?>
    ...
<?php } ?>

The following result shows that content part and footer part are overrided by capturing in content template but header part is not.

Result:
$ phptenjin --layout=layout9.phtml example9.phtml
<html lang="en">
  <head>
    <title>Capture Test</title>
  </head>
  <body>

    <!-- HEADER -->
    <h1>Capture Test</h1>
    <!-- /HEADER -->

    <!-- CONTENT -->
    <ul>
      <li>i = 0</li>
      <li>i = 1</li>
      <li>i = 2</li>
    </ul>

    <!-- /CONTENT -->

    <!-- FOOTER -->
    <div class="footer">copyright&copy; 2007 kuwata-lab.com</div>
    <!-- /FOOTER -->

  </body>
</html>

Template Arguments

It is able to specify template arguments in template files. Template arguments are variables which are passed by main program via context object. In the following example, 'title' and 'name' are template arguments.

File 'example10.phtml':
<?xml version="1.0"?>
<?php //@ARGS title, name ?>
<h1>{=$title=}</h1>
<p>Hello {=$name=}!</p>

Template arguments line is converted into assignment statements of local variables.

$ phptenjin -s example10.phtml
<?php echo '<?xml version="1.0"?>
'; $title = @$_context['title']; $name = @$_context['name'];
echo '<h1>', htmlspecialchars($title), '</h1>
<p>Hello ', htmlspecialchars($name), '!</p>
';
?>

If template arguments are specified, other variables passed by context object are not set.

File 'example11.phtml':
<p>
<?php //@ARGS x ?>
x = {=$x=}
y = {=$y=}   # error
</p>
Result:
$ phptenjin -c '$x=10; $y=20' example11.phtml
<p>
x = 10
y = 
Notice: Undefined variable: y in /usr/local/lib/php/Tenjin.php(488) : eval()'d code on line 4
   # error
</p>

Special variable '$_context' which represents context object is always available whether template arguments are specified or not.

Preprocessing

phpTenjin supports preprocessing of template. Preprocessing executes some logics when templates are loaded and that logics are not executed when rendering. Preprocessing makes your application much faster.

Notation of preprocessing is the following.

  • <?PHP ... ?> represents preprocessing statement.
  • #{{...}} represents preprocessing expression (without HTML escape).
  • ${{...}} represents preprocessing expression (with HTML escape).

For example, assume the following template.

File 'example12.phtml':
<?PHP $states = array('CA'=>"California", ?>
<?PHP                 'NY'=>"New York", ?>
<?PHP                 'FL'=>"Florida",  ?>
<?PHP                 'TX'=>"Texas",  ?>
<?PHP                 'HI'=>"Hawaii"); ?>
<?php $state = $params['state']; ?>
<?php $chk = array(true=>' selected="selected"', false=>''); ?>
<select name="state">
  <option value="">-</option>
<?PHP $codes = array_keys($states); ?>
<?PHP sort($codes); ?>
<?PHP foreach ($codes as $code) { ?>
  <option value="{*==$code=*}"{==$chk[$state=='{*==$code=*}']=}>{*=$states[$code]=*}</option>
<?PHP } ?>
</select>

If preprocessing is activated, the above will be converted into the following when template is loaded. (Command-line option -P shows the result of preprocessing.)

Result of preprocessing:
$ phptenjin -P example12.phtml
<?php $state = $params['state']; ?>
<?php $chk = array(true=>' selected="selected"', false=>''); ?>
<select name="state">
  <option value="">-</option>
  <option value="CA"{==$chk[$state=='CA']=}>California</option>
  <option value="FL"{==$chk[$state=='FL']=}>Florida</option>
  <option value="HI"{==$chk[$state=='HI']=}>Hawaii</option>
  <option value="NY"{==$chk[$state=='NY']=}>New York</option>
  <option value="TX"{==$chk[$state=='TX']=}>Texas</option>
</select>

This means that for-loop is executed only once when template is loaded and is not executed when rendering. In the result, rendering speed becomes to be much faster.

And the PHP code is here. This shows that there is no for-loop.

Translated script code:
$ phptenjin --preprocess -sb example12.phtml
 $state = $params['state']; 
 $chk = array(true=>' selected="selected"', false=>''); 
echo '<select name="state">
  <option value="">-</option>
  <option value="CA"', $chk[$state=='CA'], '>California</option>
  <option value="FL"', $chk[$state=='FL'], '>Florida</option>
  <option value="HI"', $chk[$state=='HI'], '>Hawaii</option>
  <option value="NY"', $chk[$state=='NY'], '>New York</option>
  <option value="TX"', $chk[$state=='TX'], '>Texas</option>
</select>
';

If you have errors on preprocessing, you should check source script by -Ps option(*3).

The following is an another example. Assume that link_to() is a helper method which takes label and url and generate <a></a> tag. In this case, label and url can be parameterized by _p("...") and _ep("..."). The former is converted into {== ... =} and the latter converted into {= ... =} by preprocessor.

File 'example13.phtml':
<?PHP // ex. link_to('Show', '/show/1')  => <a href="/show/1">Show</a> ?>
<?PHP function link_to($label, $url) { ?>
<?PHP     $url = preg_replace('/%2F/', '/', urlencode($url)); ?>
<?PHP     return "<a href=\"$url\">$label</a>"; ?>
<?PHP } ?>
{*==link_to('Show '._ep('$params["name"]'), '/items/show/'._p('$params["id"]'))=*}
Preprocessed template:
$ phptenjin -P example13.phtml
<a href="/items/show/{==$params["id"]=}">Show {=$params["name"]=}</a>
Translated script code:
$ phptenjin --preprocess -sb example13.phtml
echo '<a href="/items/show/', $params["id"], '">Show ', htmlspecialchars($params["name"]), '</a>
';

There are many web-application framework and they provides helper functions. These helper functions are divided into two groups.

  • link_to() or _() (function for M17N) return the same result when the same arguments are passed. These functions can be expanded by preprocessor.
  • Some functions return the different result even if the same arguments are passed. These functions can't be expaned by preprocessor.

Preprocessor has the power to make your application much faster, but it may make the debugging difficult. You should use it carefully.

(*3)
Command-line option '-Ps' is available but '-PS' is not availabe. This is a current restriction of phptenjin.

Other Options

  • Command-line option '--path=dir1,dir2,...' sepcifies template directory path.

Developer's Guide

This section shows how to use phpTenjin in your PHP script.

If you want to know the notation or features of phpTenjin, see Designer's Guide section.

An Example

The following is an example to use phpTenjin in PHP.

Example:
require_once 'Tenjin.php';
$engine = new Tenjin_Engine();
$context = array('title'=>'phpTenjin Example', 'items'=>array('AAA','BBB','CCC'));
$filename = 'file.phtml';
$output = $engine->render($filename, $context);
echo $output;

Classes and Functions in phpTenjin

phpTenjin has the follwoing classes.

Tenjin_Template
This class represents a template file. An object of Tenjin_Template correspond to a template file.
Tenjin_Engine
This class represents some template objects. It can handle nested template and layout template. Using Tenjin_Engine class, you can use phpTenjin as a template engine for web application.

Class Tenjin_Template

Tenjin_Template class represents a template file. An object of Tenjin_Template corresponds to a template file. It doesn't support nested template nor layout template (use Tenjin_Engine class instead).

This class has the following methods and attributes.

Tenjin_Template($filename=null, properties=array())
Create template object. If filename is given, read and convert it to PHP code.
Tenjin_Template->convert($input, $filename=null)
Convert input text into PHP code and return it.
Tenjin_Template->convert_file($filename)
Convert file into PHP code and return it. This is equivarent to Tenjin_Template->convert(file_get_contents($filename), $filename)
Tenjin_Template->render($context=array())
Convert PHP code, evaluate it with context data, and return the result of evaluation.
Tenjin_Template->script
Converted PHP code

The followings are examples to use Tenjin_Template in PHP script.

File 'example14.phtml':
<h1>{=$title=}</h1>
<ul>
<?php foreach ($items as $item) { ?>
 <li>{=$item=}</li>
<?php } ?>
</ul>
File 'example14.php':
<?php
/// template file
$filename = 'example14.phtml';

/// convert into php code
require_once 'Tenjin.php';
$template = new Tenjin_Template($filename);
/// or
//$template = new Tenjin_Template();
//$script = $template->convert_file($filename);
/// or
//$template = new Tenjin_Template();
//$input = file_get_contents($filename);
//$script = $template->convert($input, $filename);  # filename is optional

/// show converted php code
echo "---- php code ----\n";
echo $template->script;

/// evaluate php code
$context = array('title'=>'phpTenjin Example', 'items'=>array('<AAA>','B&B','"CCC"'));
$output = $template->render($context);
echo "---- output ----\n";
echo $output;
Result:
$ php example14.php
---- php code ----
echo '<h1>', htmlspecialchars($title), '</h1>
<ul>
'; foreach ($items as $item) { 
echo ' <li>', htmlspecialchars($item), '</li>
'; } 
echo '</ul>
';
---- output ----
<h1>phpTenjin Example</h1>
<ul>
 <li>&lt;AAA&gt;</li>
 <li>B&amp;B</li>
 <li>&quot;CCC&quot;</li>
</ul>

Class Tenjin_Engine

Tenjin_Engine class contains some template objects. It can handle nested template and layout template. Using Tenjin_Engine class, you can use phpTenjin as a template engine for web application.

This class has the following methods.

new Tenjin_Engine($properties=array('prefix'=>null, 'postfix'=>null, 'layout'=>null, 'path'=>null, 'cache'=>true, 'preprocess'=>false, 'templateclass'=>'Tenjin_Template'))
Create Engine object. 'path' represents template search path and it should be an array of directory name. If other properties are passed then they are passed to Tenjin_Template() internally.
Tenjin_Engine->render($template_name, $context=array(), $layout=null)
Convert template into PHP code, evaluate it with context data, and return the result of it. If layout is TRUE or NULL then layout template name specified by constructor option is used as layout template, else if FALSE then layout template is not used, else if string then it is regarded as layout template name.

Argument $template_name in render() methods is filename or short name of template. Template short name is a string starting with colon (':'). For example, 'render(":list", context)' is equivarent to 'render("user_list.phtml", context)' if prefix option is 'user_' and postfix option is '.phtml'.

In template file, the followings are available.

$_content
This variable represents the result of evaluation of other template. This is available only in layout template file.
import($template_name)
Include other template file.
start_capture(name)
Start capturing. Result will be stored into $_context['name'].
stop_capture()
Stop capturing.
start_placeholder(varname), stop_placeholder()
Placeholder is a region from start_placeholder() to stop_placeholder(). If captured string as varname exists then use it as content, else use content in placeholder as is. For example,
<?php start_placeholder('foo'); ?>
  ...
<?php stop_placeholder(); ?>
is equivarent to:
<?php if (array_key_exists('foo', $_context['foo'])) { ?>
<?php     echo $_context['foo']; ?>
<?php } else { ?>
  ...
<?php } ?>

(Notice that include(), start_capture(), stop_capture(), start_placeholder(), and stop_placeholder() are built-in macros of phpTenjin and they are not subroutine of PHP.)

The followings are example of Tenjin_Engine class.

File 'user_form.phtml':
<?php //@ARGS params ?>
<p>
  Name:  <input type="text" name="name"  value="{=$params['name']=}" /><br />
  Email: <input type="text" name="email" value="{=$params['email']=}" /><br />
  Gender:
<?php $gender = $params['gender']; ?>
<?php $chk = array(true=>' checked="checked"', false=>''); ?>
  <input type="radio" name="gender" value="m" {=$chk[$gender=='m']=} />Male
  <input type="radio" name="gender" value="f" {=$chk[$gender=='f']=} />Female
</p>
File 'user_create.phtml':
<?php //@ARGS ?>
<form action="user_app.cgi" method="post">
  <input type="hidden" name="action" value="create" />
<?php import(':form'); ?>
  <input type="submit" value="Create" />
</form>
File 'user_edit.phtml':
<?php //@ARGS params ?>
<form action="user_app.cgi" method="post">
  <input type="hidden" name="action" value="edit" />
  <input type="hidden" name="id" value="{=$params['id']=}" />
<?php import(':form'); ?>
  <input type="submit" value="Edit" />
</form>
File 'user_layout.phtml':
<?php //@ARGS _content, title ?>
<html>
  <body>

    <h1>{=$title=}</h1>

    <div id="main-content">
{==$_content=}
    </div>

    <div id="footer">
<?php import('footer.html'); ?>
    </div>

  </body>
</html>
File 'footer.html':
<?php //@ARGS ?>
<hr />
<address>
  <a href="mailto:webmaster@example.com">webmaster@example.com</a>
</address>
File 'user_app.cgi':
#!/usr/bin/env php
<?php

require_once 'Tenjin.php';

/// set action ('create' or 'edit')
$action = 'create';

/// set context data
if ($action == 'create') {
    $title = 'Create User';
    $params = array(
        'name'=>null, 'email'=>null, 'gender'=>null, 'id'=>null,
    );
}
else {
    $title = 'Edit User';
    $params = array(
        'name'   => 'Margalette',
        'email'  => 'meg@example.com',
        'gender' => 'f',
        'id'     => 123,
    );
}
$context = array('title'=>$title, 'params'=>$params);

/// create engine object
$layout = ':layout';   // or 'user_layout.phtml'
$properties = array('prefix'=>'user_', 'postfix'=>'.phtml', 'layout'=>$layout);
$engine = new Tenjin_Engine($properties);

/// evaluate template
$template_name = ':'.$action;   // ':create' or ':edit'
$output = $engine->render($template_name, $context);
echo $output;
Result:
$ php user_app.cgi create
<html>
  <body>

    <h1>Create User</h1>

    <div id="main-content">
<form action="user_app.cgi" method="post">
  <input type="hidden" name="action" value="create" />
<p>
  Name:  <input type="text" name="name"  value="" /><br />
  Email: <input type="text" name="email" value="" /><br />
  Gender:
  <input type="radio" name="gender" value="m"  />Male
  <input type="radio" name="gender" value="f"  />Female
</p>
  <input type="submit" value="Create" />
</form>

    </div>

    <div id="footer">
<hr />
<address>
  <a href="mailto:webmaster@example.com">webmaster@example.com</a>
</address>
    </div>

  </body>
</html>

Template Initialize Options

Tenjin_Template() can take the follwoing options.

  • 'escapefunc' (string) specifies function name to escape string. Default is 'htmlspecialchars'.

Constructor of Tenjin_Engine can also take the same options as above. These options given to constructor of Tenjin_Engine are passed to constructor of Tenjin_Template internally.

File 'example15.php':
<?php
require_once 'tenjin.php';
$filename = 'example14.phtml';
$template = new Tenjin_Template($filename, array('escapefunc'=>'escapeHTML'));
echo $template->script;

function escapeHTML($s) {
    $s = preg_replace('/\&/', '&amp;', $s);
    $s = preg_replace('/</',  '&lt;',  $s);
    $s = preg_replace('/>/',  '&gt;',  $s);
    $s = preg_replace('/"/',  '&quot;', $s);
    $s = preg_replace('/\'/', '&#039;', $s);
    return $s;
}
$context = array(
    'title' => 'phpTenjin Example',
    'items' => array('<foo>', '&bar', '"baz"'),
);
$output = $template->render($context);
echo $output;
?>
Result:
$ php example15.php
echo '<h1>', escapeHTML($title), '</h1>
<ul>
'; foreach ($items as $item) { 
echo ' <li>', escapeHTML($item), '</li>
'; } 
echo '</ul>
';
<h1>phpTenjin Example</h1>
<ul>
 <li>&lt;foo&gt;</li>
 <li>&amp;bar</li>
 <li>&quot;baz&quot;</li>
</ul>

Other Topics

  • Tenjin_Template detects newline character ("\n" or "\r\n") automatically. If input file contains "\r\n", phpTenjin generates output which contains "\r\n".
  • Tenjin_Template->render() can be called many times. If you create a Tenjin_Template object, you can call render() method many times.
  • Tenjin_Template->convert() also can be called many times. If you create a Tenjin_Template object, you can call convert() (and also render()) method many times.