risor/lex

轻量级模板解析器。

维护者

详细信息

gitlab.azmi.pl/Viperoo/lex.git

2.3.2 2014-02-07 03:39 UTC

This package is not auto-updated.

Last update: 2024-09-22 09:13:35 UTC


README

Build Status

Lex 是一个轻量级模板解析器。

Lex 在 MIT 许可证下发布,版权所有 2011 - 2014 PyroCMS 团队。

变更日志

2.3.2

  • 仅在 parser() 方法开始处使用 ->toArray() 转换对象。
  • 尽管我们希望告别 PHP 5.3,但我们现在将其带回。

2.3.1

  • 添加了 ArrayableInterface,它将对象自动转换为数组。这允许通过添加 ->toArray() 方法来定义对象如何转换为数组。
  • 现在检查循环数据是否为数组或实现了 \IteratorAggregate。这防止了“foreach 中的无效参数”错误,同时允许遍历数组和集合对象。
  • 停止支持 PHP 5.3

2.3.0

  • 添加了对自闭合回调标签的支持(例如,{{ foo.bar /}} 而不是 {{ foo.bar }}{{ /foo.bar }})。
  • 撤销了对回调标签贪婪性的更改。回调标签现在又变得贪婪了。如果您想在同一个模板中使用单标签和块标签,则必须关闭单标签。

2.2.3

  • 修复了导致所有回调都被处理为单标签的问题,即使它们是块标签。

2.2.2

  • 修复了 #7 - 循环数据标签内的条件现在按预期工作。

2.2.1

  • 将 injectNoparse 更改为静态。

2.2.0

  • 修复了一个仅适用于 PHP 5.4 的测试。
  • 添加了 PHPUnit 作为 composer 开发要求。
  • 添加了一个 Lex\ParsingException 类,当发生解析异常时抛出。

2.1.1

  • 修复了一个问题,其中回调函数内部字符串返回值被错误处理,导致条件总是失败。

2.1.0

  • 条件中的未定义变量现在评估为 NULL,因此 {{ if foo }} 现在可以正常工作。
  • 添加了 exists 关键字。
  • 添加了 not 关键字。

2.0.3

  • 修复了 composer 自动加载。
  • 将类移动到 lib 文件夹。

2.0.2

  • 修复了 2.0.1 版本中引入的一个错误,其中 NULL 变量没有被显示。

2.0.1

  • 修复了变量具有“假值”(例如 0、“0”、“-1”等)时未显示的问题。

2.0.0

  • 所有代码现在遵循 PSR-0、1 和 2。
  • Lex_Parser 已移动到 Lex 命名空间并重命名为 Parser
  • Lex_Autoloader 已删除。它现在符合 PSR-0。
  • 添加了对 {{ unless }}{{ elseunless }} 的支持。

基本用法

使用 Lex

Lex 是一个名为 pyrocms/lex 的 Composer 包。要使用它,只需将其添加到您的 composer.json 文件的 require 部分即可。

{
    "require": {
        "pyrocms/lex": "2.2.*"
    }
}

在将 Lex 添加到您的 composer.json 文件后,只需像平常一样使用该类即可。

$parser = new Lex\Parser();

使用 Lex

基本文件解析

$parser = new Lex\Parser();
$template = $parser->parse(file_get_contents('template.lex'), $data);

您还可以设置作用域粘合剂(见下文“作用域粘合剂”)

$parser = new Lex\Parser();
$parser->scopeGlue(':');
$template = $parser->parse(file_get_contents('template.lex'), $data);

要允许无解析提取累积,以便它们不会被解析器后续调用解析,请将 cumulativeNoparse 设置为 true

$parser = new Lex\Parser();
$parser->cumulativeNoparse(true);
$template = $parser->parse(file_get_contents('template.lex'), $data);
// Second parse on the same text somewhere else in your app
$template = $parser->parse($template, $data);
// Now that all parsing is done we inject the contents between the {{ noparse }} tags back into the template text
Lex\Parser::injectNoparse($template);

如果您只想解析数据数组而不用担心回调标签或注释,则可以使用 parseVariables() 方法

$parser = new Lex\Parser();
$template = $parser->parseVariables(file_get_contents('template.lex'), $data);

模板中的 PHP

默认情况下,PHP 被编码而不是执行。这是出于安全原因。但是,有时您可能想启用它。为此,只需将 true 作为第四个参数发送到您的 parse() 调用即可。

$parser = new Lex\Parser();
$template = $parser->parse(file_get_contents('template.lex'), $data, $callback, true);

语法

通用

所有 Lex 代码都由双大括号 ({{ }}) 分隔。这些分隔符被选择以减少与 JavaScript 和 CSS 冲突的机会。

以下是一些 Lex 模板代码的示例

Hello, {{name}}

作用域粘合剂

Scope Glue 是 Lex 用于触发作用域变化的字符。作用域变化是指在例如,你访问数组/对象内的嵌套变量或作用域自定义回调标签时发生的事情。

默认情况下,点(.)用作 Scope Glue,尽管你可以选择任何字符。

设置 Scope Glue

$parser->scopeGlue(':');

空白字符

允许在分隔符前后有空格,但在某些情况下,标签内的空格是禁止的(以下章节将解释)。

一些有效示例

{{ name }}
{{name }}
{{ name}}
{{  name  }}
{{
  name
}}

一些无效示例

{{ na me }}
{ {name} }

注释

你可以通过在文本中包裹 {{# #}} 来给你的模板添加注释。

示例

{{# This will not be parsed or shown in the resulting HTML #}}

{{#
    They can be multi-line too.
#}}

防止解析

你可以通过在 {{ noparse }}{{ /noparse }} 标签中包裹代码块来防止解析器解析代码块。

示例

{{ noparse }}
    Hello, {{ name }}!
{{ /noparse }}

变量标签

处理变量时,你可以:访问单个变量,访问数组/对象内深度嵌套的变量,以及遍历数组。你甚至可以遍历嵌套数组。

简单变量标签

在我们的基本示例中,假设你有一个以下变量的数组(发送给解析器)

array(
    'title'     => 'Lex is Awesome!',
    'name'      => 'World',
    'real_name' => array(
        'first' => 'Lex',
        'last'  => 'Luther',
    )
)

基本示例

{{# Parsed: Hello, World! #}}
Hello, {{ name }}!

{{# Parsed: <h1>Lex is Awesome!</h1> #}}
<h1>{{ title }}</h1>

{{# Parsed: My real name is Lex Luther!</h1> #}}
My real name is {{ real_name.first }} {{ real_name.last }}

{{ real_name.first }}{{ real_name.last }} 标签会检查 real_name 是否存在,然后检查 firstlast 是否分别存在于 real_name 数组/对象中,然后返回它。

循环变量标签

循环变量标签就像简单变量标签一样,只是它们对应于数组数组的数组/对象,这将进行循环。

循环变量标签是一个闭合标签,它包裹循环内容。关闭标签必须与打开标签完全匹配,但它必须以正斜杠(/)为前缀。在正斜杠和标签名称之间不能有空白(正斜杠前的空白是允许的)。

有效示例

{{ projects }} Some Content Here {{ /projects }}

无效示例

{{ projects }} Some Content Here {{/ projects }}

循环内容是打开和关闭标签之间的内容。这个内容将被循环输出,每次循环数组的每个项目。

当你处于循环标签内时,你可以访问循环当前元素的任何子变量。

在以下示例中,假设你有一个以下变量数组/对象的数组

array(
    'title'     => 'Current Projects',
    'projects'  => array(
        array(
            'name' => 'Acme Site',
            'assignees' => array(
                array('name' => 'Dan'),
                array('name' => 'Phil'),
            ),
        ),
        array(
            'name' => 'Lex',
            'contributors' => array(
                array('name' => 'Dan'),
                array('name' => 'Ziggy'),
				array('name' => 'Jerel')
            ),
        ),
    ),
)

在模板中,我们希望显示标题,然后显示项目及其分配者。

<h1>{{ title }}</h1>
{{ projects }}
    <h3>{{ name }}</h3>
    <h4>Assignees</h4>
    <ul>
    {{ assignees }}
        <li>{{ name }}</li>
    {{ /assignees }}
    </ul>
{{ /projects }}

正如你所见,在项目元素内部,我们可以访问该项目的分配者。你还可以看到,你可以像访问任何其他数组一样遍历子值。

条件语句

Lex 中的条件语句简单易用。它允许使用标准的 ifelseifelse,但它还添加了 unlesselseunless

unlesselseunless 与使用 {{ if ! (expression) }}{{ elseif ! (expression) }} 完全相同。它们被添加为一个更简洁、更易于理解的语法。

所有 if 块都必须用 {{ endif }} 标签关闭。

if 条件语句中的变量不应使用标签分隔符(这会导致输出中出现奇怪的问题)。

条件语句可以包含你会在 PHP 中使用的任何比较运算符(==!====!==><<=>=)。你还可以使用任何逻辑运算符(!not||&&andor)。

示例

{{ if show_name }}
    <p>My name is {{real_name.first}} {{real_name.last}}</p>
{{ endif }}

{{ if user.group == 'admin' }}
    <p>You are an Admin!</p>
{{ elseif user.group == 'user' }}
    <p>You are a normal User.</p>
{{ else }}
    <p>I don't know what you are.</p>
{{ endif }}

{{ if show_real_name }}
    <p>My name is {{real_name.first}} {{real_name.last}}</p>
{{ else }}
    <p>My name is John Doe</p>
{{ endif }}

{{ unless age > 21 }}
    <p>You are to young.</p>
{{ elseunless age < 80 }}
    <p>You are to old...it'll kill ya!</p>
{{ else }}
    <p>Go ahead and drink!</p>
{{ endif }}

not 运算符

not 运算符相当于使用 ! 运算符。它们可以完全互换(实际上,not 在编译前被翻译为 !)。

条件语句中的未定义变量

条件语句中的未定义变量将被评估为 null。这意味着你可以做类似 {{ if foo }} 的事情,而不用担心变量是否已定义。

检查变量是否存在

要检查一个变量是否在条件语句中存在,请使用 exists 关键字。

示例

{{ if exists foo }}
    Foo Exists
{{ elseif not exists foo }}
    Foo Does Not Exist
{{ endif }}

您还可以将其与其他条件组合使用

{{ if exists foo and foo !== 'bar' }}
    Something here
{{ endif }}

表达式 exists foo 的值将为 truefalse。因此,以下用法也是可行的

{{ if exists foo == false }}
{{ endif }}

条件语句中的回调标签

在条件语句中使用回调标签很简单。就像使用其他任何变量一样使用它,除了一个例外。当您需要为回调标签提供属性时,您必须用一对 单层 大括号将标签括起来(您可以选择为所有回调标签都这样做)。

注意:当在条件语句中使用大括号时,大括号后面不能有任何空白,或者回调标签关闭大括号前面也不能有任何空白。这样做会导致错误。

示例

{{ if user.logged_in }} {{ endif }}

{{ if user.logged_in and {user.is_group group="admin"} }} {{ endif }}

回调标签

回调标签允许您具有具有通过回调发送的属性的标签。这使得创建一个良好的插件系统变得容易。

以下是一个示例

{{ template.partial name="navigation" }}

您还可以关闭标签以使其成为 回调块

{{ template.partial name="navigation" }}
{{ /template.partial }}

注意,属性不是必需的。如果没有提供属性,则标签将首先被检查是否是数据变量,然后将其作为回调执行。

{{ template.partial }}

回调

回调可以是任何有效的PHP可调用函数。它作为第三个参数发送到 parse() 方法。

$parser->parse(file_get_contents('template.lex'), $data, 'my_callback');

回调必须接受以下3个参数(按此顺序)

$name - The name of the callback tag (it would be "template.partial" in the above examples)
$attributes - An associative array of the attributes set
$content - If it the tag is a block tag, it will be the content contained, else a blank string

回调还必须返回一个字符串,该字符串将替换内容中的标签。

示例

function my_callback($name, $attributes, $content)
{
    // Do something useful
    return $result;
}

关闭回调标签

如果回调标签可以使用单标签或块标签形式,那么在以单标签形式使用时,它必须关闭(就像HTML一样)。

示例

{{ foo.bar.baz }}{{ /foo.bar.baz }}

{{ foo.bar.baz }}
    Content
{{ /foo.bar.baz }}

自闭合回调标签

您可以使用自闭合标签来缩短上述内容,就像在HTML中一样。您只需在标签的末尾放置一个 //} 之间不能有空格)。

示例

{{ foo.bar.baz /}}

{{ foo.bar.baz }}
    Content
{{ /foo.bar.baz }}

递归回调块

递归回调标签允许您循环遍历与主块相同输出的子元素。它通过使用与数组键名称一起的 recursive 关键字触发。这两个词必须像以下示例中那样用星号括起来。

示例

function my_callback($name, $attributes, $content)
{
	$data = array(
			'url' 		=> 'url_1',
			'title' 	=> 'First Title',
			'children'	=> array(
				array(
					'url' 		=> 'url_2',
					'title'		=> 'Second Title',
					'children' 	=> array(
						array(
							'url' 	=> 'url_3',
							'title'	=> 'Third Title'
						)
					)
				),
				array(
					'url'		=> 'url_4',
					'title'		=> 'Fourth Title',
					'children'	=> array(
						array(
							'url' 	=> 'url_5',
							'title'	=> 'Fifth Title'
						)
					)
				)
			)
	);

	$parser = new Lex\Parser();
	return $parser->parse($content, $data);
}

在模板中按照以下方式设置。如果 children 不为空,Lex 将再次解析 {{ navigation }} 标签之间的内容,为 children 的每个数组执行。然后将生成的文本插入到 {{ *recursive children* }} 的位置。这可以做到多层。

<ul>
	{{ navigation }}
		<li><a href="{{ url }}">{{ title }}</a>
			{{ if children }}
				<ul>
					{{ *recursive children* }}
				</ul>
			{{ endif }}
		</li>
	{{ /navigation }}
</ul>

结果

<ul>
	<li><a href="url_1">First Title</a>
		<ul>
			<li><a href="url_2">Second Title</a>
				<ul>
					<li><a href="url_3">Third Title</a></li>
				</ul>
			</li>

			<li><a href="url_4">Fourth Title</a>
				<ul>
					<li><a href="url_5">Fifth Title</a></li>
				</ul>
			</li>
		</ul>
	</li>
</ul>