hindy / template

快速PHP模板引擎

1.0.3 2016-03-07 10:16 UTC

This package is not auto-updated.

Last update: 2024-09-20 19:30:03 UTC


README

Build Status Coverage Status

简介

Flow最初是Armin Ronacher原创的Twig模板引擎的一个主要分支,他为Chyrp(一个博客引擎)创建了它。Flow具有模板继承、包含、宏、自定义助手、自动转义、空白控制以及许多小功能,使得编写模板变得愉快。Flow旨在在编写干净的模板时提供一致和连贯的体验。Flow将每个模板编译为其自己的PHP类;与APC一起使用时,这使得Flow成为一个非常快速和高效的模板引擎。模板可以从文件中读取,从字符串数组中加载,甚至可以相对容易地从数据库中加载。

安装

最简单的安装方法是使用Composer;最低限度的composer.json配置如下

{
    "require": {
        "flow/flow": "0.7.*"
    }
}

Flow需要PHP 5.3或更高版本。强烈推荐使用PHP 5.4。

使用

在代码中使用Flow非常简单

<?php
require 'path/to/src/Flow/Loader.php';
use Flow\Loader;
Loader::autoload();
$flow = new Loader(array(
    'source' => 'path/to/templates', // or ['path/to/templates1', 'path/to/templates2']
    'target' => 'path/to/cache',
));

try {
    $template = $flow->load('home.html');
    $template->display(array(
        'data_1' => 'My first data',
        'data_2' => 'My second data',
    ));
} catch (\Exception $e) {
    // something went wrong!
    die($e->getMessage());
}

Loader构造函数接受一个选项数组。它们是

  • source:模板源文件的路径。
  • target:编译的PHP文件的路径。
  • mode:重新编译模式。
  • mkdir:当目标目录不存在时传递给mkdir()的模式。使用false来抑制自动创建目标目录。默认为0777。
  • adapter:可选的Flow\Adapter对象。请参阅本文档底部关于从其他来源加载模板的部分。
  • helpers:自定义助手的数组。

sourcetarget选项是必需的。

mode选项可以是以下之一

  • Loader::RECOMPILE_NEVER:从不重新编译已经编译的模板。
  • Loader::RECOMPILE_NORMAL:只有当编译的模板由于修改而比源文件旧时才重新编译。
  • Loader::RECOMPILE_ALWAYS:只要可能就始终重新编译。

默认模式是Loader::RECOMPILE_NORMAL。如果模板从未被编译过,或者编译的PHP文件缺失,无论当前模式如何,Loader都会编译它一次。

在典型的开发环境中,应使用Loader::RECOMPILE_NORMAL模式,而在可能的情况下,对于生产环境应使用Loader::RECOMPILE_NEVER模式。仅由开发人员用于内部调试目的的Loader::RECOMPILE_ALWAYS模式应一般避免。

Flow抛出两种类型的异常:用于语法错误的SyntaxError和用于其他所有内容的RuntimeException

source目录以外的模板文件的任何引用都视为错误。

语法检查

语法检查可以按以下方式执行

<?php
require 'path/to/src/Flow/Loader.php';
use Flow\Loader;
Loader::autoload();
$flow = new Loader(array(
    'source' => 'path/to/templates',
    'target' => 'path/to/cache',
));

$file = 'my_template.html';

if (!$flow->isValid($file, $error)) {
    echo 'The template ' . $file . ' is not valid: ' . $error;
}

上面的示例将检查模板中的错误,而不实际编译它。

程序化编译

可以在不加载和显示模板的情况下编译模板

<?php
require 'path/to/src/Flow/Loader.php';
use Flow\Loader;
Loader::autoload();
$flow = new Loader(array(
    'source' => 'path/to/templates',
    'target' => 'path/to/cache',
));

try {
    $flow->compile('some_template.html');
} catch (\Exception $e) {
    // something went wrong!
    die($e->getMessage());
}

如果您的应用程序需要批量编译多个模板或允许用户上传、创建或修改模板,则这很有用。

基本概念

Flow使用{%%}来分隔块标签。块标签主要用于模板继承中的块声明和控制结构。块标签的示例是blockforif。一些块标签可能有主体部分。它们通常被相应的end<tag>标签包围。Flow使用{{}}来分隔输出标签,并使用{##}来分隔注释。关键字和标识符是区分大小写的

注释

使用{##}来分隔注释

{# This is a comment. It will be ignored. #}

注释可以跨多行,但不能嵌套;它们将被完全从输出结果中删除。

表达式输出

要输出字面量、变量或任何类型的表达式,请使用开头的 {{ 和结尾的 }} 标签。

Hello, {{ username }}

{{ "Welcome back, " ~ username }}

{{ "Two plus two equals " ~ 2 + 2 }}

字面量

存在几种字面量类型:数字、字符串、布尔值、数组以及 null

数字

数字可以是整数或浮点数。

{{ 42 }} and {{ 3.14 }}

大数字可以用下划线分隔以提高可读性。

Price: {{ 12_000 | number_format }} USD

下划线 _ 的确切位置不重要,尽管第一个字符必须是数字;数字内的任何 _ 字符都将被删除。数字将被转换为 PHP 数字,因此受 PHP 对数字上下限和精度的处理方式限制。复杂的数字和货币运算应在 PHP 中使用 GMP 扩展或 bcmath 扩展完成。

字符串

字符串可以是双引号或单引号;两者都识别转义序列字符。不支持变量扩展。请使用字符串连接代替。

{{ "This is a string " ~ 'This is also a string' }}

您还可以使用连接运算符连接两个或多个字符串或标量。

{{ "Welcome," .. user.name }}

连接运算符使用单个空格字符将字符串连接起来。

布尔值

{{ true }} or {{ false }}

当打印或连接时,true 将转换为 1,而 false 将转换为空字符串。这种行为与 PHP 在字符串上下文中处理布尔值的方式一致。

数组

{{ ["this", "is", "an", "array"][0] }}

数组也像 PHP 中的哈希表一样。

{{ ["foo" => "bar", 'oof' => 'rab']['foo'] }}

打印数组会导致抛出 PHP 警告;请使用 join 辅助函数。

{{ [1,2,3] | join(', ') }}

空值

{{ null }}

当打印或连接时,null 将转换为空字符串。这种行为与 PHP 在字符串上下文中处理空值的方式一致。

运算符

除了短路操作,布尔运算符 orand 会返回它们的操作数之一。这意味着您可以这样做,例如

Status: {{ user.status or "default value" }}

注意字符串 '0''' 被视为假。有关更多信息,请参阅分支部分。这种行为与 PHP 在布尔上下文中处理字符串的方式一致。

比较运算符可以接受多个操作数。

{% if 1 <= x <= 10 %}
<p>x is between 1 and 10 inclusive.</p>
{% endif %}

这相当于

{% if 1 <= x and x <= 10 %}
<p>x is between 1 and 10 inclusive.</p>
{% endif %}

in 运算符与数组、迭代器和普通对象一起使用。

{% if 1 in [1,2,3] %}
1 is definitely in 1,2,3
{% endif %}

{% if 1 not in [4,5,6] %}
1 is definitely not in 4,5,6
{% endif %}

对于迭代器和普通对象,in 运算符首先使用简单的 (array) 类型转换将它们转换为数组。

使用 ~ (波浪号) 将两个或多个标量作为字符串连接起来。

{{ "Hello," ~ " World!" }}

字符串连接的优先级低于算术运算符。

{{ "1 + 1 = " ~ 1 + 1 ~ " and everything is OK again!" }}

将产生

1 + 1 = 2 and everything is OK again!

使用 .. (双点) 将两个或多个标量作为字符串使用单个空格字符连接起来。

{{ "Welcome," .. user.name }}

字符串输出、连接和连接将标量值强制转换为字符串。

运算符优先级

以下是按优先级降序排列的 Flow 中所有运算符的列表

  • 属性访问:对象和数组使用 .[]
  • 过滤器链:|
  • 算术:一元 -+%/*-+
  • 连接:..~
  • 比较:!=======!=<><>>=<=
  • 条件:innotandorxor
  • 三元:? :

您可以通过括号分组子表达式来覆盖优先级规则。

属性访问

对象

您可以使用 . 运算符访问对象的成员变量或方法。

{{ user.name }}

{{ user.get_full_name() }}

调用对象的方法时,如果没有传递参数,则括号是可选的。对象属性访问的完整语义如下

对于无括号的属性访问,按优先级顺序

  1. 如果属性是可访问的成员变量,则返回其值。
  2. 如果对象实现了 __get,则调用并返回其值。
  3. 如果属性是一个可调用的方法,则调用并返回其值。
  4. 如果对象实现了 __call,则调用并返回其值。
  5. 返回 null。

对于带括号的属性访问,按照优先级顺序

  1. 如果属性是一个可调用的方法,则调用并返回其值。
  2. 如果对象实现了 __call,则调用并返回其值。
  3. 返回 null。

您始终可以通过使用括号来强制调用方法。

数组

您可以使用 . 运算符或 [] 运算符来返回数组的一个元素

{{ user.name }} is the same as {{ user['name'] }}

{{ users[0] }}

. 运算符更为严格:只能使用名称类型的标记作为属性。名称类型的标记以字母或下划线开头,只能包含字母数字和下划线字符,就像 PHP 变量和函数名一样。

数组的一个特殊属性访问规则是可以调用存储在数组中的闭包函数

<?php
$template = $flow->load('my_template.html');
$template->display(array(
    'user' => array(
        'firstname' => 'Rasmus',
        'lastname'  => 'Lerdorf',
        'fullname'  => function($self) {
            return $self['firstname'] . ' ' .  $self['lastname'];
        },
    ),
));

如下在模板中调用 fullname "方法"

{{ user.fullname }}

以这种方式调用时,闭包函数将隐式传递它所在的数组作为第一个参数。额外的参数将作为第二个和后续参数传递给闭包函数。此规则允许您拥有类似对象的数组:它们可以访问数组中的其他成员值或函数。

动态属性访问

您可以动态访问对象或数组的属性

{% set attr = 'name' %}

Your name: {{ user[attr] }}

助手函数

助手函数是您可以用来在使用前测试或修改值的简单函数。您可以使用两种方式使用它们

  • 将助手函数用作函数
  • 将助手函数用作过滤器

除了少数例外,它们是可以互换的。

将助手函数用作函数

{{ upper(title) }}

您可以将助手函数链接起来,就像您可以在 PHP 中链接函数调用一样

{{ nl2br(upper(trim(my_data))) }}

将助手函数用作过滤器

使用 | 字符来分隔数据和过滤器

{{ title | upper }}

您可以通过使用 | 字符链接它们来使用多个过滤器。以这种方式使用它们类似于 Unix 中的管道:上一个过滤器的输出是下一个过滤器的输入。例如,为了按顺序修剪、转换为大写和将换行符转换为 <br> 标签,只需简单写下

{{ my_data | trim | upper | nl2br }}

一些内置助手函数接受额外的参数,这些参数由括号分隔,并用逗号分隔,如下所示

{{ "foo " | repeat(3) }}

这等价于以下

{{ repeat("foo ", 3) }}

当将助手函数用作过滤器时,请小心混合运算符

{{ 12_000 + 5_000 | number_format }}

由于运算符优先级,上述示例在语义上等价于

{{ 12_000 + (5_000 | number_format) }}

当编译为 PHP 时,将输出 12005,这可能不是您预期的。可以将加法放在括号内,如下所示

{{ (12_000 + 5_000) | number_format }}

或使用助手函数作为函数

{{ number_format(12_000 + 5_000) }}

特殊的 raw 助手函数

raw 助手函数只能用作过滤器。它的唯一目的是将表达式标记为原始字符串,即使在启用自动转义的情况下也不会被转义

{% autoescape on %}
{{ "<p>this is a valid HTML paragraph</p>" | raw }}

如果没有应用 raw 过滤器,上述将产生

&lt;p&gt;this is a valid HTML paragraph&lt;/p&gt;

内置助手函数

absbytescapitalizecycledatedumpeescapefirstformatis_divisible_byis_emptyis_evenis_oddjoinjson_encodekeyslastlengthlowernl2brnumber_formatrangerawrepeatreplacestrip_tagstitletranstrimtruncateunescapeupperurl_encodeword_wrap

注册自定义助手函数

注册自定义助手函数很简单

<?php
$helpers = array(
    'random' => function() { return 4; },
    'exclamation' => function($s = null) { return $s . '!'; },
);

$flow = new Loader(array(
    'source'  => 'templates',
    'target'  => 'cache',
    'helpers' => $helpers,
));

try {
    $template = $flow->load('my_template.html');
    $template->display();
} catch (\Exception $e) {
    // something went wrong!
    die($e->getMessage());
}

您可以使用您的自定义助手函数,就像使用任何其他内置助手函数一样

A random number: {{ random() }} is truly {{ "bizarre" | exclamation }}

当用作函数时,即使您的助手函数没有参数,也需要括号。一般来说,当用作过滤器时,输入将作为第一个参数传递给助手函数。为您的自定义助手函数中的每个参数设置默认值是明智的。

由于内置助手和自定义助手共享相同的命名空间,您可以使用自己的版本覆盖内置助手,尽管通常不推荐这样做。

分支

使用if标签进行分支。使用可选的elseifelse标签来拥有多个分支

{% if expression_1 %}
    expression 1 is true!
{% elseif expression_2 %}
    expression 2 is true!
{% elseif expression_3 %}
    expression 3 is true!
{% else %}
    nothing matches!
{% endif %}

被认为是假的值有:falsenull0'0'''[](空数组)。这种行为与PHP在布尔上下文中处理数据类型的方式一致。从经验来看,将字符串'0'视为假值通常很有用:通常数据来自关系型数据库,在PHP的大多数驱动程序中,返回的元组中的整数会被转换为字符串。您始终可以使用严格的比较运算符===!==

内联if和unless语句修饰符

除了独立块标签版本外,if标签还可用作语句修饰符。如果您熟悉Ruby或Perl,您可能会觉得这很熟悉

{{ "this will be printed" if this_evaluates_to_true }}

上述内容在语义上等同于

{%- if this_evaluates_to_true -%}
{{ "this will be printed" }}
{%- endif -%}

您可以使用任何类型的布尔逻辑,就像标准块标签版本一样

{{ "this will be printed" if not this_evaluates_to_false }}

在某些情况下,使用unless构造可能更自然。以下内容等同于上述内容

{{ "this will be printed" unless this_evaluates_to_false }}

内联if和unless修饰符适用于输出标签、break和continue标签、extends标签、parent标签、set标签和include标签。

三元运算符?:

如果您需要在表达式中进行分支,可以使用三元运算符

{{ error ? '<p>' ~ error ~ '</p>' :  '<p>success!</p>' }}

三元运算符在表达式中的优先级最低。

迭代

使用for标签遍历数组或迭代器的每个元素。使用可选的else子句,如果未发生迭代,则隐式分支

{% for link in links %}
    <a href="{{ link.url }}">{{ link.title }}</a> {% else %}
{% else %}
    There are no links available.
{% endfor %}

空数组或迭代器以及非数组或迭代器的值将分支到elseempty子句。

您还可以通过使用逗号来迭代键和值对

{% for key, value in associative_array %}
    <p>{{ key }} = {{ value }}</p>
{% endfor %}

上面示例中的keyvalue都是局部于迭代的。一旦迭代停止,它们将保留其先前的值(如果有)。

特殊变量loop包含多个有用的属性,并在for块内部可用

{% for user in users %}
    {{ user }}{{ ", " unless loop.last }}
{% endfor %}

如果您有一个普通的loop变量,其值在for块内部将暂时超出作用域。

特殊的loop变量有一些属性

  • loop.counter0loop.index0:零起始索引。
  • loop.counterloop.index:一起始索引。
  • loop.revindex0:零起始逆索引。
  • loop.revindex:一起始逆索引。
  • loop.first:如果当前迭代是第一个,则评估为true
  • loop.last:如果当前迭代是最后一个,则评估为true
  • loop.parent:如果有适用,则包含父迭代loop对象。

Break和continue

您可以使用breakcontinue来跳出循环,以及跳到下一个迭代。以下将打印“1 2 3”

{% for i in [0,1,2,3,4,5] %}
    {% continue if i < 1 %}
    {{ i }}
    {% break if i > 2 %}
{% endfor %}

Set

有时不可避免地要将值设置为变量、对象或数组属性;请使用set构造

{% set fullname = user.firstname .. user.lastname %}

{% set user.fullname = fullname %}

您还可以将set用作缓冲输出并将结果存储在变量中的方式

{% set slogan %}
<p>This changes everything!</p>
{% endset %}
...
{{ slogan }}
...

set构造引入的变量的作用域始终是其周围上下文的局部。

块是模板继承的核心

{# this is in "parent_template.html" #}
<p>Hello</p>
{% block content %}
<p>Original content</p>
{% endblock %}
<p>Goodbye</p>

{# this is in "child_template.html" #}
{% extends "parent_template.html" %}
This will never be displayed!
{% block content %}
<p>This will be substituted to the parent template's "content" block</p>
{% endblock %}
This will never be displayed!

当加载child_template.html时,它将产生

<p>Hello</p>

<p>This will be substituted to the parent template</p>

<p>Goodbye</p>

块继承是通过替换父模板或扩展模板中的所有块,以与子模板或扩展模板中找到的块相同,并使用父模板作为布局模板来实现的;丢弃子模板布局。这会递归向上进行,直到没有更多要扩展的模板。模板中的两个块不能有相同的名称。您可以在另一个块内定义块,但不能在宏内定义。

扩展

extends 构造函数指示 Flow 加载和扩展一个模板。当前模板中定义的块将覆盖扩展模板中定义的块。

{% extends "path/to/layout.html" %}

模板扩展机制是全动态的,但有一些限制。您可以像任何其他语句一样使用上下文变量或将其包裹在条件语句中。

{% extends layout if some_condition %}

您还可以使用三元运算符。

{% extends some_condition ? custom_layout : "default_layout.html" %}

但是,您不能使用在模板中 extends 标签之前计算的任何表达式和变量。这是因为无论 extends 标签在模板中的位置如何,它都是模板首先评估的内容。这也是为什么最好将您的 extends 标签放置在模板顶部的原因。例如,以下将不会工作:

{% set extend_template = true %}
{% extends "parent.html" if extend_template %}

以下也将不会工作,因为 tpl 是在 extends 标签之前在模板内部计算的值。

{% set tpl = "parent.html" %}
{% extends tpl %}

然而,如果 extend_templatetpl 变量是在模板加载之前已经存在的上下文变量,那么上述两个示例将按预期工作。

在模板中每个模板只能声明一个 extends 标签,或者在顶级作用域之外声明 extends 标签,这是语法错误。

参数化模板扩展

在扩展父模板之前使用 set 标签覆盖上下文变量将不起作用。这是因为无论其位置如何,extends 标签都是模板首先评估的内容,并且扩展模板将丢弃当前扩展模板的布局(即 block 标签之外的所有内容),以扩展模板的布局。

但是,您可以在扩展模板时传递一个数组以覆盖父模板的上下文。对于父模板

{# this is in parent.html #}
{% if show %}
TADA!
{% endif %}

和子模板

{# this is in child.html #}
{% extends "parent.html" with ['show' => true] %}

渲染子模板将产生

TADA!

同样,您不能在用于 extends 标签的数组参数中使用使用 set 标签创建的变量。例如,以下将不会工作:

{% set foo = "BAR" %}
{% extends "parent.html" with [some_string => foo] %}

父模板

通过使用 parent 标签,您可以在子块中包含父块的內容

{% block child %}
    {% parent %}
{% endblock %}

在块之外或宏内部使用 parent 标签是语法错误。

宏是创建灵活且可重用部分模板的绝佳方法

{% macro bolder(text) %}
<b>{{ text }}</b>
{% endmacro %}

要调用它们

{{ @bolder("this is great!") }}

宏调用前面加有 @ 字符。这样做是为了避免与助手、方法调用和属性访问发生名称冲突。

所有参数都是可选的;默认值为 null,而传递的额外位置参数将被忽略。Flow 允许您为每个参数定义自定义默认值。

{% macro bolder(text="this is a bold text!") %}
<b>{{ text }}</b>
{% endmacro %}

您还可以使用命名参数

{{ @bolder(text="this is a text") }}

额外的命名参数将覆盖相同名称的位置参数和之前的相同名称的命名参数。括号在未传递任何参数时是可选的。使用 set 构造函数在宏内声明的参数和变量是局部于宏的,并且一旦宏返回,它们将不再存在。

宏是动态作用域的。它们继承调用上下文。

{% macro greet %}
<p>{{ "Hello," .. name }}</p>
{% endmacro %}

{% set name = "Joe" %}

{{ @greet }}

上述代码将打印

<p>Hello Joe</p>

调用上下文被参数和默认参数值屏蔽。

宏的输出默认是未转义的,无论当前的 autoescape 设置如何。要转义输出,必须显式应用 escapee 过滤器。用作表达式一部分的宏调用将根据当前的 autoescape 设置进行转义。在宏内部,转义按常规进行,取决于当前的 autoescape 设置。未定义的宏在调用时返回 null。

宏通过扩展模板继承,同时覆盖父模板中具有相同名称的其他宏。

在块或其他宏内部定义宏是语法错误。在模板中重新定义宏也是语法错误。

导入宏

最好将模板中的宏分组,就像在模块或类中分组函数一样。要使用在另一个模板中定义的宏,只需导入它们

{% import "path/to/form_macros.html" as form %}

所有导入的宏都必须使用 as 关键字进行别名。要调用导入的宏,只需在宏名称前加上别名,后跟一个点

{{ @form.text_input }}

导入的宏通过扩展模板继承,同时覆盖父模板中具有相同别名和名称对的导入宏。

装饰宏

您可以通过首先导入它们来装饰宏

{# this is in "macro_A.html" #}
{% macro emphasize(text) %}<b>{{ text }}</b>{% endmacro %}

{# this is in "macro_B.html" #}
{% import "macro_A.html" as A %}
{% macro emphasize(text) %}<i>{{ @A.emphasize(text) }}</i>{% endmacro %}

{# this is in "template_C.html" #}
{% import "macro_B.html" as B %}
Emphasized text: {{ @B.emphasize("this is pretty cool!") }}

上述内容在渲染时将生成

Emphasized text: <i><b>this is pretty cool!</b></i>

包含

使用 include 标签将模板的片段包含到您的模板中

{% include "path/to/sidebar.html" if page.sidebar %}

这对于诸如页眉、侧边栏和页脚之类的东西很有用。包含不存在或不可读的模板是运行时错误。请注意,没有机制来防止模板的循环包含,尽管存在 PHP 运行时递归限制:要么达到允许的内存分配大小,从而产生致命的运行时错误,要么达到最大嵌套层数,如果您正在使用 xdebug。

参数化模板包含

与模板扩展一样,您可以将数组作为包含的模板的覆盖上下文传递

{% include "footer.html" with ['year' => current_year] %}

数组参数将覆盖当前上下文中的任何变量,但仅限于包含期间。

路径解析

extendsincludeimport 标签中引用的路径可以是当实例化加载器对象时从指定的 source 选项开始的绝对路径,也可以是当前模板目录的相对路径。

绝对路径

绝对路径必须以 / 字符开始,如下所示

{% include "/foo/bar.html" %}

在上面的示例中,如果 source 目录是 /var/www/templates,则标签将尝试包含模板 /var/www/templates/foo/bar.html,而不管当前模板的目录是什么。

相对路径

相对路径 不能/ 字符开始

{% include "far.html" %}

在这个示例中,如果 source 目录是 /var/www/templates,当前模板的目录相对于 sourceboo,那么标签将尝试包含模板 /var/www/templates/boo/far.html

路径注入防止

如果尝试加载 source 目录之外的任何文件,Flow 将抛出 RuntimeException

从其他源加载模板

有时您需要从数据库或甚至字符串数组中加载模板。在 Flow 中,可以通过将实现 Flow\Adapter 接口的类的对象传递给 Loader 构造函数的 adapter 选项来实现这一点。

Flow\Adapter 接口声明了三个方法

  • isReadable($path):确定路径是否可读。
  • lastModified($path):返回路径的最后修改时间。
  • getContents($path):返回给定路径的内容。

Loader 构造函数中给出的 source 选项仍然确定模板是否有效;即,模板是否可以在源目录中逻辑上找到。

以下是实现流程适配器到字符串数组的示例

<?php
require 'path/to/src/Flow/Loader.php';

use Flow\Loader;
use Flow\Adapter;

class ArrayAdapter implements Adapter
{
    static $templates = array(
        'first.html' => 'First! {% include "second.html" %}',
        'second.html' => 'Second!',
    );

    public function isReadable($path)
    {
        return isset(self::$templates[$path]);
    }

    public function lastModified($path)
    {
        return filemtime(__FILE__);
    }

    public function getContents($path)
    {
        return self::$templates[$path];
    }
}

Loader::autoload();
$flow = new Loader(array(
    'source'  => __DIR__ . '/templates',
    'target'  => __DIR__ . '/cache',
    'mode'    => Loader::RECOMPILE_ALWAYS,
    'adapter' => new ArrayAdapter,
));
$flow->load('first.html')->display();

上述内容将编译模板并渲染以下内容

First! Second!

输出转义

您可以使用escape或其别名e过滤器来转义要打印出的数据。输出转义假设输出为HTML。

使用自动转义

如果您想将所有表达式的输出在打印前自动转义,以最大限度地减少潜在的XSS攻击,请使用自动转义功能。

{% autoescape on %}

将自动转义视为在每次表达式输出时隐式地放置一个escapee过滤器。您通常希望将此指令放置在模板的顶部附近。自动转义按模板逐个处理;它永远不会继承、包含或从其他模板导入。

如果您不小心重复转义了一个变量,无需担心。所有已转义的数据将不会自动转义;请注意,这仅适用于使用escape或其别名e作为过滤器,而不是作为函数的情况。

{% autoescape on %}
{{ "Dr. Jekyll & Mr. Hyde" | escape }}

您可以通过将其设置为off在任何时候关闭自动转义。

{% autoescape off %}

您可以使用相应的endautoescape标签来隔离自动转义的效应,无论它是开启还是关闭。

{% autoescape on %}
This section is specifically autoescaped: {{ "<b>bold</b>" }}
{% endautoescape %}

默认情况下,自动转义最初设置为关闭。

原始过滤器

通过在变量输出上使用raw过滤器,数据将不会被转义,无论是否有escape过滤器或当前的自动转义状态。您必须将其用作过滤器;raw助手不可用作函数。

控制空白

当您正在编写一个对空白敏感的文件格式的模板时,您可以使用{%--%}代替正常的打开和关闭块标签来分别抑制块标签前后空白。根据您的需要,您可以使用一个或两个同时使用。{{--}}分隔符也适用于表达式输出标签,而{#--#}分隔符适用于注释标签。

以下是一个空白控制的演示

<ul>
    {%- for user in ["Alice", "Bob", "Charlie"] -%}
    <li>{{ user }}</li>
    {%- endfor -%}
</ul>

这将产生一个紧凑的

<ul>
    <li>Alice</li>
    <li>Bob</li>
    <li>Charlie</li>
</ul>

而同样的例子,这次没有使用任何空白控制

<ul>
    {% for user in ["Alice", "Bob", "Charlie"] %}
    <li>{{ user }}</li>
    {% endfor %}
</ul>

这将产生一个非常稀疏的

<ul>

    <li>Alice</li>

    <li>Bob</li>

    <li>Charlie</li>

</ul>

其语义如下

  • {%-{{-{#-分隔符将删除它们左侧的所有空白,直到但不包括遇到的第一个换行符。

  • -%}-}}-#}分隔符将删除它们右侧的所有空白,直到并包括遇到的第一个换行符。

spaceless模板标签

{% spaceless %}
<ul>

        <li>Alice</li>

        <li>Bob</li>

        <li>Charlie</li>

</ul>
{% endspaceless %}

结果

<ul><li>Alice</li><li>Bob</li><li>Charlie</li></ul>

原始输出

有时您需要输出原始文本块,例如代码的情况。您可以使用原始标签

{% raw %}
I'm inside a raw tag
{% this will be printed as is. %}
{% endraw %}

许可证

Flow在MIT许可证下发布。

致谢

Flow在很大程度上基于Armin Ronacher的原始Twig实现,并随后受到Jinja2、Fabien Potencier的Twig分支、PythonRuby的影响。