For a group project I am trying to create a template engine for PHP for the people less experienced with the language can use tags like {name} in their HTML and the PHP will
A simple approach is to convert the template into PHP and run it.
$template = preg_replace('~\{(\w+)\}~', 'showVariable(\'$1\'); ?>', $template);
$template = preg_replace('~\{LOOP:(\w+)\}~', 'data[\'$1\'] as $ELEMENT): $this->wrap($ELEMENT); ?>', $template);
$template = preg_replace('~\{ENDLOOP:(\w+)\}~', 'unwrap(); endforeach; ?>', $template);
For example, this converts the template tags to embedded PHP tags.
You'll see that I made references to $this->showVariable()
, $this->data
, $this->wrap()
and $this->unwrap()
. That's what I'm going to implement.
The showVariable
function shows the variable's content. wrap
and unwrap
is called on each iteration to provide closure.
Here is my implementation:
class TemplateEngine {
function showVariable($name) {
if (isset($this->data[$name])) {
echo $this->data[$name];
} else {
echo '{' . $name . '}';
}
}
function wrap($element) {
$this->stack[] = $this->data;
foreach ($element as $k => $v) {
$this->data[$k] = $v;
}
}
function unwrap() {
$this->data = array_pop($this->stack);
}
function run() {
ob_start ();
eval (func_get_arg(0));
return ob_get_clean();
}
function process($template, $data) {
$this->data = $data;
$this->stack = array();
$template = str_replace('<', '', $template);
$template = preg_replace('~\{(\w+)\}~', 'showVariable(\'$1\'); ?>', $template);
$template = preg_replace('~\{LOOP:(\w+)\}~', 'data[\'$1\'] as $ELEMENT): $this->wrap($ELEMENT); ?>', $template);
$template = preg_replace('~\{ENDLOOP:(\w+)\}~', 'unwrap(); endforeach; ?>', $template);
$template = '?>' . $template;
return $this->run($template);
}
}
In wrap()
and unwrap()
function, I use a stack to keep track of current state of variables. Precisely, wrap($ELEMENT)
saves the current data to the stack, and then add the variables inside $ELEMENT
into current data, and unwrap()
restores the data from the stack back.
For extra security, I added this extra bit to replace <
with PHP echos:
$template = str_replace('<', '', $template);
Basically to prevent any kind of injecting PHP codes directly, either ,
<%
, or .
Usage is something like this:
$engine = new TemplateEngine();
echo $engine->process($template, $data);
This isn't the best method, but it is one way it could be done.