PHP 中带有 {{field}} 的简单模板系统

Simple templating system with {{field}} in PHP

我正在为 PHP 中的 CMS 设计一个简单的模板系统,该系统目前在内部使用类似的东西:

require_once 'templates/template1.php`;

导入所需的模板。

我希望此 PHP 文件中的每个内容 {{field123}} 在传递到 require_once 并由 PHP 执行之前自动转换为 <?php echo $row['field123']; ?> .

有没有办法激活 预处理器(我知道 PHP 已经以预处理器命名)做这个替换 {{anything}} -> <?php echo $row['anything']; ?> 在执行 PHP 代码之前 template1.php?如果不是,通常的做法是什么?

如评论和 How do I capture PHP output into a variable? 中所述,使用输出缓冲可以工作:

<?php
ob_start();
?>

Hello
{{field123}} and {{field4}}    
World
<?php // or require_once 'template1.php'; ?>

<?php
$s = ob_get_clean();
$a = array('field123' => 'test', 'field4' => 'test2');
$s = preg_replace_callback('/{{(.*?)}}/', function ($m) use ($a) { return isset($a[$m[1]]) ? $a[$m[1]] : $m[0]; }, $s);
echo $s;
?>

// Output:
// Hello
// test and test2    
// World

这里我们也用了类似的方法来做替换

在模板中包含 PHP 代码 - 特别是具有潜力 side-effects 的代码 - 会很快变脏。我建议使用静态模板,将它们视为字符串而不是执行它们,然后将它们解析为标记,由您的主应用程序编译它们并处理输出。

这是一个基本的实现,它可以将变量解析为标记,还可以处理模板中映射的函数调用。首先,“获取”我们的模板(举个简单的例子):

$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!';

然后,模板解析器:

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;
}

这些是我们的处理函数,handler_X 匹配 {{func:X}}

function handler_hello() {
    return 'HELLO THERE';
}
function handler_world() {
    return '@Current World Population: ' . mt_rand();
}

那么,这里是你想要解析的变量:

$vars = [
    'foo' => 'Food',
    'bar' => 'Barnacle',
    'username' => 'Herbert'
];

现在让我们解析我们的模板:

$parsed = parse_template($tpl, $vars);

echo $parsed;

这导致:

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!

工作完成。你真的不需要一个复杂的模板引擎来做这样的事情。您可以轻松地扩展它以允许处理程序接收在模板标记中定义的参数——但是我将把它留给您的作业部分。这应该用来证明这个概念。