vanilla / ebi
使用 HTML 制作的模板引擎。
Requires
- php: >=5.6.0
- symfony/expression-language: ^103.2.8
Requires (Dev)
- bobthecow/faker: dev-master
- symfony/yaml: ^3.2
- vanilla/htmlawed: v2.2.4.1
README
Ebi 模板语言使用基本的 HTML 和特殊属性,是一种简单而强大的模板语言。
模板的基本原理
通常,您编写正常的 HTML。数据通过在 {...}
之间包含来包含在模板中。其他特殊功能通过特殊的模板属性添加。
通过将数据放在 {...}
括号之间包含在模板中。这被称为“插值”,有相当多的选项。
字段
要包含数据中的字段,请使用其名称。
<p>Hello {firstName} {lastName}.</p>
这将包含“firstName”和“lastName”数据库键。您可以通过点分隔字段名称来访问深层嵌套的数组。
<p>Hello {user.firstName} {user.lastName}.</p>
您可能会想在自己的字段名称中放置连字符。然而,这不会按预期工作,因为字段名称被解释为表达式。所以例如 {is-on} 将被解释为 "is" 减去 "on"!
元数据
您可以向所有模板添加全局元数据,然后通过在变量名前放置一个 @
符号来访问它们。
<h1>{@title}</h1>
$ebi = new Ebi(...); $ebi->setMeta('title', 'Welcome to the Page'); $ebi->write(...);
元数据数组是放置与模板数据分开的配置信息的良好位置。由于它是所有组件的全局信息,您可以从任何组件中访问它,而不管其作用域如何。
运算符
在编写字段时,您不仅限于仅使用字段名称。支持相当丰富的表达式语法。
函数
使用 functionName()
语法调用函数。Ebi 提供了一组默认函数,它们映射到 PHP 的标准库。
文字
您还可以在表达式中包含文字。
数据转义
默认情况下,所有变量都会进行 HTML 转义。如果您想返回未转义的 HTML,可以使用 unescape 函数。
<p>{unescape(bodyHtml)}</p>
模板属性
Ebi 的大部分功能都是通过模板属性访问的。这些是添加到模板中任何标签上的 HTML 风格属性,用于添加逻辑。所有 Ebi 属性都以 x-
前缀开头,以便您区分 Ebi 属性和常规 HTML 属性。
选择字母 "X" 是因为 "扩展属性",并受到了 HTTP 标头中相同前缀的启发。
x-if
当条件为真时才显示元素。
<p x-if="empty(items)"> There are no items! </p>
if (empty($props['items'])) { echo "<p>There are no items!</p>"; }
x-else
与 if 元素一起添加 else 元素。
<div x-if="signedIn"> Welcome! </div> <div x-else> Sign in to participate. </div>
if ($props['signedIn']) { echo '<div>Welcome!</div>'; } else { echo '<div>Sign in to participate.</div>'; }
x-each
遍历元素。
<ul x-each="people"> <li>Hi {firstName} {lastName}!</li> </ul>
echo '<ul>'; foreach ($props['people'] as $props1) { echo 'Hi ', $this->escape($props1['firstName']), ' ', $this->escape($props1['lastName']); } echo '</ul>';
x-each x-as
命名迭代器元素,以便您仍然可以引用父元素。
<ul x-each="comments" x-as="i comment"> <li>{name}: {comment.body} #{i}</li> </ul>
echo '<ul>'; foreach ($props['comments'] as $i1 => $props1) { echo '<li>', $this->escape($props['name']), ': ', $this->escape($props1['body']), ' #', $this->escape($i1) '</li>'; } echo '</ul>';
提示:如果您想访问数组的关键字,但仍然想使用点符号访问其值,则可以使用 x-as="key this"
。
迭代器变量
当您指定 x-as 与 x-each 时,您将有权访问三个特殊迭代器变量: index, first, last。
<ul x-each="this" x-as="i item"> <li id="{i}" data-index="{i.index}" class="{{first: i.first, last: i.last}}">{item}</li> </ul>
因此,如果您在 x-as 中指定一个键,您可以将特殊变量作为键的属性来访问。生成的 PHP 代码稍微有点冗长,但足够直观。
echo '<ul>'; $count1 = count($props); $index1 = -1; foreach ($props as $i1 => $props1) { $index1++; $first1 = $index1 === 0; $last1 = $index1 === $count1 - 1; echo '<li', $this->attribute('id', $i1), $this->attribute('data-index', $index1), $this->attribute('class', $this->attributeClass(array("first" => $first1, "last" => $last1))), '>', $this->escape($props1), '</li>'; } echo '</ul>';
x-empty
当没有项目时指定模板。
<ul x-each="messages"> <li>{body}</li> <li x-empty>There are no messages.</li> </ul>
echo '<ul>'; if (empty($props['messages'])) { echo '<li>There are no messages.</li>'; } else { foreach ($props['message'] as $i1 => $props1) { echo '<li>', $this->escape($props1['body']), '</li>'; } } echo '</ul>';
x-with
将项目传递到模板中。
<div x-with="user"> Hello {name}. </div>
$props1 = $props['user']; echo '<div>', 'Hello ', $this->escape($props1['name']), '</div>';
x-with x-as
您可以为使用 x-with
引用的数据提供一个别名,以便您仍然可以在块内访问父数据。这种用法的一个很好的例子是执行一些数据的计算并将它分配给一个变量。
<x x-with="trim(ucfirst(sentence))" x-as="title"><h1 x-if="!empty(title)">{title}</h1></x>
$props1 = trim(ucfirst($props['sentence'])); if (!empty($props1)) { echo $this->escape($props1); }
x-literal
在字面量中不要解析模板。
<code x-literal>Hello <b x-literal>{username}</b></code>
echo '<code>Hello <b x-literal>{username}</b></code>';
x标签
有时您想动态确定标签的名称。这时,x标签
属性就派上用场了。
<x x-tag="'h'~level">{heading}</x>
echo '<h'.$props['level'].'>', $this->escape($props['heading']), '</h'.$props['level'].'>';
使用x标签的条件包装器
如果您的模板中有一个x标签
表达式,其结果为空字符串,则该标签将不会渲染,但标签内容将渲染。这样,您可以使用x标签
来实现条件包装。
如果x标签
表达式计算结果为true
,则将使用其所在的标签。
<p x-tag="true">Hello!</p>
上述示例将只渲染<p>Hello!</p>
。这种表示法在您有一个确定何时渲染包装器的布尔表达式时很有用。
模板标签
大多数Ebi功能使用特殊属性。然而,也有一些特殊标签得到支持。
<script type="ebi">
标签
通常,您通过括号({..}
)包围表达式来编写表达式。然而,括号本身不允许括号字符。它们也不允许多行表达式。当您有这样的表达式时,您可以将其包围在一个<script tpye="ebi">
标签中。
<script type="ebi"> join( "|", [1, 2, 3] ) </script>
echo $this->escape(join('|', [1, 2, 3]);
<script x-unescape>
<script x-unescape>
如果您不想在<script>
标签中转义输出,则添加x-unescape
属性。在这种情况下,您不必包含type="ebi"
。
<script x-unescape>join('>', [1, 2, 3])</script>
echo join('>', [1, 2, 3]);
<script x-as="...">
<script x-as="...">
您还可以使用具有x-as
属性的<script>
标签来创建可以在模板中稍后使用的表达式变量。在这种情况下,您不必包含type="ebi"
。
<script x-as="title">trim(ucfirst(sentence))</script> <h1 x-if="!empty(title)">{title}</h1>
$title = trim(ucfirst($props['sentence'])); if (!empty($title) { echo '<h1>', $this->escape($title), '</h1>'; }
<x>
标签
有时您可能想使用ebi属性,但不想渲染HTML标签。在这种情况下,您可以使用x
标签,它只会渲染其内容。
<x x-if="signedIn">Welcome back</x>
if ($props['signedIn']) { echo 'Welcome back'; }
组件
组件是Ebi的强大功能之一。使用组件,您可以创建可重用的模板,这些模板可以包含在其他模板中。以下是一些组件基础知识
- 每个模板都是一个组件。您还可以在模板中声明其他组件。
- 组件使用小写字母。建议您使用短横线分隔组件名称中的单词。请确保将模板文件命名为小写,以避免与大小写敏感的文件系统发生问题。
- 组件通过声明一个具有组件名称的HTML元素来使用。组件创建自定义标签!
- 您可以使用contributes将数据传递到组件中。如果您想将当前模板的所有数据传递给组件,请使用
x-with
属性。 - 强烈建议您不要使用
x-
前缀命名组件,因为这可能用于未来的功能。
x-component
定义可以在模板稍后使用的组件。
<time x-component="long-date" datetime="{date(date, 'c')}">{date(date, 'r')}</time> <long-date date="{dateInserted}" />
$this->register('long-date', function ($props) { echo '<time datetime="', htmlspecialchars(date($props['date'], 'c')), '">', htmlspecialchars(date($props['date'], 'r')), '</time>'; }); $this->render('long-date', ['date' => $props['dateInserted']]);
组件必须以大写字母开头或包含短横线或点。否则,它们将作为普通HTML标签渲染。
组件数据
默认情况下,组件继承当前作用域的数据。您还可以执行一些其他操作,以将更多数据传递到组件中。
使用x-with
传递数据
如果您想将除当前上下文之外的数据传递到组件中,请使用x-with
属性。
<div class="post post-commment" x-component="Comment"> <img src="{author.photoUrl}" /> <a href="author.url">{author.username}</a> <p>{unescape(body)}</p> </div> <Comment x-with="lastComment" />
x-children
和x-block
您可以在组件内使用块定义自定义内容元素。未命名的块将使用其声明的相同标签。
<!-- Declare the layout component. --> <html x-component="layout"> <head><title x-children="title" /></head> <body> <h1 x-children="title" /> <div class="content" x-children="content" /> </body> </html> <!-- Use the layout component. --> <layout> <x x-block="title">Hello world!</x> <p x-block="content">When you put yourself out there you will always do well.</p> </layout>
当使用组件时,块将被插入到组件中。
<html> <head><title>Hello world!</title> <body> <h1>Hello world!</title> <div class="content"><p>When you put yourself out there you will always do well.</p></div> </body> </html>
提示:您可以使用hasChildren()
函数确定是否将特定块传递到您的组件中。
x-include
有时您想动态包含组件。在这种情况下,您可以使用x-include
属性。
<div x-component="hello">Hello {name}</div> <div x-component="goodbye">Goodbye {name}</div> <x x-include="salutation" />
$this->register('hello', function ($props) { echo 'Hello ', $this->escape($props['name']); }); $this->register('goodbye', function ($props) { echo 'Goodbye ', $this->escape($props['name']); }); $this->write($props['salutation'], $props);
HTML实用工具
属性表达式
当您指定属性值为表达式时,则属性将根据值渲染不同。
示例
以下模板
<input type="checkbox" checked="{true}" /> <input type="checkbox" checked="{false}" /> <span role="checkbox" aria-checked="{true}" />
将生成以下输出
<input type="checkbox" checked /> <input type="checkbox" /> <span role="checkbox" aria-checked="true" />
CSS类属性
当你用一个包含数据的CSS类进行赋值时,你可以传递一个数组或者一个对象。
数组类属性
<p class="{['comment', 'is-default']}">Hello</p>
数组中的所有元素都会被渲染为单独的类。
<p class="comment is-default">Hello</p>
对象类属性
<p class="{{comment: true, 'is-default': isDefault }}">Hello</p>
当你将对象作为类属性传递时,键定义了类名,值定义了是否应该包含。这样你可以从模板的数据中启用/禁用CSS类。
注意上面示例中的双大括号。第一个大括号告诉我们我们正在使用变量插值,第二个大括号将对象包裹在JSON符号中。
样式属性
当你用一个包含数据的样式属性进行赋值时,你可以传递一个关联数组或对象表达式。当你这样做时,键和值将被分别视为CSS属性和值。
<p style="{{'font-family': ['Open Sans', 'Helvetica', 'sans-serif'], 'font-size': '16px'}}">Hello</p>
样式对象将被转换为字符串。
<p style="font-family: 'Open Sans','Helvetica','sans-serif'; font-size: 16px">Hello</p>
注意字体族是如何被赋予一个数组并正确转换为CSS的。
布尔样式值
有一些CSS属性可以接受true或false值
空白符
默认情况下,块级元素周围的空白符会被去除,从而使得HTML输出更加紧凑。
HTML注释
你在模板中声明的任何HTML注释都将作为PHP注释添加到编译后的模板函数中。这对于调试或静态模板生成非常有用。
<!-- Do something. --> <p>wut!?</p>
function ($props) { // Do something. echo '<p>wut!?</p>'; };
在代码中使用Ebi
Ebi类用于编译和渲染Ebi模板。你应该只需要一个类的实例来渲染任何数量的模板。
基本用法
$ebi = new Ebi( new FilesystemLoader('/path/to/templates'), '/path/to/cache' ); $ebi->write('component', $props);
在这个例子中,创建了一个Ebi对象,并将一个基本组件写入输出。