
The fTemplating class allows for simple PHP templating with abstraction of HTML into a separate scope. This is a contrast to system like Smarty where a complete language has been developed, and must be learned, to use templating.
When creating an instance of the fTemplating class, a single optional parameter $root
can be passed. This controls the directory that is used as the basis for relative paths. If no root is passed, the $_SERVER['DOCUMENT_ROOT']
is used as a default.
$template = new fTemplating('/var/www/inc/templates/');
The fTemplating class is built around the concept of elements. Elements can be any data type, and may represent anything from a chunk of data, to an object, or a file path.
The first operation to be performed is setting an element via the set()
method. The first parameter, $element
, is an identifier for the element you are setting. The second parameter is the $value
.
Here are some example of setting various elements:
// These value would usually be set in the initialization script for a site
$template->set('header', 'header.php');
$template->set('footer', 'footer.php');
$template->set('db', $database);
Multiple elements can be set in one method call by passing an associative array to set()
.
$template->set(array(
'header' => 'header.php',
'footer' => 'footer.php'
));
Elements can be retrieved by calling the get()
method. This method required the first parameter, $element
, which is what you want to retrieve. You can also optionally pass a second parameter $default_value
to specify what should be returned in the element has not yet been set. If no $default_value
is specified and the element has not been set, NULL
will be returned.
// This will be the name or NULL
$name = $template->get('name');
// This will be the name or 'No name provided'
$name = $template->get('name', 'No name provided');
Multiple elements can be retrieved in one method call by passing an array of element names to get()
. If default values are required, an associative array can be passed with the element name being the key and the default value being the value.
// Get an array with two values - the element names will be the keys and the
// values will be the values, NULL will be returned if an element is not set
$values = $template->get(array('first_name', 'last_name'));
// This returns the same elements, but with the explicit default values N/A
$values = $template->get(array(
'first_name' => 'N/A',
'last_name' => 'N/A'
));
Elements can be removed by passing the $element
name to delete()
. The value of the element being deleted will be returned.
$value = $template->delete('name');
A $default_value
can be passed as the second parameter to delete()
, and it will be returned if the element specified was not set.
$value = $template->delete('name', 'None specified');
Multiple elements can be deleted at a time by passing an array of element names to delete()
. Default values can be provided by passing an associative array of element names as keys and default values as values.
// Delete the first_name and last_name elements
$values = $template->delete(array('first_name', 'last_name'));
// Delete the first_name and last_name elements, providing defaults
$values = $template->delete(array(
'first_name' => 'No first name provided',
'last_name' => 'No last name provided'
));
In addition to set()
, there is a method called add()
that will allow appending a value to an element that is an array. It will also initialize an element to an array if the element does not already exist.
$template->add('js', '/sup/js/main.js');
$template->add('css', array('path' => '/sup/css/print.css', 'media' => 'print'));
$template->add('css', '/sup/css/base.css');
A value can be added at the beginning by passing TRUE
as the third parameter.
$template->add('js', '/sup/js/jquery-min.js', TRUE);
The remove()
method can used to remove a value from an array element. The removed value will be returned.
$template->add('numbers', 1);
$template->add('numbers', 2);
$two = $template->remove('numbers');
A value may be removed from the beginning of an array element by passing TRUE
as a second parameter to remove()
.
$one = $template->remove('numbers');
Values may be removed from an array element by calling filter()
with the $element
as the first parameter, and the $value
to remove as the second.
$template->filter('numbers', 1);
The filter()
method does a normal equality comparison, so filtering 0
will also removed FALSE
and other empty values. All matching values will be removed from the array, not just the first one.
While simply retrieving elements is useful in certain situations, much of the time there will be a need to process text content for output in the HTML. The encode()
and prepare()
methods function the same was as get()
except that they run the returned value through fHTML::encode() and fHTML::prepare(), respectively.
// Encode turns <, >, & and " into HTML entities to prevent XSS
echo $template->encode('name');
// ::prepare() should only be used with trusted content to make
// sure special characters outside of HTML tags are encoded
echo $template->prepare('html_bio');
When an element is an array, it is possible to use array dereference syntax in the element name to access a specific array key. This syntax works with set()
, get()
, delete()
, add()
, remove()
, filter()
, encode()
and prepare()
.
$template->set(
'user',
array(
'first_name' => 'John',
'last_name' => 'Smith'
)
);
// This will echo John
echo $template->get('user[first_name]');
Array dereferencing can be any number of layers deep.
echo $template->get('user[groups][0][name]');
Probably the most useful aspect of the fTemplating class is the place()
method. This method takes a single parameter, $element
, in which you specify what element you want to place in the page.
place()
works differently depending of the type of path is contained in the element. If the element contains a path is to a .php (.inc or .php5) file, the file will be included inside the scope of the fTemplating class (i.e. $this
will return to the instance of the class). If a .css, .js or .rss (.xml) path is placed, the proper XHTML tags will be echoed.
In order to handle the title
attribute for RSS feeds and the media
attribute for CSS files it is possible to set the value of an element to be an associative array containing a key path
and a key corresponding to the appropriate attributes.
Here are some examples of using place()
for different types of files:
$template->set('title', 'Flourish');
$template->add('rss', array('path' => '/sup/rss/blog.rss', 'title' => 'Blog Posts'));
$template->place('header');
?>
<h1>Flourish</h1>
...
<?php
$template->place('footer');
Placing the header would include the file header.php
from the root we defined earlier of /var/www/inc/templates/
. Placing the footer would do that same thing for footer.php
.
Here is an example header.php
:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title><?php echo $this->prepare('title') ?></title>
<?php echo $this->place('css') ?>
<?php echo $this->place('js') ?>
<?php echo $this->place('rss') ?>
</head>
<body>
The HTML output from the above script would be:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Flourish</title>
<link rel="stylesheet" type="text/css" href="/sup/css/base.css" media="all" />
<link rel="stylesheet" type="text/css" href="/sup/css/print.css" media="print" />
<script type="text/javascript" src="/sup/js/main.js"></script>
<link rel="alternate" type="application/rss+xml" href="/sup/rss/blog.rss" title="Blog Posts" />
</head>
<body>
<h1>Flourish</h1>
...
</body>
</html>
If paths are contained in element that do not end in one of the extensions listed below, you can force all paths in an element to be displayed as a certain type of file by passing the type of file as the second parameter to place.
Here are the valid extensions:
.php
, .php5
, .inc
.js
.css
.rss
, .xml
Here is how you could force all paths in an element to be displayed as a certain type regaredless of the path extensions:
// JS files
$this->place('element1', 'js');
// CSS files
$this->place('element2', 'css');
// RSS files
$this->place('element3', 'rss');
// PHP files
$this->place('element4', 'php');
The method inject()
works identically to place()
, except that is accepts a file name or path instead of an element name. This allows placing a file without having to pass it to set()
first.
// Include a PHP file in the template root
$template->inject('sidebar.php');
// Add a JS file
$template->inject('/path/to/example.js');
The second parameter also functions identically to place()
, allowing the developer to specify the type of file being injected if it can't be auto-detected.
// Specifying the file type
$template->inject('/path/to/rss', 'rss');
fTemplating includes functionality that will minify javascript and CSS, combine multiple files into one, and add a query string to allow for far-futures expire headers. All of these features provide increased performance for HTTP clients.
Minification reduces the number of bytes sent to a user by removing whitespace and other unnecessary syntactic sugar. Combining multiple files reduces the number of HTTP requests made for each script execution. Adding a query string of the cache-file creation date allows use of far-futures expires headers without worrying about out-dated browser caches, which further reduces the number of HTTP requests.
In this section of documentation, a **cache file** refers to a file containing minified code of one or more original code files.
JS and CSS minification is enabled by calling enableMinification()
with a $mode
, $cache_directory
and optional $path_prefix
. This should be called before calling place()
on any elements since it works through the place()
method.
$template->enableMinification('development', $_SERVER['DOCUMENT_ROOT'] . '/sup/minification_cache/');
The $mode
can be either 'development'
or 'production'
. In 'development'
mode, all files are checked for modifications on each script execution. If a file has been modified since the cache was last created, the cache is regenerated. In 'production'
mode, cache files are only regenerated if they are missing.
The $cache_directory
should be a web-accessible directory for fTemplating to write the cache files to. It needs to be web-accessible since <script>
and <link>
tags will be referencing cache files stored inside of it.
The $path_prefix
is optional, and defaults to $_SERVER['DOCUMENT_ROOT']
. The $path_prefix
is appended to all JS and CSS paths in order to load them from the filesystem. For example a CSS file /css/main.css
would be prefixed with $_SERVER['DOCUMENT_ROOT']
, resulting in fTemplating looking for the file in /document/root/css/main.css
.
Since the minified files are stored on the filesystem, fTemplating must know how to translate the filesystem path into a URL. This translation is done by using fFilesystem::translateToWebPath(). If fTemplating is generating incorrect URLs for the minified files, please use fFilesystem::addWebPathTranslation() to indicate what translation should be done. fFilesystem: Translation has an example showing how the method is used.
Once minification is enabled, all calls to place()
that end up placing one or more CSS or JS files will automatically be minified.
$template->add('css', '/css/structure.css');
$template->add('css', '/css/typography.css');
$template->place('css');
Would normally result in the following HTML being written to the output:
<link rel="stylesheet" type="text/css" href="/css/structure.css" />
<link rel="stylesheet" type="text/css" href="/css/typography.css" />
With minification enabled, it would be written as:
<link rel="stylesheet" type="text/css" href="/cache/ae9210f4cbd017f4cbes.css?v=10298382912" />
fTemplating uses Douglas Crockfords JSMin algorithm, but has its own implementation that is optimized for PHP.
For CSS, there is not a definitive algorithm like JSMin. Currently fTemplating performs the following steps to minify CSS:
;
, {
, }
, ,
, >
and +
is removed:
inside of rule blocks is removed;}
is reduced to }
CSS minification does not touch @import
statements or url()
literals. Depending on the location of the $cache_directory
relative to the normal CSS directory, some @import
statements may break. However, with enableMinification()
, @import
statements should not generally be necessary. Similarly, url()
literals may break if the $cache_directory
is on a different directory level than the original CSS directory. This can be remedied by placing them on the same level, or using website-relative URLs.
PHP short tags, such as <?
and <?=
often make for easy-to-read and easy-to-write templates in raw PHP. Unfortunately, short tags can be disabled by an ini setting, and cant be relied upon when developing PHP for different environments.
The method enablePHPShortTags()
allows any PHP file directly included via place()
or inject()
to use PHP short tags even if they are turned off. This is accomplished by transforming short tags into long tags and saving the transformed PHP into a cache directory. The method accepts two parameters, $mode
and $cache_directory
.
$template->enablePHPShortTags('development', '/path/to/php/cache');
The $mode
can be either 'development'
or 'production'
. In 'development'
mode, all files are checked for modifications on each script execution. If a file has been modified since the cache was last created, the cache is regenerated. In 'production'
mode, cache files are only regenerated if they are missing.
The $cache_directory
is where the transformed PHP files are saved. This directory should not be web-accessible, but must be writable by the web server. fTemplating will properly preserve the value of __FILE__
and __DIR__
constants that are contained within templates that are transformed.
In certain circumstances it is useful to buffer the output of the page to allow for elements to be changed after content has already been output. The buffer()
method allows such buffering to be enabled.
When buffering is enabled, all calls to place()
actually insert a text token into the output. When the fTemplating class destructor is called (explicitly, or at the end of the page execution) the text tokens are all replaced by the output of the place()
call. The place()
method is actually executed in the destructor, so it will have the state of all elements at the end of the page, thus allowing elements to be modified after they have been seemingly already output.
There are two caveats to using the fTemplating output buffering.
place()
call and then in other non-templating code, it will be executed in the wrong order due to the fact that the place()
call is not done until the end of the page. If this is the case, you should probably not use buffering, or consider a way to refactor your code so that such sequential code execution is not required.Here is an example of using buffered output:
// Create the template and turn on buffering
$temp = new fTemplating();
$temp->buffer();
// Set up out elements
$temp->set('header', 'header.php');
$temp->set('title', 'This is the old title');
// Output the header
$temp->place('header');
// Change the title after the fact
$temp->set('title', 'This is the new title');
Lets assume that header.php
looks like this:
echo $this->get('title');
The output from above would be:
This is the new title
since the place()
call is not executed until the $temp
destructor is called at the end of the code (implicitly) and the title was changed before the end of the code.
This buffering technique can greatly reduce the complexity of the code that needs to be executed before the first output can be sent to the user.
It is possible to attach and retrieve fTemplating objects in any scope using the static methods attach()
and retrieve()
. attach()
accepts an fTemplating instance and an optional $name
for it. If no $name
is provided, it will be set to default
.
fTemplating::attach(new fTemplating('/path/to/root'));
The method retrieve()
accepts the $name
of a previously creating instance and returns it. If no $name
is passed, the default
template is returned.
$tmpl = fTemplating::retrieve();
It is possible to nest fTemplating objects for more complex situations. The concept is to have a child fTemplating object that will be placed by a parent. Such a child fTemplating object will automatically call place()
on the __main__
element when it itself is placed.
The __main__
element will normally be set by passing a PHP file path to the second parameter of the constructor. Like calls to set()
, any file path that is not absolute and does not start with ./
will be relative to the template root.
$user_info = new fTemplating('/templating/root/', 'sub_templates/user_info.php');
The fTemplating object may then be set to another fTemplating object and placed.
$template->set('user_info', $user_info);
// At this point sub_templates/user_info.php is placed
$template->place('user_info');
All sub-templates will inherit all minification and PHP short tag settings from their parent if not explicitly set.