YATS (Yet another templating system)
[email protected] (Dan Libby)
| Newsgroups | php.template |
|---|---|
| Message-ID | <[email protected]> |
Okay, so a few weeks ago I was tasked with writing a templating system
for use with PHP capable of separating content from display. We had
looked at FastTemplate, but found it much too slow for production use.
I agreed, on the condition that I could open source it when done.
I spent a few days and hacked together a PHP module. So far, we haven't
really used the system beyond my limited testing, and it no doubt
contains many bugs. In particular, I added cacheing of templates across
multiple requests (not a big win, unfortunately) which requires using
real malloc and was somewhat painful to do with Zend's hashtable and
macros, which use emalloc by default. So I expect problems there. But
I digress....
The design is also derived from fasttemplate, but deviates more
significantly, I think. In particular, parsing occurs only once, when
the template is defined/loaded. Thus, there is no need for
parse_dynamic(). The template syntax is also different. It allows
nested dynamic sections. It does not allow conditionals, but has
support for an "alt" parameter if the requested variable is not found.
Other parameters may be defined as needed due to the {{var key1="val"
key2="val"}} syntax. There is support for implicit looping, and
(optional) automatic hiding of sections with undefined variables.
Anyway, I was wondering what to do with this code. I could place it on
sourceforge, or try to get it into the PHP distro, or sit on it. I was
happy to discover this list and andrei's proposal sounds similar in many
regards to what I had come up with. I have some documentation, which
I am including with this mail (readme.HTML). I would like to hear your
feedback on my design, and would also appreciate any thoughts on how
best to distribute the code.
regards,
-dan
README.html
(text/html, 8 KB)
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.51 [en] (X11; I; Linux 2.2.5-15 i686) [Netscape]">
</head>
<body>
<h1>
Faster Than FastTemplate (FTF)</h1>
<h1>
Overview:</h1>
This is a templating system that was inspired by FastTemplate, the primary
goal of which is to separate HTML content from PHP code. FastTemplate
is widely used because it makes maintenance of large sites easier.
Programmers can work on the logic while designers work on the templates
separately. FTF seeks to preserve these features while improving on
usability and speed. It is not a direct port of <a href="http://www.phpbuilder.com/columns/sascha19990316.php3">FastTemplate</a>,
as it adds/removes some features, changes the syntax, and simplifies a
few things, such as template definition. The PHP version of FastTemplate
is implemented as a native PHP class and uses regular expressions for parsing.
This means two things: the templating code must be interpreted for each
request, and the template must be re-parsed for each request (or sometimes
even more often). By comparison, FTF is written in C as a PHP module,
so it is always available. Further, it parses the template only once
into a set of stored data structures and uses these to quickly substitute
new variables on subsequent calls to retrieve the template contents. The file
is not re-parsed unless it changes or a new server process begins, no matter
how many requests occur.
<p>[todo: insert speed comparisons here]
<br>
<h1>
Usage:</h1>
In general, usage of FTF will follow this process:
<ul>
<li>
PHP Code.</li>
<ul>
<li>
define template</li>
<li>
assign variables</li>
<li>
get template buffer (interpolate)</li>
<li>
print buffer</li>
</ul>
<li>
Template</li>
<ul>
<li>
define HTML structure</li>
<li>
add variables</li>
<li>
add sections</li>
</ul>
</ul>
<h1>Example</h1>
This example shows how Database results can be inserted into a template. Assume that
the DB returns 3 entries. The code is then responsible for assigning them into
variables for display via the template.
<h3>examp.php</h3>
<XMP>
<?php
// Define and parse template.
$tmpl = tmpl_define("examp.tmpl");
// Omitted: Query DB or otherwise obtains some results.
// Assign # of results (scalar)
tmpl_assign($tmpl, "num_found", "3");
// Assign the results. An array of first names and an array of last names.
tmpl_assign($tmpl, "fname", array("Dan", "John", "Homer"));
tmpl_assign($tmpl, "lname", array("Libby", "Doe", "Simpson"));
// Assign a page background color and font.
tmpl_assign($tmpl, array(page_bgcolor => "red",
default_font => "arial",
default_size => "+2") );
// Retrieve buffer with variables inserted, and print.
print tmpl_getbuf($tmpl);
?>
</XMP>
<hr>
<h3>examp.tmpl</h3>
<XMP>
<HTML>
<body bgcolor="{{page_bgcolor}}">
<font face="{{default_font}}" size="{{default_size}}">
found {{num_found}} results.
<TABLE>
<TR><TD>First Name</TD><TD>Last Name</TD></TR>
<!-- Begin list of names -->
{{section:names}}
<TR><TD>{{fname}}</TD><TD>{{lname}}</TD></TR>
{{/section:names}}
<!-- End list of names -->
</TABLE>
</font>
</body>
</HTML>
</XMP>
<hr>
<h3>results</h3>
<TABLE width="80%" align='center'>
<TR><TD><I>HTML Code</I></TD><TD width="10%"> </TD><TD><I>Display</I></TD></TR>
<TR><TD>
<XMP>
<HTML>
<body bgcolor="red">
<font face="arial" size="+2">
found 3 results.
<TABLE>
<TR><TD>First Name</TD><TD>Last Name</TD></TR>
<!-- Begin list of names -->
<TR><TD>Dan</TD><TD>Libby</TD></TR>
<TR><TD>John</TD><TD>Doe</TD></TR>
<TR><TD>Homer</TD><TD>Simpson</TD></TR>
<!-- End list of names -->
</TABLE>
</font>
</body>
</HTML>
</XMP>
</TD>
<TD></TD>
<TD bgcolor="red" valign="top">
<font face="arial" size="+2">
found 3 results.
<TABLE>
<TR><TD>First Name</TD><TD>Last Name</TD></TR>
<!-- Begin list of names -->
<TR><TD>Dan</TD><TD>Libby</TD></TR>
<TR><TD>John</TD><TD>Doe</TD></TR>
<TR><TD>Homer</TD><TD>Simpson</TD></TR>
<!-- End list of names -->
</TABLE>
</font>
</TD>
</TR>
</TABLE>
<h1>
Template Features:</h1>
<h2>
Variables</h2>
<h3>
Syntax:</h3>
{{variable_name}} or
<br>{{variable_name key1="val1" key2="val2"}}
<h3>
Description:</h3>
The variable will be replaced by the corresponding value associated with
this template. This variable is assigned using the PHP tmpl API.
Variable names and parameters are case sensitive.
<h3>
Recognized Arguments:</h3>
<I>alt</I>="<I>val</I>". Specified that if variable_name is not found, display val instead.
<P>
<I>repeatscalar</I>="<I>yes</I>" If this variable contains only a single value, then display it
once for each loop. See implicit looping.
<h2>
Sections</h2>
<h3>
Syntax:</h3>
{{section:section_name}} ... some stuff ... {{/section:section_name}}
or
<br>{{section:section_name key1="val1" key2="val2"}} ... some stuff
... {{/section:section_name}} or
<h3>
Description:</h3>
Section marks the begin and end of a region of text and assigns it a name.
The section may be selectively displayed or hidden (via PHP). Further,
one or more variables may be embedded within a section. If any of
these variables contain multiple values, the entire section will be repeated.
This is called implicit looping. Variable names and parameters are
case sensitive.
<h3>
Nesting:</h3>
Sections may be embedded one within the other. This is called nesting.
If a child section contains a loop, the child section is repeated within
the parent section, but the parent section itself does not repeat.
[note: this should be optional, along with collation rules]
<h3>
Automatic Hiding</h3>
If any variable is not found within a section, the entire section will
be hidden. This can be useful when there is surrounding text (such
as a table row) that should only be displayed if the variable is found.
Child sections are also hidden.
<h3>
Implicit Looping</h3>
[note: subject to change.] The simplest case is a section with one
variable that contains a list of two values. This section will be
repeated twice, with value1 inserted the first time and value2 inserted
the second time. The situation becomes more complicated when the
section contains multiple variables because of the possibility of a mis-matched
number of values per variable. For example, var1 may contain 2 values
and var2 contains 3 values. In this case, the section loops twice,
and the extra value in var3 is simply discarded. It is the responsibility
of the PHP code to correctly align these value lists.
<p>An exception is made for any variable that contains only one value.
This variable is said to be 'scalar' and the value is repeated for each
iteration of the loop. [note: behavior should be made optional]
<h3>
Recognized Arguments</h3>
<I>autohide</I>="<I>yes</I>" hide this section if any of the variables contained within are
undefined.
<br>
<h1>
PHP API:</h1>
<h3>
template_object tmpl_define($filename);</h3>
Reads/parses filename and returns an object representing it, or false if
error.
<h3>
bool tmpl_assign($tmpl, $key, $val);</h3>
Assign key, val pair to tmpl. Returns true/false.
<h3>
bool tmpl_assign($tmpl, $key, $index_array);</h3>
Assign an indexed array of values to key. These will be looped over
when displayed.
<h3>
bool tmpl_assign($tmpl, $assoc_array);</h3>
Assign multiple keys and values at once. Each key may reference a value
that is either scalar or an indexed array.
<h3>
string tmpl_getbuf($tmpl);</h3>
Returns buffer with all appropriate substitutions performed.
<h3>
assoc_array tmpl_getvars($tmpl);</h3>
Returns an associative array containing all currently defined key/value
pairs for a template.
<h3>
bool tmpl_hide($tmpl, $name, $bool);</h3>
Selectively disable/endable display of section with id $name. A value
of 1 hides the section, a value of 0
<br>allows it to be displayed. Returns true if section exists, false
otherwise.
<br>
<pre>
</pre>
</body>
</html>