I'm searching for a very basic PHP templating system. Right now I'm using:
/**
* Renders a single line. Looks for {{ var }}
*
* @param string $string
* @param array $parameters
*
* @return string
*/
function renderString($string, array $parameters)
{
$replacer = function ($match) use ($parameters)
{
return isset($parameters[$match[1]]) ? $parameters[$match[1]] : $match[0];
};
return preg_replace_callback('/{{\s*(.+?)\s*}}/', $replacer, $string);
}
(from here: PHP - Extremely light templating system)
but I can only assign and display variables. I also need a way to use conditions like IF and loop arrays.
I found Rain TPL - http://www.raintpl.com/Quick-Start/#if - which is very close to what I'm looking for, but there are a few things that I don't like it it:
So, is there anything out there similar to this, but even more "basic", strict, and more secure?
From your requirements I am guessing you are wanting your website users to write some basic php scripts. You might not find a free template engine that does that.
I think it's better for you if you change an existing template engine to your needs.
You can change Rain TPL to disable some of its features that you don't want. For example you can do...
Disable function use in IF statements:
a. Locate elseif( preg_match( '/\{if(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){
b. Replace $this->function_check( $tag );
with a new method something like $this->ifcondition_function_check( $tag );
c. Create the new method that will disable all functions in IF statements.
private function ifcondition_function_check($code)
{
$preg = '/[a-zA-z0-9]+\((.*?)\)/';
if (preg_match( $preg, $code, $match ) ){
// find the line of the error
$line = 0;
$rows=explode("
",$this->tpl['source']);
while( !strpos($rows[$line],$code) )
$line++;
// draw the error line
$error = str_replace( array('<','>'), array( '<','>' ), array($code,$rows[$line]) );
$error = str_replace( $code, "<font color=red>$code</font>", $rows[$line] );
// debug the error and stop the execution of the script
die( "<div>RainTPL Sandbox Error in template <b>{$this->tpl['tpl_filename']}</b> at line $line : <i>$error</i></b>" );
}
}
d. Now functions are disabled.
draw()
unset( $this->tpl );
@unlink($this->tpl['compiled_filename']);
.Hope this helps
Have a look at Twig or H2O.
Twig might be for you.
It can do conditions, and has a sandbox mode for untrusted code.
It does compilation and caching, but that seems to be possible to turn off.
There's also a Mustache port for PHP. The PHP port is here. The syntax is similar to what you're already doing, and supports simple IF and FOREACH-type loops.
And, does it without eval.
try PHPTAL: http://phptal.org/
the syntax for TAL templates does not break html, so you - and the designers can check if they going to look good.
see also:
http://wiki.zope.org/ZPT/TALSpecification14
http://wiki.zope.org/ZPT/TAL
very easy to use
When you want it really small and flexible maybe the best is to stay with your own stuff? I like handcrafting ;-) You can extend your existing function. Following, your function plus if
and loop
statement and escaping of variables for security:
<?php
function renderString($str, $parms)
{
// if
$str = preg_replace_callback('/{{if (?P<name>\w+)}}(?P<inner>.*?){{endif}}/is', function($match) use ($parms) {
if( isset($parms[$match['name']])) {
// recursive
return renderString($match['inner'], $parms);
}
}, $str);
// loop
$str = preg_replace_callback('/{{loop (?P<name>\w+)}}(?P<inner>.*?){{endloop}}/is', function($match) use ($parms) {
if( isset($parms[$match['name']]) && is_array($parms[$match['name']])) {
$str = '';
foreach ($parms[$match['name']] as $value) {
$parms['loop'] = $value;
// recursive
$str .= renderString($match['inner'], $parms);
}
return $str;
}
}, $str);
// var
$str = preg_replace_callback('/{{(?P<name>\w+)}}/is', function($match) use ($parms) {
if( isset($parms[$match['name']])) {
return htmlspecialchars($parms[$match['name']]);
}
}, $str);
return $str;
}
$template = "<h1>{{title}}</h1>
{{if optional}}
<p>Optional: {{optional}}</p>
{{endif}}
{{if noop}}I'm not there{{endif}}
<ul>
{{loop numbers}}
<li>{{symbol}} {{loop}}</li>
{{endloop}}
</ul>";
echo renderString($template, array(
'title' => 'The Title',
'optional' => 'I am optional',
'numbers' => array( 'one', 'two', 'three'),
'symbol' => '>',
));
This script is tested in PHP 5.3 and you can copy it 1:1 to a file to play with it.