ZOGAN: PHP template engine for XML

Haruki Setoyama

This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at http://www.opencontent.org/openpub/ ).

A part of the samples in this guide is based on the guide of Amrita template library .

$Id: zogan.manual.html,v 1.2 2004/10/19 03:55:40 haruki Exp $


Table of Contents

1. Introduction
1.1. What is ZOGAN?
1.2. Presentation?
1.3. Features
1.4. Requirements
1.5. License
1.6. Download
1.7. Install
2. Quick start guide
2.1. Hello World
2.2. List
2.3. Table
2.4. Modify attributes
3. Convert of special characters to XML entities
4. Getting displayed text into PHP variable
5. Direct declaration of hash
6. Object as data
6.1. Substitute of hash
6.2. Data itself
7. Syntax for conditional
7.1. Content of element
7.2. Attribute
8. Omission of tag or element
8.1. 'omit-tag' attribute
8.2. 'omit' attribute
9. Display alternately
10. Processing data in template with function
10.1. Built-in functions
10.2. User defined functions
11. Number label
12. Inclusion of other templates
13. For debugging
13.1. Comment
13.2. Debug information
14. Namespace URI for ZOGAN
15. Permitted characters
16. API and Other examples

1. Introduction

1.1. What is ZOGAN?

ZOGAN is a template engine for XML (such as XHTML) with PHP, that embeds data in PHP variable into a template in the form of XML.

Template engine makes it possible to separate XML presentation from programming code, and presentation structure from presentation logic. The separation makes your code easy to understand and to edit.

I have got the concept of this template engine thanks to Amrita template library that is for Ruby.

1.2. Presentation?

Presentation is the part where an application presents information for its user. Here I classify presentation into three parts, which are “structure”, “design” and “logic”. For example, XHTML has charge of presentation structure, CSS does of design, and PHP itself should take charge of logic. So ZOGAN template engine does not have complicated control stricture like 'if ... then ...'.

Separation of presentation from main processing makes it possible and easy for designers and programmer to work on their own file separately. But the three parts depends on each other to some extent, so plenty of communications between them are necessary.

1.3. Features

  • Templates for ZOGAN are and must be well-formed XML documents.

  • ZOGAN uses tag attributes with a namespace ("var" as default) to specify instructions for template.

  • So, the templates are expected to be written using normal XML or XHTML editor.

  • Model data to display with the templates is a nested hash.

  • The templates will be compiled into PHP code before display for speed.

1.4. Requirements

1.5. License

the GNU Lesser General Public License version 2.1 , or any later version.

1.6. Download

ZOAGN belongs to a sourceforge.jp project called PHPOOT . Download latest ZOGAN from there.

1.7. Install

Use PEAR installer. For example,

pear install zogan-0.2.tgz
    

2. Quick start guide

This section provides you a quick start guide to use ZOGAN version 0.2 or later.

2.1. Hello World

This is a simple template file, which is written with XHTML.

<html>
  <body>
    <h1 var:cont="{title}">title will be inserted here</h1>
    <p var:cont="{body}">body text will be inserted here</p>
  </body>
</html>
   

ZOGAN treats elements with "var:cont" attribute as dynamic parts where data will be embedded. ZOGAN gets the data for template from hash variable with the value of "var:cont" attribute as key. The key in "var:cont" attribute must be enclosed with braces like {foo}. For example,

<h1 var:cont="{title}">...</h1>

will be replaced with

<h1>$model_data['title']</h1>

.

The PHP code to display the template with a hash model data is below. Name the template “helloworld.html” and put it in the same directory with the PHP code, and change the mode of the directory to writable by PHP interpreter, because ZOGAN complies templates to PHP code before display when the templates are new or modified.

require_once 'XML/Template/zogan.php'; // change this to properly path.
$tmpl = new zogan;
$tmpl->setTemplateFile('./helloworld.html');
$tmpl->setCompileDir('.');
$model_data = array(
  'title' => 'Hello World',
  'body'  => 'ZOGAN is a PHP template engine for XML'
);
$tmpl->display($model_data);
   

The output of this code is ...

<html>
  <body>
    <h1>Hello World</h1>
    <p>ZOGAN is a PHP template engine for XML</p>
  </body>
</html>
   

Note

'<', '>', '&', ''' and '"' in data will be escaped to XML entity as default. For more information, see there.

For the permitted characters in key of data, see there.

2.2. List

To repeat one element with multiple data, use array as data to display. The sample is a template for XHTML list, which is named list.html.

<ul>
  <li var:cont="{list1}"></li> // repeat is required in this line. 
</ul>
   

And PHP code for it is

 
require_once 'XML/Template/zogan.php';
$tmpl = new zogan;
$tmpl->setTemplateFile('./list.html');
$tmpl->setCompileDir('.');
$model_data = array(
  'list1' => array(1, 2, 3)
);
$tmpl->display($model_data);
   

When the model data to display, in this case that is 'list1', is array, ZOGAN displays them one by one. So the output will be

<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>
    

Note

PHP do not distinguish array and hash, but normaly array(0 => 'foo', 1 => 'bar') is called array and array('a' => 'foo', 'b' => 'bar') is called hash. So ZOGAN look on PHP array with integer key 0 as array.

2.3. Table

XHTML table can be displayed with nested hash as data. The template is simple like this.

<table border="1">
  <tr><th>name</th><th>author</th></tr>
  <tr var:cont="{table1}">
    <td var:cont="{name}"></td><td var:cont="{author}"></td>
  </tr>
</table>
   

It is possible to nest elements that have "var:cont" attribute.

And the PHP code is

require_once 'XML/Template/zogan.php';
$tmpl = new zogan;
$tmpl->setTemplateFile('./table.html');
$tmpl->setCompileDir('.');
$model_data = array(
  'table1' => array(
    array( 'name' => 'PHP',    'author' => 'Rasmus Lerdorf' ),
    array( 'name' => 'Ruby',   'author' => 'matz' ),
    array( 'name' => 'python', 'author' => 'Guido van Rossum' )
  )
);
$tmpl->display($model_data);
   

The output will be

<table border="1">
  <tr><th>name</th><th>author</th></tr>
  <tr>
    <td>PHP</td><td>Rasmus Lerdorf</td>
  </tr>
  <tr>
    <td>Ruby</td><td>matz</td>
  </tr>
  <tr>
    <td>python</td><td>Guido van Rossum</td>
  </tr>
</table>
   

2.4. Modify attributes

Use {data} style value to embed data in attributes of XML start-tags.

Template:

<table border="1">
  <tr><th>name</th><th>webpage</th></tr>
  <tr var:cont="{table}">
    <td var:cont="{name}"></td>
    <td><a href="{url}" var:cont="{webpage}"></a></td>
  </tr>
</table>
   

Code:

require_once 'XML/Template/zogan.php';
$tmpl = new zogan;
$tmpl->setTemplateFile('./attribute.html');
$tmpl->setCompileDir('.');
$model_data = array(
  'table' => array(
    array( 
        'name'    => 'PHP',
        'url'     => 'http://www.php.net/',
        'webpage' => 'PHP: Hypertext Preprocessor'
    ),
    array( 
        'name'    => 'Ruby',
        'url'     => 'http://www.ruby-lang.org/',
        'webpage' => 'Ruby Home Page'
    ),
    array( 
        'name'    => 'python',
        'url'     => 'http://www.python.org/',
        'webpage' => 'Python Language Website'
    )
  )
);
$tmpl->display($model_data);
   

And output:

<table border="1">
  <tr><th>name</th><th>webpage</th></tr>
  <tr>
    <td>PHP</td>
    <td><a href="http://www.php.net/">PHP: Hypertext Preprocessor</a></td>
  </tr>
  <tr>
    <td>Ruby</td>
    <td><a href="http://www.ruby-lang.org/">Ruby Home Page</a></td>
  </tr>
  <tr>
    <td>python</td>
    <td><a href="http://www.python.org/">Python Language Website</a></td>
  </tr>
</table>
   

Note

Order of attribute for data against var:cont attribute is important. For more information, see here.

3. Convert of special characters to XML entities

By default, special characters such as '<', '>', '&', ''' and '"' in model data is converted to XML entities when display. To stop the convert in any part of data, set names of key you want to stop the convert with setNoEscapeVars() method. For example, PHP code is:

$body_html = '<b>ZOGAN</b> is a template engine for <i>XML</i>';
$model_data = array(
  'title' => 'Hello World',
  'body'  => $body_html,
  'body_html'  => $body_html
);
$tmpl->setNoEscapeVars(array('body_html'));
$tmpl->display($model_data);
   

And the template is:

<body>
  <h1 var:cont="{title}">title will be inserted here</h1>
  <p var:cont="{body}">body text will be inserted here</p>
  <p var:cont="{body_html}">body html will be inserted here</p>
</body>
   

So, the output will be:

  <body>
    <h1>Hello World</h1>
    <p>&lt;b&gt;ZOGAN&lt;/b&gt; is a template engine for &lt;i&gt;XML&lt;/i&gt;</p>
    <p><b>ZOGAN</b> is a template engine for <i>PHP</i></p>
  </body>
   

4. Getting displayed text into PHP variable

To get displayed text into PHP variable, use expand() method or fetch() method.

$output = '';
$tmpl->expand($output, $model_data);
    

or

$output = $tmpl->fetch($model_data);
    

The method fetch() returns only XML text embedded with data, but does not an error object.

5. Direct declaration of hash

There is another way to access directly hash data with syntax 'key1/key2'. The nest level is not limited.

<p var:cont="{body/text}"></p>
    

Conversion of XML entities for the hash of above syntax is decided with the name of last key.

6. Object as data

6.1. Substitute of hash

You can use object as substitute of hash, see following code.

  class data {
  	var $title = 'Hello World with Object Property';
  	var $body  = 'ZOGAN is a PHP template engine for XML';
  }
  require_once 'XML/Template/zogan.php';
  $tmpl = new zogan;
  $tmpl->setTemplateFile('./helloworld.html');
  $tmpl->setCompileDir('.');
  $model_data = new data;
  $tmpl->display($model_data);   
      

A property of object is used as if it were a value of hash. Accurately, if there is method of the same name as key of data, the method would be called. Without the method, property of the same name would be used.

6.2. Data itself

By specifying the method to get data from object with addClassForData() method, you can handle objects as data itself. Precisely, if objects of the specified class is given for data, the method is used to get data for template. The following sample is for the template of Hallow World section. The difference is to use "title" object as data.

class title {
	function getTitle() {
		return 'Hello World';
	}
}
require_once 'XML/Template/zogan.php';
$tmpl = new zogan;
$tmpl->setTemplateFile('./helloworld.html');
$tmpl->setCompileDir('.');
$model_data = array(
  'title' => new title,
  'body'  => 'ZOGAN is a PHP template engine for XML'
);
$tmpl->addClassForData('title', 'getTitle');
$tmpl->display($model_data);
      

Class "Zogan" and its method "fetch" is specified by default, so you can use Zogan object as data like this.

require_once 'XML/Template/zogan.php';
$tmpl_table = new zogan;
$model_data = array(
  'table1' => array(
    array( 'name' => 'PHP',    'author' => 'Rasmus Lerdorf' ),
    array( 'name' => 'Ruby',   'author' => 'matz' ),
    array( 'name' => 'python', 'author' => 'Guido van Rossum' )
  )
);
$tmpl_table->setTemplateFile('./table.html');
$tmpl_table->setCompileDir('.');
$tmpl_table->setData($model_data);

$tmpl = new zogan;
$tmpl->setTemplateFile('./helloworld.html');
$tmpl->setCompileDir('.');
$model_data = array(
  'title' => 'Including other ZOGAN template Object',
  'table' => $tmpl_table
);
$tmpl->display($model_data);
      

Use setData() method to give date for the template.

7. Syntax for conditional

7.1. Content of element

If a part of model data for content of element is null or false, the XML element for the part will be deleted. Using this, you can select the part of template to be displayed.

If true was given to a part of model data, the content of the tag will be displayed without modified.

template

<html>
  <body>
    <div var:cont="{groups}">
      <h1 var:cont="{title}"></h1>
      <div var:cont="{no_data}">
        <em>This group has no data.</em>
      </div>
      <div var:cont="{one_data}">
        This group has only one data: 
        "<span var:cont="{data}" var:omit-tag="true"></span>".
      </div>
      <div var:cont="{many_data}">
        Here's the list of this group's data.
        <ul>
          <li var:cont="{list}"></li>
        </ul>
      </div>
    </div>
  </body>
</html>
   

code

require_once 'XML/Template/zogan.php';
$tmpl = new zogan;
$tmpl->setTemplateFile('./conditional.html');
$tmpl->setCompileDir('.');
$data = array(
  array('Group A', array('only_one')),
  array('Group B', array('one', 'two', 'three')),
  array('Group C', array())
);
$model_data = array();
foreach ($data as $datum) {
  $model_datum = array();
  $model_datum['title'] = $datum[0]; 
  switch(count($datum[1])){
    case 0: 
        $model_datum['no_data'] = true;
        break;
    case 1: 
        $model_datum['one_data'] = array(
          'data' => $datum[1][0]
        );
        break;
    default:
        $model_datum['many_data'] = array(
          'list' => $datum[1]
        );
  }
  $model_data[] = $model_datum;
}
$tmpl->display(array('groups' => $model_data));
   

output

<html>
  <body>
    <div>
      <h1>Group A</h1>
      <div>
        This group has only one data: 
        "only_one".
      </div>
    </div>
    <div>
      <h1>Group B</h1>
      <div>
        Here's the list of this group's data.
        <ul>
          <li>one</li>
          <li>two</li>
          <li>three</li>
        </ul>
      </div>
    </div>
    <div>
      <h1>Group C</h1>
      <div>
        <em>This group has no data.</em>
      </div>
    </div>
  </body>
</html>
   

7.2. Attribute

If a part of model data for attribute is null or false, the attribute for the part will be deleted. If true was given, for example, the attribute "foo" will be foo="foo". Sample of this feature is shown below.

<select name="lang">
    <option var:cont="{lang}" value="{iso}" selected="{selected}">
        <span var:cont="{name}">language</span>
    </option>
</select>
   
$tmpl = new zogan;
// snip
$model_data = array(
    'lang' => array(
        array(
            'name' => 'english',
            'iso'  => 'en'
        ),
        array(
            'name' => 'japanese',
            'iso'  => 'ja',
            'selected' => true
        )
    )
);
$tmpl->display($model_data);
   
<select name="lang">
    <option value="en">
        <span>english</span>
    </option>
    <option value="ja" selected="selected">
        <span>japanese</span>
    </option>
</select>
   

When you want to make the attribute "foo" foo="other_default_value" if true was given to the data, Use attr attribute like below.

<a href="http://planewave.org" var:attr="href={url}">there</a>
   

When variable 'url' is true, the output will be

<a href="http://planewave.org">there</a>
   

When variable 'url' is 'http://www.php.net', the output will be

<a href="http://www.php.net">there</a>
   

8. Omission of tag or element

8.1. 'omit-tag' attribute

To omit both start-tag and end-tag, use omit-tag attribute with “true” value like this.

<span var:omit-tag="true">Tag will be omitted!</span>
   

the output will be:

Tag will be omitted!
   

Off cause, You can use this attribute with other attribute in ZOGAN name space.

8.2. 'omit' attribute

To omit whole element, use omit attribute with “true” value. e.g.

<p var:omit="true">This text will be omitted</p>
   

No output will be given.

To omit whole element when first time in iteration, use omit attribute with “first” value.

<span var:cont="{list1}">
  <span var:omit="first"> | </span>
  <span var:cont="{item}"></span>
</span>
   

9. Display alternately

When the table or list is shown, striped coloring is frequently required for easy reading. To realize this, the template engine has to display two or more elements alternately. To do it, use alt attribute.

<table border="1">
  <tr var:cont="{table1}" var:alt="true" class="blue">
    <td var:cont="{name}"></td>
    <td var:cont="{author}"></td>
  </tr>
  <tr var:cont="{table1}" var:alt="true" class="red">
    <td var:cont="{name}"></td>
    <td var:cont="{author}"></td>
  </tr>
</table>
   

$tmpl = new zogan;
// snip
$model_data = array(
  'table1' => array(
    array( 'name' => 'PHP',    'author' => 'Rasmus Lerdorf' ),
    array( 'name' => 'Ruby',   'author' => 'matz' ),
    array( 'name' => 'python', 'author' => 'Guido van Rossum' )
  )
);
$tmpl->display($model_data);
   

<table border="1">
  <tr class="blue">
    <td>PHP</td>
    <td>Rasmus Lerdorf</td>
  </tr>
  <tr class="red">
    <td>Ruby</td>
    <td>matz</td>
  </tr>
  <tr class="blue">
    <td>python</td>
    <td>Guido van Rossum</td>
  </tr>
</table>
   

Successive element with the same tag name and the same value in cont attribute is possible to be displayed alternately. So

<p var:cont="{name}" var:alt="true"></p>
<span var:cont="{name}" var:alt="true"></span>
   

is not shown alternately.

10. Processing data in template with function

It is possible to process data in template when it is displayed. Syntax of the template function is "function(parameter):keyOfData". Part of "(parameter)" can be omitted.

10.1. Built-in functions

ZOGAN provide functions below.

Table 1. ZOGAN Built-in functions

namedesc.
date date() function. The parameter is format string.
sprintf sprintf() function. The parameter is format string.
not provides `NOT` logical operator. No parameter.
bool casts data to boolean value. No parameter.

Now only date and printf have been implemented. The format string is the same of date() and printf() function.

<body>
  <h1>date</h1>
  <p var:cont="{date(F j, Y, g:i a):date}"></p>
  <h1>sprintf</h1>
  <p var:cont="{sprintf(I love %s):printf}"></p>
</body>
   

10.2. User defined functions

For user defined functions, use addFunction() method.

<table border="1">
  <tr>
    <th>number</th><th>category</th>
  </tr>
  <tr var:cont="{numbers}">
    <td var:cont="{number}"></td><td var:cont="{category:number}"></td>
  </tr>
</table>
   

function getCategory($num, $param, $option)
{
    if ($num < 100) {
        return 'Small';
    }
    elseif ($num < 200) {
        return 'Medium';
    }
    else  {
        return 'Large';
    }
}

$tmpl = new zogan;
// snip
$tmpl->addFunction('category', 'getCategory');
$nums = array();
for ($i =0; $i < 10 ;$i++ ) {
    $nums[] = array('number' => mt_rand(0,300));
}
$nums = array('numbers' => $nums);
$tmpl->display($nums);
   

11. Number label

If you want to show iterative data with label, use "{#num}" at cont attribute.

<p var:cont="{table1}">
  <span var:cont="{#num}"></span> - 
  <span var:cont="{name}"></span> -
  <span var:cont="{author}"></span>
</p>
   

12. Inclusion of other templates

ZOGAN can include other templates in the same directory from a template. Use "{#inc included_template}" syntax at cont attribute. The below is a sample.

<body>
  <h1 var:cont="{title}">title will be inserted here</h1>
  <!-- start inclusion -->
<div var:cont="{#inc table.html}" var:omit-tag="true"></div>
  <!-- finish inclusion -->
</body>
   

13. For debugging

13.1. Comment

XML comments are displayed without change normaly. To remove some comments, start comment with #.

<!-- this comment will keep. --> 
<!-- # this comment will be removed. -->
     

13.2. Debug information

Easy way to show debug information is to use

setErrorHandling(PEAR_ERROR_PRINT)

method.

And

setOption(ZOGAN_OPTION_DEBUG, true);

method is also easy way. In this case, the template will be compiled every display.

To compile templates every time to display, use

setOption(ZOGAN_OPTION_FORCE_COMPILE, true)

method.

14. Namespace URI for ZOGAN

You can change the prefix of attribute for template information. Namespace URI for ZOGAN is "http://zogan.planewave.org/", so use xmlns:* attribute with the URI to change the prefix.

<html xmlns:foo="http://zogan.planewave.org/">
  <body>
    <h1 foo:cont="{title}">title will be inserted here</h1>
    <p foo:cont="{body}">body text will be inserted here</p>
  </body>
</html>
   

15. Permitted characters

Permitted characters for keys of data expressed as a regular expression are

'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'
   

This rule is the same as PHP variables.

16. API and Other examples

API document with phpDocumentor is available at http://phpoot.sourceforge.jp/zogan/api/.

And you may get ZOGAN sample release from there.