I'm designing a simple templating system for a CMS in PHP which internally currently uses something like:
require_once 'templates/template1.php`;
to import the desired template.
I would like every content {{field123}}
in this PHP file to be automatically converted into <?php echo $row['field123']; ?>
before being passed into require_once
and executed by PHP.
Is there a way to activate a preprocessor (I know that PHP is already named after preprocessor) that does this replacement {{anything}} -> <?php echo $row['anything']; ?>
before executing the PHP code template1.php
? If not, what's the usual way to do this?
Having PHP code in templates - especially code with potential side-effects - can get dirty real quick. I would recommend using static templates, treating them as strings instead of executing them, then parsing them for tokens, with your main application compiling them and handling output.
Here is a rudimentary implementation that parses variables into tokens, and also handles mapped function calls in your templates. First, "fetching" our template (for a simple example):
$tpl = 'This is a sample template file.
It can have values like {{foo}} and {{bar}}.
It can also invoke mapped functions:
{{func:hello}} or {{func:world}}.
Hello user {{username}}. Have a good day!';
Then, the template parser:
function parse_template(string $tpl, array $vars): string {
// Catch function tokens, handle if handler exists:
$tpl = preg_replace_callback('~{{func:([a-z_]+)}}~', function($match) {
$func = 'handler_' . $match[1];
if(function_exists($func)) {
return $func();
}
return "!!!What is: {$match[1]}!!!";
}, $tpl);
// Generate tokens for your variable keys;
$keys = array_map(fn($key) => '{{' . $key . '}}', array_keys($vars));
// Substitute tokens:
$tpl = str_replace($keys, $vars, $tpl);
return $tpl;
}
These are our handler functions, with handler_X
matching {{func:X}}
.
function handler_hello() {
return 'HELLO THERE';
}
function handler_world() {
return '@Current World Population: ' . mt_rand();
}
Then, here are the variables you'd like to parse in:
$vars = [
'foo' => 'Food',
'bar' => 'Barnacle',
'username' => 'Herbert'
];
Now let's parse our template:
$parsed = parse_template($tpl, $vars);
echo $parsed;
This results in:
This is a sample template file.
It can have values like Food and Barnacle.
It can also invoke mapped functions:
HELLO THERE or @Current World Population: 1477098027.
Hello user Herbert. Have a good day!
Job done. You really don't need a complicated templating engine for something like this. You could easily extend this to allow the handlers to receive arguments defined in the template tokens -- however I'll leave that for your homework part. This should do to demonstrate the concept.