nitricware/tonic

无依赖的 PHP 模板引擎。

3.3.1 2020-07-16 12:10 UTC

This package is auto-updated.

Last update: 2024-09-16 21:04:55 UTC


README

超级快速且强大的模板引擎。纯 PHP,零依赖。

此 tonic 分支继续了 rgamba 的工作。

使用方法

使用 Tonic 非常简单直接。

use Tonic\Tonic;
$tpl = new Tonic("demo.html");
$tpl->user_role = "member";
echo $tpl->render();

它也非常灵活。上面的代码也可以写成以下形式

$tpl = new Tonic();
echo $tpl->load("demo.html")->assign("user_role","member")->render();

显示语法

使用 Tonic

<body>
<h1>Welcome {$user.name.capitalize().truncate(50)}</h1>
User role: {$role.lower().if("admin","administrator").capitalize()}
</body>

与全部使用 PHP 编写对比

<body>
<h1>Welcome <?php echo (strlen($user["name"]) > 50 ? substr(ucwords($user["name"]),0,50)."..." : ucwords($user["name"])) ?></h1>
User role: <?php if(strtolower($role) == "admin") { echo "Administrator" } else { echo ucwords($role) } ?>
</body>

安装

使用 composer 进行安装

{
    "require": {
        "nitricware/tonic": "^3.0"
    }
}

缓存

所有 tonic 模板都会编译回原生 PHP 代码。强烈建议您使用缓存功能,这样相同的模板就不需要反复编译,从而提高服务器端的 CPU 使用率。

$tpl = new Tonic();
$tpl->cache_dir = "./cache/"; // Be sure this directory exists and has writing permissions
$tpl->enable_content_cache = true; // Importante to set this to true!

修饰符

修饰符是用于以各种方式修改输出变量的函数。所有修饰符都必须以变量开头,并且可以与其他修饰符链式使用。示例

{$variable.modifier1().modifier2().modifier3()}

当使用关联数组时,我们也可以以相同的方式使用修饰符

{$my_array.item.sub_item.modifier1().modifier2().modifier3()}

处理日期

在 Tonic 模板中处理和格式化日期非常简单。

Tonic::$local_tz = 'America/New_york'; // Optionaly set the user's local tz
$tpl = new Tonic();
$tpl->my_date = date_create();

并且模板

<p>Today is {$my_date.date("Y-m-d h:i a")}</p>

处理时区

<p>The local date is {$my_date.toLocal().date("Y-m-d h:i a")}</p>

这将把 $my_date 渲染为 Tonic::$local_tz 中配置的时区

自定义时区

<p>The local date is {$my_date.toTz("America/Mexico_city").date("Y-m-d h:i a")}</p>

修饰符列表

创建自定义修饰符

如果您需要自定义修饰符,可以扩展列表并创建自己的。

// This function will only prepend and append some text to the variable
Tonic::extendModifier("myFunction",function($input, $prepend, $append = ""){
    // $input will hold the current variable value, it's mandatory that your lambda
    // function has an input receiver, all other arguments are optional
    // We can perform input validations
    if(empty($prepend)) {
        throw new \Exception("prepend is required");
    }
    return $prepend . $input . $append;
});

并且您可以轻松使用此修饰符

<p>{$name.myFunction("hello "," goodbye")}</p>

匿名修饰符

有时您只需要从模板内部直接调用函数,其返回值是不断变化的,因此不能与静态变量链接。此外,它的值不依赖于任何变量。在这些情况下,您可以使用匿名修饰符。要做到这一点,您需要创建一个自定义修饰符,如果您需要使用其他参数,请忽略 $input 参数。

Tonic::extendModifier("imagesDir", function($input){
    // Note that $input will always be empty when called this modifier anonymously
    return "/var/www/" . $_SESSION["theme"] . "/images";
});

然后您可以直接从模板中调用它

<img src="{$.imagesDir()}/pic.jpg" />

上下文感知

Tonic 会阻止您在应用程序中对可能导致潜在攻击的变量进行转义。每个将要显示给用户的变量都应该仔细转义,并且应该根据上下文进行转义。例如,链接的 href 属性中的变量应与 <h1> 标签中的某些变量以不同的方式进行转义。好消息是 Tonic 会为您做所有这些工作。

$tonic->assign("array",array("Name" => "Ricardo", "LastName", "Gamba"));
$tonic->assign("ilegal_js","javascript: alert('Hello');");

并且 HTML

<a href="{$ilegal_js}">Click me</a>
<!-- Will render: <a href="javascript%3A+alert%28%27Hello%27%29%3B">Click me</a> -->
<p>The ilegal js is: {$ilegal_js}</p>
<!-- Will render: <p> The ilegal js is: javascript: alert(&#039;Hello&#039;);</p> -->
<a href="?{$array}">Valid link generated</a>
<!-- Will render: <a href="?Name=Ricardo&LastName=Gamba">Valid link generated</a> -->
<p> We can also ignore the context awareness: {$ilegal_js.ignoreContext()}</p>

包含模板

您可以在另一个模板内部包含一个模板

{include footer.html}

我们还可以获取外部页面并将其加载到当前模板中

{include http://mypage.com/static.html}

模板包含支持嵌套调用,但请注意,在模板 "A" 中包含模板 "A" 可能会导致无限循环。

控制结构

如果 / 否则

制作条件非常简单

{if $user.role eq "admin"}
<h1>Hello admin</h1>
{elseif $user.role.upper() eq "MEMBER" or $user.role.upper() eq "CLIENT"}
<h1>Hello member</h1>
{else}
<h1>Hello guest</h1>
{endif}

您可以使用常规逻辑运算符(==、!=、>、<、>=、<=、||、&&)或使用以下

循环

<ul>
{loop $user in $users}
<li>{$user.name.capitalize()}</h1>
{endloop}
</ul>

如果需要数组键

<ul>
{loop $i,$user in $users}
<li>{$i} - {$user.name.capitalize()}</h1>
{endloop}
</ul>

处理宏

如果结构和循环结构都可以以更 HTML 适应的方式编写,这样代码就可以更易于阅读。以下是一个示例

<ul tn-if="$users">
    <li tn-loop="$user in $users">Hello {$user}</li>
</ul>

这与以下内容完全相同

{if $users}
<ul>
    {loop $user in $users}
    <li>Hello {$user}</li>
    {endloop}
</ul>
{endif}

本地化

Tonic 支持本地化。按照以下方式更改调用

use Tonic\Tonic;
$tpl = new Tonic("demo.html", "localized.xml");
$tpl->user_role = "member";
echo $tpl->render();

如果您想使用多个本地化文件,请将数组用作第二个构造参数,如下所示

$tpl = new Tonic("demo.html", array("localized.xml", "anotherFile.xml");

文件 localized.xml 可以有任意名称(例如 EN.xml),但必须是有效的XML。以下是它的结构

<?xml version="1.0" encoding="UTF-8"?>
<Strings>
	<string>
		<key>TEXT</key>
		<value>Tonic will automatically load the specified localized text from the specified language file.</value>
	</string>
</Strings>

要在模板中访问本地化字符串,请使用 {$localized.FILENAME.KEY},其中 KEY 是您在XML文档中使用的键,而 FILENAME 是本地化文件的名称,不包含扩展名(例如 EN)。在这个例子中就是 {$localized.EN.TEXT}

模板继承

Tonic支持单模板继承。其背后的理念是保持简单。多重继承可能导致复杂且难以维护的视图。

在Tonic中,模板继承基于 blocks。假设我们有一个以下的基础模板

base.html

<html>
<head>
<title>Tonic</title>
</head>
<body>
<section tn-block="header">
    <h1>Default welcome message!</h1>
</section>
<section>
    <div tn-block="content">
        <p>This is the default content.</p>
    </div>
</section>
<section tn-block="footer">Tonic 2016</section>
</body>

然后你有几个部分模板或视图,你想重用主 base.html 作为“骨架”。为了做到这一点,我们使用 blocks。每个块由标签 {block name}{endblock} 定义,或由HTML属性 tn-block="name" 定义,它实际上用属性作为块的名称 name 封闭了HTML元素。

inner.html

{ extends base.html }
<section tn-block="header" class="myheader">
    <h1>Welcome to my inner page!</h1>
</section>
<p>This content WON´T be rendered at all!</p>
<div tn-block="content">
    <p>This is the content of my inner view.
</div>

结果我们将得到以下视图

<html>
<head>
<title>Tonic</title>
</head>
<body>
<section class="myheader">
    <h1>Welcome to my inner page!</h1>
</section>
<section>
    <div>
        <p>This is the content of my inner view.
    </div>
</section>
<section>Tonic 2016</section>
</body>

这里有几个键

  • 标签 { extend }。它唯一的参数应该是相对于 Tonic::$root 的模板文件(默认为 ./)。
  • HTML属性 tn-block="header" 定义了块,并位于HTML元素的闭合匹配标签内。
  • 子模板(inner.html)中找到的所有块将有效地替换父模板(base.html)上匹配的块。如果子模板中有一个在父模板中没有定义的块,则该块将不会渲染
  • 块名称必须是字母数字的,且不能包含 $ 或任何特殊字符或空格。
  • 父模板(base.html)继承了子模板的上下文(作用域、变量)。
  • 您只能扩展一个模板。

注意 还可以使用 {block header}{endblock} 语法来定义块。我们更喜欢使用HTML属性,因为它更简洁。示例

{block myBlock}<div><h1>Welcome</h1></div>{endblock}

与以下完全相同

<div tn-block="myBlock"><h1>Welcome</h1></div>

路线图

  • 添加更多 @throws 而不是 @return bool|void

变更日志

  • 2020-07-16 - 3.3.1 - composer修复
  • 2020-07-14 - 3.3 - PHP 7.4更新:添加了类型提示并移除了未使用的功能
  • 2019-05-10 - 3.2 - NitricWare分支:支持本地化
  • 2016-10-11 - 3.1.0 - 添加了对模板继承的支持
  • 2015-03-25 - 3.0.0 - 添加了对上下文感知和条件语句、循环的宏语法的支持
  • 2015-03-23 - 2.2.0 - 添加了对命名空间的支持并添加了修饰符异常
  • 2015-03-20 - 2.1.0 - 添加了扩展修饰符的选项。
  • 2015-03-19 - 2.0.0 - 重要更新。大多数结构的语法略有变化,与之前版本不兼容。