Smarty-Advanced Features

Chapter 15. Advanced Features

Objects

Smarty allows access to PHP objects through the templates. There are two ways to access them. One way is to register objects to the template, then use access them via syntax similar to custom functions. The other way is to assign objects to the templates and access them much like any other assigned variable. The first method has a much nicer template syntax. It is also more secure, as a registered object can be restricted to certain methods or properties. However, a registered object cannot be looped over or assigned in arrays of objects, etc. The method you choose will be determined by your needs, but use the first method whenever possible to keep template syntax to a minimum.

If security is enabled, no private methods or functions can be accessed (begininning with "_"). If a method and property of the same name exist, the method will be used.

You can restrict the methods and properties that can be accessed by listing them in an array as the third registration parameter.

By default, parameters passed to objects through the templates are passed the same way custom functions get them. An associative array is passed as the first parameter, and the smarty object as the second. If you want the parameters passed one at a time for each argument like traditional object parameter passing, set the fourth registration parameter to false.

The optional fifth parameter has only effect with format being true and contains a list of methods that should be treated as blocks. That means these methods have a closing tag in the template ({foobar->meth2}...{/foobar->meth2}) and the parameters to the methods have the same synopsis as the parameters for block-function-plugins: They get 4 parameters $params, $content, &$smarty and &$repeat and they also behave like block-function-plugins.

Example 15-1. using a registered or assigned object
php
// the object

class My_Object {
function meth1($params, &$smarty_obj) {
return "this is my meth1";
}
}

$myobj = new My_Object;
// registering the object (will be by reference)
$smarty->register_object("foobar",$myobj);
// if we want to restrict access to certain methods or properties, list them
$smarty->register_object("foobar",$myobj,array(\'meth1\',\'meth2\',\'prop1\'));
// if you want to use the traditional object parameter format, pass a boolean of false
$smarty->register_object("foobar",$myobj,null,false);

// We can also assign objects. Assign by ref when possible.
$smarty->assign_by_ref("myobj", $myobj);

$smarty->display("index.tpl");
?>

And here\'s how to access your objects in index.tpl:

{* access our registered object *}
{foobar->meth1 p1="foo" p2=$bar}

{* you can also assign the output *}
{foobar->meth1 p1="foo" p2=$bar assign="output"}
the output was {$output}

{* access our assigned object *}
{$myobj->meth1("foo",$bar)}

Prefilters

Template prefilters are PHP functions that your templates are ran through before they are compiled. This is good for preprocessing your templates to remove unwanted comments, keeping an eye on what people are putting in their templates, etc.

Prefilters can be either registered or loaded from the plugins directory by using load_filter() function or by setting the $autoload_filters variable.

Smarty will pass the template source code as the first argument, and expect the function to return the resulting template source code.

Example 15-2. using a template prefilter
php
// put this in your application
function remove_dw_comments($tpl_source, &$smarty)
{
return preg_replace("//U","",$tpl_source);
}

// register the prefilter
$smarty->register_prefilter("remove_dw_comments");
$smarty->display("index.tpl");
?>

This will remove all the comments in the template source.

Postfilters

Template postfilters are PHP functions that your templates are ran through after they are compiled. Postfilters can be either registered or loaded from the plugins directory by using load_filter() function or by setting $autoload_filters variable. Smarty will pass the compiled template code as the first argument, and expect the function to return the result of the processing.

Example 15-3. using a template postfilter
// put this in your application
function add_header_comment($tpl_source, &$smarty)
{
return "\n\"; ?>\n".$tpl_source;
}

// register the postfilter
$smarty->register_postfilter("add_header_comment");
$smarty->display("index.tpl");
?>

This will make the compiled Smarty template index.tpl look like:


{* rest of template content... *}

Output Filters

When the template is invoked via display() or fetch(), its output can be sent through one or more output filters. This differs from postfilters because postfilters operate on compiled templates before they are saved to the disk, and output filters operate on the template output when it is executed.

Output filters can be either registered or loaded from the plugins directory by using load_filter() function or by setting $autoload_filters variable. Smarty will pass the template output as the first argument, and expect the function to return the result of the processing.

Example 15-4. using a template outputfilter
php
// put this in your application
function protect_email($tpl_output, &$smarty)
{
$tpl_output =
preg_replace(\'!(\S+)@([a-zA-Z0-9\.\-]+\.([a-zA-Z]{2,3}|[0-9]{1,3}))!\',
\'%40\', $tpl_output);
return $tpl_output;
}

// register the outputfilter
$smarty->register_outputfilter("protect_email");
$smarty->display("index.tpl");

// now any occurrence of an email address in the template output will have
// a simple protection against spambots
?>

Cache Handler Function

As an alternative to using the default file-based caching mechanism, you can specify a custom cache handling function that will be used to read, write and clear cached files.

Create a function in your application that Smarty will use as a cache handler. Set the name of it in the $cache_handler_func class variable. Smarty will now use this to handle cached data. The first argument is the action, which will be one of \'read\', \'write\' and \'clear\'. The second parameter is the Smarty object. The third parameter is the cached content. Upon a write, Smarty passes the cached content in these parameters. Upon a \'read\', Smarty expects your function to accept this parameter by reference and populate it with the cached data. Upon a \'clear\', pass a dummy variable here since it is not used. The fourth parameter is the name of the template file (needed for read/write), the fifth parameter is the cache_id (optional), and the sixth is the compile_id (optional).

Note: The last parameter ($exp_time) was added in Smarty-2.6.0.

Example 15-5. example using MySQL as a cache source
php
/*

example usage:

include(\'Smarty.class.php\');
include(\'mysql_cache_handler.php\');

$smarty = new Smarty;
$smarty->cache_handler_func = \'mysql_cache_handler\';

$smarty->display(\'index.tpl\');


mysql database is expected in this format:

create database SMARTY_CACHE;

create table CACHE_PAGES(
CacheID char(32) PRIMARY KEY,
CacheContents MEDIUMTEXT NOT NULL
);

*/

function mysql_cache_handler($action, &$smarty_obj, &$cache_content, $tpl_file=null, $cache_id=null, $compile_id=null, $exp_time=null)
{
// set db host, user and pass here
$db_host = \'localhost\';
$db_user = \'myuser\';
$db_pass = \'mypass\';
$db_name = \'SMARTY_CACHE\';
$use_gzip = false;

// create unique cache id
$CacheID = md5($tpl_file.$cache_id.$compile_id);

if(! $link = mysql_pconnect($db_host, $db_user, $db_pass)) {
$smarty_obj->_trigger_error_msg("cache_handler: could not connect to database");
return false;
}
mysql_select_db($db_name);

switch ($action) {
case \'read\':
// read cache from database
$results = mysql_query("select CacheContents from CACHE_PAGES where CacheID=\'$CacheID\'");
if(!$results) {
$smarty_obj->_trigger_error_msg("cache_handler: query failed.");
}
$row = mysql_fetch_array($results,MYSQL_ASSOC);

if($use_gzip && function_exists("gzuncompress")) {
$cache_content = gzuncompress($row["CacheContents"]);
} else {
$cache_content = $row["CacheContents"];
}
$return = $results;
break;
case \'write\':
// save cache to database

if($use_gzip && function_exists("gzcompress")) {
// compress the contents for storage efficiency
$contents = gzcompress($cache_content);
} else {
$contents = $cache_content;
}
$results = mysql_query("replace into CACHE_PAGES values(
\'$CacheID\',
\'".addslashes($contents)."\')
");
if(!$results) {
$smarty_obj->_trigger_error_msg("cache_handler: query failed.");
}
$return = $results;
break;
case \'clear\':
// clear cache info
if(empty($cache_id) && empty($compile_id) && empty($tpl_file)) {
// clear them all
$results = mysql_query("delete from CACHE_PAGES");
} else {
$results = mysql_query("delete from CACHE_PAGES where CacheID=\'$CacheID\'");
}
if(!$results) {
$smarty_obj->_trigger_error_msg("cache_handler: query failed.");
}
$return = $results;
break;
default:
// error, unknown action
$smarty_obj->_trigger_error_msg("cache_handler: unknown action \"$action\"");
$return = false;
break;
}
mysql_close($link);
return $return;

}

?>

Resources

The templates may come from a variety of sources. When you display or fetch a template, or when you include a template from within another template, you supply a resource type, followed by the appropriate path and template name. If a resource is not explicitly given the value of $default_resource_type is assumed.

Templates from $template_dir

Templates from the $template_dir do not require a template resource, although you can use the file: resource for consistancy. Just supply the path to the template you want to use relative to the $template_dir root directory.

Example 15-6. using templates from $template_dir
$smarty->display("index.tpl");
$smarty->display("admin/menu.tpl");
$smarty->display("file:admin/menu.tpl"); // same as one above
?>

{* from within Smarty template *}
{include file="index.tpl"}
{include file="file:index.tpl"} {* same as one above *}

Templates from any directory

Templates outside of the $template_dir require the file: template resource type, followed by the absolute path and name of the template.

Example 15-7. using templates from any directory
$smarty->display("file:/export/templates/index.tpl");
$smarty->display("file:/path/to/my/templates/menu.tpl");
?>

And from within Smarty template:

{include file="file:/usr/local/share/templates/navigation.tpl"}

Windows Filepaths

If you are using a Windows machine, filepaths usually include a drive letter (C:) at the beginning of the pathname. Be sure to use "file:" in the path to avoid namespace conflicts and get the desired results.

Example 15-8. using templates from windows file paths
$smarty->display("file:C:/export/templates/index.tpl");
$smarty->display("file:F:/path/to/my/templates/menu.tpl");
?>

And from within Smarty template:

{include file="file:D:/usr/local/share/templates/navigation.tpl"}

Templates from other sources

You can retrieve templates using whatever possible source you can access with PHP: databases, sockets, LDAP, and so on. You do this by writing resource plugin functions and registering them with Smarty.

See resource plugins section for more information on the functions you are supposed to provide.

Note: Note that you cannot override the built-in file resource, but you can provide a resource that fetches templates from the file system in some other way by registering under another resource name.

Example 15-9. using custom resources
// put these function somewhere in your application
function db_get_template ($tpl_name, &$tpl_source, &$smarty_obj)
{
// do database call here to fetch your template,
// populating $tpl_source
$sql = new SQL;
$sql->query("select tpl_source
from my_table
where tpl_name=\'$tpl_name\'");
if ($sql->num_rows) {
$tpl_source = $sql->record[\'tpl_source\'];
return true;
} else {
return false;
}
}

function db_get_timestamp($tpl_name, &$tpl_timestamp, &$smarty_obj)
{
// do database call here to populate $tpl_timestamp.
$sql = new SQL;
$sql->query("select tpl_timestamp
from my_table
where tpl_name=\'$tpl_name\'");
if ($sql->num_rows) {
$tpl_timestamp = $sql->record[\'tpl_timestamp\'];
return true;
} else {
return false;
}
}

function db_get_secure($tpl_name, &$smarty_obj)
{
// assume all templates are secure
return true;
}

function db_get_trusted($tpl_name, &$smarty_obj)
{
// not used for templates
}

// register the resource name "db"
$smarty->register_resource("db", array("db_get_template",
"db_get_timestamp",
"db_get_secure",
"db_get_trusted"));

// using resource from php script
$smarty->display("db:index.tpl");
?>

And from within Smarty template:

{include file="db:/extras/navigation.tpl"}

Default template handler function

You can specify a function that is used to retrieve template contents in the event the template cannot be retrieved from its resource. One use of this is to create templates that do not exist on-the-fly.

Example 15-10. using the default template handler function
// put this function somewhere in your application

function make_template ($resource_type, $resource_name, &$template_source, &$template_timestamp, &$smarty_obj)
{
if( $resource_type == \'file\' ) {
if ( ! is_readable ( $resource_name )) {
// create the template file, return contents.
$template_source = "This is a new template.";
$template_timestamp = time();
$smarty_obj->_write_file($resource_name,$template_source);
return true;
}
} else {
// not a file
return false;
}
}

// set the default handler
$smarty->default_template_handler_func = \'make_template\';
?>