PHP Pagemill

Introduction

PHP Pagemill is a barebones template engine for PHP scripts. It provides a simple way to separate logic and design, as is typically done in applications that use the MVC design pattern. Although it is not a complete framework like Smarty or Symfony, it is designed to be configurable and extendable. Developers can use it as a foundation for custom frameworks or for any web application in which a separation between HTML and application code needs to be enforced.

Back to Top

Features

  • Lightweight. The Pagemill class can be used in any PHP script by including a single file.
  • Highly configurable. Developers can change practically every aspect of the core syntax for templates. The Pagemill also includes a method for extending the templating engine with custom tags.

Requirements

PHP Pagemill requires PHP version 4.3.0 or higher.

Back to Top

A Simple Example

Here is a simple example of the Pagemill in action.

demo.php

<?
require_once('pagemill.class.php');
$pm = new Pagemill();
$pm->setVariable('appname', 'Hello World');
$pm->setVariable('hostname', $_SERVER['HTTP_HOST']);
$pm->writeFile('demo.tmpl');
?>

demo.tmpl

<html>
<head>
<title>PHP Pagemill Demo</title>
</head>
<body>
<p>
The name of this demo is @{appname}@.
</p>
<p>
It is hosted on @{hostname}@.
</p>
</body>
</html>
Back to Top

Variables

The most important function of the Pagemill is variable transformations. The application code sets the values of variables and inserts the values into the template. Variable names in the template are surrounded by @{ and }@, e.g., @{name}@.

Note that the Pagemill does not parse variables within other variables. For example, if the value of the variable "foo" is "Show me @{bar}@", when the Pagemill parses a template that references , the literal string "@{bar}@" will appear in the rendered page instead of the value of the "bar" variable. This feature is included to provide some basic security against code injection.

Back to Top

Includes

The Pagemill provides a method of including files that is similar to the SSI #include directive.

Back to Top

Loops

Web applications often need to render data that is tabular or hierarchical in nature, such as lists of names in an address book or items in an invoice. The Pagemill's loop feature provides a way to store data in a logical structure and parse it into a template.

In the following example, a web page needs to display a list of people's names and phone numbers. Each entry in the list should be an associative array of values. The following PHP script will populate the Pagemill with the data:

<?
// Make three entries for the list
$entry1 = array('name' => 'Bob Jones', 'phone' => '555-1111');
$entry2 = array('name' => 'John Smith', 'phone' => '555-2222');
$entry3 = array('name' => 'Jane Tate', 'phone' => '555-3333');

require_once('pagemill.class.php');
$pm = new Pagemill();

// Put the entries in a loop
$pm->addLoop('list', $entry1);
$pm->addLoop('list', $entry2);
$pm->addLoop('list', $entry3);

$pm->writeFile('list.tmpl');
?>

Now the template can access each row in the list using the loop tag as follows:

<loop name="list">
@{name}@can be reached at @{phone}@.<br />
</loop>

The Pagemill will repeat the loop's code for every item in the list. The item's associative array defines a new collection of variables that is available in the loop. In the above example, the first iteration of the loop parses the value of $entry1['name'] into .

The output of the above example would look like this:

Bob Jones can be reached at 555-1111.
John Smith can be reached at 555-2222.
Jane Tate can be reached at 555-3333.

See the sections about the loop tag and the addLoop() function for more information.

Back to Top

Conditions

Templates can define blocks of content that should only appear if certain conditions are met.

A condition tag would be used in a template as follows:

<condition name="loggedin">
You are logged into the system.
</condition>

The sentence inside the tag would only appear if the "loggedin" condition is true. See the sections about condition tags and the setCondition() function for more information.

Back to Top

Template Tags

The Pagemill provides four base tags for templates: loop, loopexists, condition, and else.

Back to Top

loop

The loop tag iterates through rows of data that have been added to the Pagemill via the addLoop() function. Its only required attribute is the name of the loop.

Example:

<loop name="capitals">
The capital of @{state}@ is @{city}@.<br />
</loop>

Loop elements can also have a cycle attribute. The cycle attribute should be a list of values separated by a comma. As the Pagemill loops through the data, the variable loops through the listed values.

Example:

<loop name="capitals" cycle="red,blue">
<p style="color: @{cycle}@">The capital of @{state}@ is @{city}@.</p>
</loop>

The above template will cause the text color of each paragraph to alternate between red and blue.

The cycle attribute can contain any number of values.

Back to Top

loopexists

The loopexists tag causes a block of code to appear only if the loop contains data. This is useful for things like table headers that should only appear if there is data to put in the table.

Example:

<loopexists name="capitals">
<table>
<tr>
<th>
State
</th>
<th>
Capital
</th>
</tr>
<loop name="capitals">
<tr>
<td>
@{city}@
</td>
<td>
@{state}@
</td>
</tr>
</loop>
</table>
</loopexists>

It is also possible to use a loopexists tag to display a block of code when there is not data in a loop. To do this, put an exclamation point (!) in front of the loop's name.

<loopexists name="!capitals">
<p>
This paragraph will only appear if the capitals loop does not contain
any data.
</p>
</loopexists>
Back to Top

condition

Sometimes there are elements on a web page that should only appear under certain circumstances. For example, there could be a login form that should only appear if the user has not logged in yet. Templates can test for those circumstances with the condition tag.

There are two ways to define a condition from the Pagemill. The first is the setCondition() function. This function simply sets a named condition to true or false (true is the default).

The second way to define a condition is to use a variable. Any variable can be treated like a condition. The condition will be considered false under any of these circumstances:

  • The variable has not been set.
  • The variable's value is a zero-length string.
  • Its value is false.
  • Its value is zero.

A condition can also make its contents appear when its named value is false by putting an exclamation point (!) in front of the name.

<condition name="loggedin">
<p>
This should appear when the user is logged in.
</p>
</condition>
<condition name="!loggedin">
<p>
This should appear when the user is not logged in.
</p>
</condition>
Back to Top

else

Else is a special tag that should only appear inside loopexists or condition tags. It specifies a block of code that should appear when the condition tested is NOT true.

<condition name="loggedin">
<p>
This should appear when the user is logged in.
</p>
<else />
<p>
This should appear when the user is not logged in.
</p>
</condition>
Back to Top

Custom Tags

The Pagemill provides a method for developers to add their own tags with custom functionality to the templating engine. Two common uses of custom tags are to simplify the inclusion of boilerplate code, and to add special attributes to HTML tags. Custom tags are added to the Pagemill with the registerTag() function.

In order to create a custom tag, first the developer must create a function for it. The function receives two arguments: an associative array of the tag's attributes, and a string containing the HTML code inside the tag. The function should return the code that will replace the tag when the Pagemill parses the template.

The following example creates a red tag that styles its content to appear red. (In the real world, this tag would not be very useful, since the same thing can easily be done in plain CSS and HTML; it is only presented here as a crude example of how custom tags work.)

<?
function redTag($attribs, $innerHTML) {
return '<span style="color: red;">' . $innerHTML . '</span>';
}
require_once('pagemill.class.php');
$pm = new Pagemill();
$pm->registerTag('red', 'redTag');
?>

The template would use the tag as follows:

This sentence is normal.  <red>This one is inside a custom tag.</red>

This next example demonstrates use of a custom tag to generate JavaScript code. It creates an inputdate tag that issues a warning if the user's input is not in the expected format.

<?
function inputdateTag($attribs, $innerHTML) {
static $includedFunction = false;
$name = $attribs['name'];
$output = "<input type=\"text\" name=\"$name\" onchange=\"isDateValid(this);\" />";
// Only include the code for the isDateValid() function once
if (!$includedFunction) {
$output .= '<script language="javascript">
function isDateValid(input) {
var re = /\d{1,2}-\d{1,2}-\d{4}/;
if (!input.value.match(re)) {
alert(\'Date must be in the format mm-dd-yyyy\');
}
};
</script>';
$includedFunction = true;
}
}
require_once('pagemill.class.php');
$pm = new Pagemill();
$pm->registerTag('inputdate', 'inputdateTag');
?>

Templates would use the tag as follows:

Enter your birthdate: <inputdate name="birthdate" />

A more robust version of the inputdate tag might generate code that displays a GUI calendar to complement or replace the text box.

Back to Top

Pagemill Functions

Following is a list of the most important functions in the Pagemill class. Not all functions are listed here, as there are some that do not typically need to be used outside of the class itself; in other words, functions not listed here are considered private in nature.

Back to Top

addLoop($name, [$name, ...,], $data)

addLoop() adds a row of data to a loop. In its simplest form, it takes two arguments: the name of the loop and the data in an associative array.

$pm = new Pagemill();
$name = array();
$name['first'] = 'Bob';
$name['last'] = 'Jones';
$pm->addLoop('names', $name);

It is also possible to add subloops to loops. This is accomplished by listing multiple loop names before the data. In the following example, each row of names contains rows of phone numbers.

$pm = new Pagemill();
$name = array();
$name['first'] = 'Bob';
$name['last'] = 'Smith';
$pm->addLoop('names', $name);
$phone1 = array();
$phone1['location'] = 'home';
$phone1['number'] = '555-1111';
$pm->addLoop('names', 'phones', $phone1);
$phone2 = array();
$phone2['location'] = 'office';
$phone2['number'] = '555-2222';
$pm->addLoop('names', 'phones', $phone2);

Whenever a subloop is added to a loop, it gets appended to the last row added to the parent loop. The data in the above example would get used in a template as follows:

<loop name="names">
This person's name is @{first}@ @{last}@.<br />
<loop name="phones">
@{first}@'s @{location}@ phone number is @{number}@.<br />
</loop>
</loop>

The output would be as follows:

This person's name is Bob Smith.
Bob's home phone number is 555-1111.
Bob's office phone number is 555-2222.
Back to Top

dump()

Return a string containing an HTML-formatted list of all the data in the Pagemill. Useful for debugging.

echo $pm->dump();
Back to Top

registerTag($name, $function)

Add a custom tag to the pagemill. The $name argument is the name of the tag as it will appear in the template markup. The $function argument is the name of the function that the Pagemill calls to process the tag.

The custom tag's function needs to accept two arguments. The first is an associative array of the tag's attributes. The second is the tag's contents. The function returns a string containing the code that should replace the custom tag.

$pm->registerTag('red', 'redTag');
Back to Top

setCondition($name, $value = true)

Set the value of a condition. All conditions set using setCondition() are global; in other words, they are accessible outside of loops.

Back to Top

setVariable($name, $value)

Set the value of a variable. All variables set using setVariable() are global; in other words, they are accessible outside of loops.

Back to Top

sortLoop($loopname, [$loopname, ...,] $key)

Change the order of the rows in a loop. For top-level loops, this function takes two arguments: the name of the loop and the key that should be used to sort it.

In the following example, the rows in the names loop will be sorted alphabetically by last name.

$pm = new Pagemill();
$name1 = array();
$name1['first'] = 'Bob';
$name1['last'] = 'Smith';
$name2 = array();
$name2['first'] = 'Jim';
$name2['last'] = 'Harris';
$pm->addLoop('names', $name1);
$pm->addLoop('names', $name2);
$pm->sortLoop('names', 'last');
Back to Top

writeFile($file)

Parse a template file. The parsed results are returned as a string.

Back to Top

writeText($source)

Parse a string as a template. The parsed results are returned as a string.

Back to Top

Changing the Pagemill's Internal Syntax

The tag names that are built into Pagemill (loop, loopexists, condition, and else) can be changed in the define() functions at the top of the pagemill.class.php script. The delimiters used to identify variable names can also be changed.

The default setting for the loop tag, for example, is this:

define(PM_LOOP_TAG, "loop");

To change the names of loop tags to "foreach," change the line as follows:

define(PM_LOOP_TAG, "foreach");

The built-in tags are expected to be XML compliant. The tag name can be any valid string of characters. They must always follow the format of standard XML elements; e.g., <loop></loop> or <foreach></foreach>.

The delimiters for variable names must have both a beginning and ending delimiter. The default settings are as follows:

define(PM_VAR_START, "@{");
define(PM_VAR_END, "}@");

To change the delimiters to double curly brackets ( {{ and }} ), change the lines to the following:

define(PM_VAR_START, "{{");
define(PM_VAR_END, "}}");

Variables would now be identified in templates like {{foo}} and {{bar}}.

To change them to the PHP-style syntax for variables inside double-quoted strings:

define(PM_VAR_START, '{$');
define(PM_VAR_END, '}');

Variables would now be identified in templates like {$foo} and {$bar}.

Back to Top

Online Resources

Information about the Pagemill, including support, examples, and updates, can be found at http://dev.castwide.com.

Back to Top

Credits

The PHP Pagemill and this documentation were written by Fred Snyder for Castwide Technologies. This documentation was created on June 18, 2007. It was last updated on June 24, 2007.

Back to Top

More Articles