text/template

简单安全的字符串模板引擎(类似Twig语法),支持嵌套的if/elseif/else、循环、过滤器。简单的OOP API:仅一个类完成所有工作(2行代码)。快速安全:无代码生成,无eval()代码。通过回调扩展。全面测试。包含丰富的示例。

v2.8.5 2023-06-26 17:11 UTC

README

Downloads this Month Latest Stable Version Actions Status Coverage Status Supports PHP 5.4+ Supports PHP 7.0+ Homepage Examples

{if user.searching=='template'}This is for you {= user.name }{else}Welcome {= user.name }{/if}

单类PHP5/7模板引擎,支持if/循环/过滤器

  • 简单:无需编译或缓存 - 直接解析 input stringoutput string
  • 安全:无eval(); 无代码生成。无需文件系统访问。单元测试。
  • 小巧:无依赖。
  • 功能:嵌套循环、if/elseif/else、自定义过滤器、自动转义

旨在成为一个小型字符串模板引擎,以满足电子邮件或小型HTML模板的需求。它不打算替代像Smarty或Twig这样的预编译功能齐全的模板引擎。

TextTemplate使用正则表达式进行文本解析。不生成或评估代码 - 因此这可能是在非时间关键情况下的安全解决方案。

虽然大多数模板引擎依赖于评估生成的代码和文件系统访问,但Text-Template使用一组正则表达式来解析模板。既不生成任何中间代码,也不评估任何代码。因此,TextTemplate在设计上应该比Smarty或Twig更安全。
TextTemplate支持无限嵌套循环和序列。

基本示例

安装

// 1. Define the Template
$tplStr = <<<EOT

Hello World {= name }
{if name == "Matthias"}
Hallo {= name | capitalize }
{elseif name == "Jan"}
Hi Buddy
{else}
You are not Matthias
{/if}

EOT;

// 2. Define the Data for the Template
$data = [
    "name" => "Matthias"
];

// 3. Parse
$tt = new TextTemplate($tplStr);
echo $tt->apply ($data);

我推荐使用 composer

灵活的标签开始/结束

composer require text/template

本文档引用默认标签:使用一个括号 { 作为开始,一个 } 作为结束分隔符。当然,它们是灵活的:如果你想更改这些,请参阅:TextTempate::setOpenCloseTagChars()

值注入

使用值标签

向代码中注入值。任何变量都将默认使用 htmlspecialchars() 编码。要输出原始内容,请使用 raw 过滤器:{=htmlCode|raw}

{= varName}

要访问数组元素或对象,请使用 "." 来访问子元素

循环

{= users.0.name}

您可以在其中插入循环

在每个循环内部,有两个魔法值 @index0(索引从0开始)和 @index1(索引从1开始)。

{for curName in names}
Current Name: {= curName}
{/for}

在循环内部,您可以 {break}{continue} 循环。

{for curName in names}
Line {= @index1 }: {= curName}
{/for}

条件(if)

您可以使用if条件

快捷方式:测试变量是否为null

{if someVarName == "SomeValue"}
Hello World
{/if}

可以使用 &&(与)、||(或)和括号构建复杂逻辑表达式。

{if someVarName}
    someVarName is set!
{/if}
{if !someVarName}
    someVarName is not set!
{/if}

您可以在比较中使用的值上使用过滤器。

{if someVarName && otherVarName}
    someVarName and otherVarName are set!
{/if}
{if someVarName || otherVarName}
    someVarName or otherVarName are set!
{/if}
{if someVarName || (otherVarName && anotherVarName)}
    Condition is true!
{/if}
{if someVarName && !(otherVarName && anotherVarName)}
    Condition is true!
{/if}

您可以添加自定义运算符用于条件。

{if someArray|count > otherArray|count}
    someArray has more items than otherArray
{/if}

添加运算符

您可以为条件添加自定义运算符。

添加新运算符

$tt->addOperator("contains",
    function ($operand1, $operand2) {
        return strpos($operand1, $operand2) !== false; 
    }
);

预定义运算符

条件(else)

{if someVarName == "SomeValue"}
Hello World
{else}
Goodbye World
{/if}

选择列表

{if someVarName == "SomeValue"}
Hello World
{elseif someVarName == "OtherValue"}
Hello Moon
{else}
Goodbye World
{/if}

调用函数

您可以注册用户定义的函数。

$template->addFunction("sayHello", 
    function ($paramArr, $command, $context, $cmdParam, $self) {
        return "Hello " . $paramArr["msg"];
    }
);

调用函数并将输出放入模板

{sayHello msg="Joe"}

或将结果注入上下文进行进一步处理

{sayHello msg="Joe" > out}
{=out}

处理异常

使用 !> 捕获异常并将它们重定向到作用域。

{throw msg="SomeMsg" !> lastErr}

或使用 !break!continue 来中断/继续循环

注释

使用 {# #} 添加注释(将从输出中删除

Template {# Some Comment #}
{# Some
Multiline
Comment #}

添加过滤器

您可以添加自定义过滤器或覆盖自己的过滤器。默认过滤器是html(htmlspecialchars)。

添加新的过滤器

$tt->addFilter ("currency", function ($input, $decimals=2, $decSeparator=",", $thounsandsSeparator=".") {
    return number_format ($input, $decimals, $decSeparator, $thousandsSeparator);
});

使用参数调用过滤器(参数分隔符为:

{= variable | currency:2:,:. }

在您的模板中使用此过滤器

{= someVariable | currency }

预定义过滤器

替换默认过滤器

默认情况下,出于安全考虑,所有值都将使用"DEFAULT"-过滤器进行转义。(除非在过滤器部分选择了"raw")

如果您出于某种原因想禁用此功能或更改转义函数,您可以覆盖DEFAULT-过滤器

$tt->addFilter ("_DEFAULT_", function ($input) {
    return strip_tags ($input);
});

或者

$tt->setDefaultFilter("singleLine");

以下示例将用strip_tags()函数替换htmlspecialchars()转义器。

部分

部分就像函数一样,但提供它们包含的内容

{sectionxy name="someName"}
Some Content
{/sectionxy}
{sectionxy name="someName" > out}
Some Content
{/sectionxy}

{= out}

要使用部分,您只需设置回调

$textTemplate->addSection("sectionxy", function ($content, $params, $command, $context, $cmdParam, $self) {
    return "Content to replace section content with";
});

删除空行

{strip_empty_lines}
line1

line2
{/strip_empty_lines}

计数数组

{trim > countElem}{= var | count}{/trim}
{if countElem == "0" }
{/if}

函数返回重定向

将输出追加到变量中。

{print >> out}
A
{/print}
{print >> out}
B
{/print}

{= out}

参数调试

要查看传递给模板的所有参数,请使用

{= __CONTEXT__ | raw}

它将输出当前上下文的结构。

处理未定义变量

默认情况下,文本模板在模板尝试访问未定义变量时不会抛出任何异常。

为了提高调试,您可以通过将$softFail设置为falseapply()函数的第二个参数)来切换此行为

try {
    $tt = new TextTemplate("{=someUndefinedName}");
    echo $tt->apply([], false);
    //                  ^^^^^
} catch (UndefinedVariableExceptions $e) {
    echo "UndefinedVariable: {$e->getTriggerVarName()}"
}

将返回

UndefinedVariable: someUndefinedName

更改标签开始和结束字符

有时在解析其他模板文件时,{tag}{\tag}并不合适。您可以使用setOpenCloseTagChars()函数更改开始和结束字符。

$textTemplate->setOpenCloseTagChars("{{", "}}");

上面的示例将监听{{tag}}{{/tag}}

基准测试

尽管解析器是用纯正则表达式构建的,但我尽量避免了像预读等过于昂贵的结构。

我们得到了相当好的结果

贡献、错误报告、增强

如果您想做出贡献,请发送您的Pull-Request或在github上打开一个问题。

保持测试为绿色:请参阅/提供单元测试。此项目使用nette/tester进行单元测试。

此项目使用kickstart的基于docker的即用型开发容器。只需运行./kickstart.sh即可运行此项目。

要启动开发容器

./kickstart.sh

要运行测试,请在容器内运行kick test。(见.kick.yml

关于

Text-Template由Matthias Leuffen编写http://leuffen.de

加入InfraCAMP