Copyright © 2004 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
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.
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.
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.
PHP 4 or 5
XML_HTMLSax 2.1.2, or later.
or XML_Parser (optional)
the GNU Lesser General Public License version 2.1 , or any later version.
This section provides you a quick start guide to use ZOGAN version 0.2 or later.
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>
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>
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.
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>
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>
Order of attribute for data against var:cont attribute is important. For more information, see here.
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><b>ZOGAN</b> is a template engine for <i>XML</i></p> <p><b>ZOGAN</b> is a template engine for <i>PHP</i></p> </body>
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.
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.
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.
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.
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>
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>
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.
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>
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.
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.
ZOGAN provide functions below.
Table 1. ZOGAN Built-in functions
name | desc. |
---|---|
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>
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);
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>
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>
XML comments are displayed without change normaly. To remove some comments, start comment with #.
<!-- this comment will keep. --> <!-- # this comment will be removed. -->
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.
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>
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.
API document with phpDocumentor is available at http://phpoot.sourceforge.jp/zogan/api/.
And you may get ZOGAN sample release from there.