phpTenjin FAQ

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

Release: 0.0.2

Table of contents:

Basic

I got an SyntaxError exception.

Command-line option '-z' checks syntax of template file. You should check template by it.

File 'ex1.phtml':
<ul>
<?php $i = 0; ?>
<?php foreach ($items as $item) { ?>
<?php    $i += 1  // ';' is missed ?>
  <li>{=$item=}</li>
<?php } ?>
</ul>
Result:
$ phptenjin -z ex1.phtml
[NG] ex1.phtml
Parse error: syntax error, unexpected T_ECHO in ex1.phtml on line 5
Errors parsing ex1.phtml

Notice that the above message shows that error is on line 5 but in fact the error is on line 4.

'{== $_content =}' includes extra newline at end. Can I delete it?

Yes. You can use '<?php echo($_content); ?>' instead of '[== $_context =]'.

File 'ex2-layout.phtml':
<!-- -->
{== $_content =}
<!-- -->

<!-- -->
<?php echo($_content); ?>
<!-- -->
File 'ex2-content.phtml':
foo
bar
baz
Result:
$ phptenjin --layout=ex2-layout.phtml ex2-content.phtml
<!-- -->
foo
bar
baz

<!-- -->

<!-- -->
foo
bar
baz
<!-- -->

Can I change 'htmlspecialchars()' function name?

Yes. You can change them by setting escapefunc property for Tenjin_Template() or Tenjin_Engine().

File 'ex3.php':
<?php
require_once 'Tenjin.php';
$properties = array('escapefunc'=>'escapeHTML');
$engine = new Tenjin_Engine($properties);
$template = $engine->get_template('ex3.phtml');
echo $template->script;
?>
File 'ex3.phtml':
Hello {=$name=}!
Result:
$ php ex3.php
echo 'Hello ', escapeHTML($name), '!
';

Command-line option '--escapefunc=name' is equivarent to the above.

Result:
$ phptenjin -s --escapefunc=escapeHTML ex3.phtml
<?php echo 'Hello ', escapeHTML($name), '!
';
?>

Is it able to specify default value if a variable is not set?

Yes, by $_context['varname']. It is able to check whether value is set or not by inspecting special variable '$_context' which represents context values.

File 'ex4.phtml':
<?php $user = array_key_exists('user', $_context) ? $_context['user'] : 'Guest'; ?>
Hello {=$user=}!
Result:
$ phptenjin -c '$user="Tenjin";' ex4.phtml
Hello Tenjin!
$ phptenjin ex4.phtml
Hello Guest!

Template

Is it possible to specify variables passed to template?

Yes. You can specify template arguments by '<?php //@ARGS arg1, arg2, arg3 ?>'.

File 'ex5-layout.phtml'
<?xml version="1.0 ?>
<?php //@ARGS x, y ?>
<p>
  x = {==$x=}
  y = {==$y=}
  z = {==$z=}
</p>

Template arguments line is converted into local variable assignment statements.

Source code
$ phptenjin -s ex5.phtml
<?php  echo '<?xml version="1.0 ?>
'; $x = @$_context['x']; $y = @$_context['y'];
echo '<p>
  x = ', $x, '
  y = ', $y, '
  z = ', $z, '
</p>
';
?>

Undeclared arguments are not available even when they are passed via context object.

Result:
$ phptenjin -c '$x=10; $y=20; $z=30;' ex5.phtml
<?xml version="1.0 ?>
<p>
  x = 10
  y = 20
  z = 
Notice: Undefined variable: z in /usr/local/lib/php/Tenjin.php(497) : eval()'d code on line 6

</p>

Is it able to change embedded expression pattern?

Yes, you can create subclass of Template class and override embedded expression pattern.

ex6-expr-pattern.phtml:
<p>HTML escaped: {==$value=}</p>
<p>not escaped:  {=$value=}</p>
<p>not escaped:  <%= $value %></p>
ex6-expr-pattern.php:
<?php
require_once 'Tenjin.php';

class MyTemplate extends Tenjin_Template {

    /// return pattern string for embedded expressions
    function expr_pattern() {
      return '/\{=(=)?(.*?)=\}|<%=(.*?)%>/s';
    }
  
    // return expression string and flag whether escape or not from matched array
    function get_expr_and_escapeflag($match) {
        if (isset($match[3])) {
            $expr = trim($match[3][0]);
            $escapeflag = false;
        }
        else {
            $expr = $match[2][0];
            $escapeflag = @$match[1][0] == '=';
        }
        return array($expr, $escapeflag);
    }

}

## test program
$context = array('value' => 'AAA&BBB');
$properties = array('templateclass'=>'MyTemplate');
$engine = new Tenjin_Engine($properties);
$output = $engine->render('ex6-expr-pattern.phtml', $context);
echo $output;
?>
Result:
$ php ex6-expr-pattern.php
<p>HTML escaped: AAA&amp;BBB</p>
<p>not escaped:  AAA&BBB</p>
<p>not escaped:  AAA&BBB</p>

Does phpTenjin support M17N?

No, but it is easy to support M17N. The point is:

  • Change cache filename according to language. For example, create cache file 'file.phtml.en.cache', 'file.phtml.fr.cache', 'file.phtml.it.cache', and so on from template file 'file.phtml'.
  • Create Engine object for each language.
  • (optinal) Use preprocessing for performance reason.

The following is an example to generate M17N pages from a template file.

ex7-m18n.phtml:
<div>
<?PHP /// '_()' represents translator method ?>
 <p>{*= _('Hello') =*} {=$username=}!</p>
</div>
ex7-m18n.php:
<?php
require_once 'Tenjin.php';

##
## message catalog to translate message
##
$MESSAGE_CATALOG = array(
    'en' => array(
              'Hello'    => 'Hello',
              'Good bye' => 'Good bye',
            ),
    'fr' => array(
              'Hello'    => 'Bonjour',
              'Good bye' => 'Au revoir',
            ),
);

##
## language
##
$LANG = 'en';


##
## define translator function
##
function _($message_key) {
    global $MESSAGE_CATALOG, $LANG;
    $arr = $MESSAGE_CATALOG[$LANG];
    if (! $arr)
        return $message_key;
    return isset($arr[$message_key]) ? $arr[$message_key] : $message_key;
}


##
## engine class which supports M17N
##
class M17NEngine extends Tenjin_Engine {

    ## change cache filename to 'file.html.lang.cache'
    function cachename($filename) {
        global $LANG;
        return "$filename.$LANG.cache";
    }

}


##
## test program
##
$template_name = 'ex7-m18n.phtml';
$context = array('username' => 'World');

## english page
$properties = array('preprocess'=>true);
$engine = new M17NEngine($properties);
$output = $engine->render($template_name, $context);
echo "--- lang: $LANG ---\n";
echo $output, "\n";

## french page
$LANG = 'fr';
$engine = new M17NEngine($properties);
$output = $engine->render($template_name, $context);
echo "--- lang: $LANG ---\n";
echo $output;
?>
Result:
$ php ex7-m18n.php
--- lang: en ---
<div>
 <p>Hello World!</p>
</div>

--- lang: fr ---
<div>
 <p>Bonjour World!</p>
</div>

phpTenjin doesn't provide M17N feature directly because requirements for M17N are different for each applications or frameworks. Some applications or frameworks adapt GetText library and others use its original M17N library. What phpTenjin should do is not to provide M17N feature but to show an example to support M17N.

Layout Template

Can I change layout template name in a template file?

Yes. If you set $_context['_layout'], its value is regarded as layout template name.

  • You can specify template file name (ex. 'user_list.phtml') or template short name (ex. ':list').
  • If you set TRUE to '$_context['_layout']', default layout template name is used instead.
  • It is able to N-th level nested template.

See the next section for details.

Can I nest layout templates for any depth?

Yes. If you set $_context['_layout'], you can nest layout templates in any depth.

The following example shows that:

  • 'ex7-content.phtml' uses 'ex7-mylayout.phtml' as layout template.
  • 'ex7-mylayout.phtml' uses 'ex7-baselayout.phtml' as layout template.
File 'ex8-content.phtml':
<?php $_context['title'] = 'Changing Layout Template Test'; ?>
<?php /// specify layout template name ?>
<?php $_context['_layout'] = 'ex8-mylayout.phtml'; ?>
foo
bar
baz
File 'ex8-mylayout.phtml':
<?php /// use default layout template name ?>
<?php $_context['_layout'] = true; ?>
<div id="content">
<?php echo $_content; ?>
</div>
File 'ex8-baselayout.phtml':
<html>
  <body>
<?php $title = @$_context['title']; ?>
<?php if ($title) { ?>
    <h1>{=$title=}</h1>
<?php } ?>
<?php echo $_content; ?>
  </body>
</html>
Result:
$ phptenjin --layout=ex8-baselayout.phtml ex8-content.phtml
<html>
  <body>
    <h1>Changing Layout Template Test</h1>
<div id="content">
foo
bar
baz
</div>
  </body>
</html>

Can I disable default layout template for a certain template?

Yes. If you set FALSE to $_context['_layout'], default layout template will not be applied.

Is Django-like "Template Inheritance" supported?

No, but you can emulate it partially by combination of template capturing and '$_context['_layout']'.

File 'ex9-baselayout.phtml':
<html>
 <body>

<?php /// if a string captured as 'header_part' exists then print it, ?>
<?php /// else print default header part. ?>
  <div id="header">
<?php start_placeholder('header_part'); ?>
   <img src="img/logo.png" alt="logo" ?>
<?php stop_placeholder(); ?>
  </div>

<?php /// main content part ?>
  <div id="content">
<?php echo $_context['content_part']; ?>
  </div>

<?php /// if a string captured as 'footer_part' exists then print it, ?>
<?php /// else print default footer part. ?>
  <div id="footer">
<?php start_placeholder('footer_part'); ?>
   <hr />
   <em>webmaster@example.com</em>
<?php stop_placeholder(); ?>
  </div>
  
 </body>
</html>
File 'ex9-customlayout.phtml':
<?php /// '$_context["_layout"] = "foobar.html"' is equivarent to
<?php /// '{% extends "foobar.html" %}' in Django template engine. ?>
<?php $_context['_layout'] = 'ex9-baselayout.phtml'; ?>

<?php /// you can override header or footer by capturing. ?>
<?php start_capture('footer_part'); ?>
<address style="text-align:right">
  copyright&copy; 2007 kuwata-lab all rights reserved<br />
  <a href="webmaster&#64;kuwata-lab.com">webmaster&#64;kuwata-lab.com</a>
</address>
<?php stop_capture(); ?>
File 'ex9-content.phtml':
<?php /// '$_context["_layout"] = "foobar.html"' is equivarent to
<?php /// '{% extends "foobar.html" %}' in Django template engine. ?>
<?php $_context['_layout'] = 'ex9-customlayout.phtml'; ?>

<?php /// main content part ?>
<?php start_capture('content_part'); ?>
<ul>
<?php foreach ($items as $item) { ?>
  <li>{=$item=}</li>
<?php } ?>
</ul>
<?php stop_capture(); ?>

'start_placeholder()' and 'stop_placeholder()' are pre-defined helper macros. For example,

<?php start_placeholder('header_part'); ?>
   <img src="img/logo.png" alt="logo" ?>
<?php stop_placeholder(); ?>

is equivarent to the following.

<?php if (array_key_exists('header_part', $_context)) { ?>
<?php     echo $_context['header_part']; ?>
<?php } else { ?>
   <img src="img/logo.png" alt="logo" ?>
<?php } ?>

The following is the result. It shows that footer part in baselayout is overrided by other templates.

Result:
$ phptenjin -c '$items=array("AAA","BBB","CCC");' ex9-content.phtml
<html>
 <body>

  <div id="header">
   <img src="img/logo.png" alt="logo" ?>
  </div>

  <div id="content">
<ul>
  <li>AAA</li>
  <li>BBB</li>
  <li>CCC</li>
</ul>
  </div>

  <div id="footer">
<address style="text-align:right">
  copyright&copy; 2007 kuwata-lab all rights reserved<br />
  <a href="webmaster&#64;kuwata-lab.com">webmaster&#64;kuwata-lab.com</a>
</address>
  </div>
  
 </body>
</html>

Performance

How fast is phpTenjin compared with other solutions?

phpTenjin contains benchmark script. This shows that phpTenjin works much faster than other solutions.

$ 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

In addition, module size of phpTenjin is small, and it is very light-weight to import it. This is important for CGI program. Other solutions may be very heavy to import the module and suitable only for apache module or FastCGI.

Why phpTenjin is so fast?

Because it doesn't use template engine original language.

Other template engines, such as Template-Toolkit(perl), Django(python), or Smarty(php), has their original languages. This is not good idea for script language because:

  • They are slow.
  • Implementation will be complex.
  • Learning cost is high.

In addition, phpTenjin is faster than Jakarta Velocity which is a very popular template engine in Java. (It means that dynamic Java is slower than script languages.)

Template engine should use their host language directly unless there are some kind of reasons.