视图解析器
视图解析器可以对视图文件中的伪变量进行简单的文本替换。它可以解析简单的变量或变量标签对。
伪变量名或控制结构用大括号括起来,如下所示
<html>
<head>
<title>{blog_title}</title>
</head>
<body>
<h3>{blog_heading}</h3>
{blog_entries}
<h5>{title}</h5>
<p>{body}</p>
{/blog_entries}
</body>
</html>
这些变量不是实际的 PHP 变量,而是纯文本表示,允许您从模板(视图文件)中消除 PHP。
注意
CodeIgniter **不需要**您使用此类,因为在您的视图页面中使用纯 PHP(例如使用 视图渲染器)可以让它们运行得更快。但是,一些开发人员更喜欢使用某种模板引擎,如果他们与设计师合作,他们认为设计师会对使用 PHP 感到困惑。
使用视图解析器类
加载解析器类的最简单方法是通过其服务
<?php
$parser = \Config\Services::parser();
或者,如果您没有使用 Parser
类作为您的默认渲染器,您可以直接实例化它
<?php
$parser = new \CodeIgniter\View\Parser();
然后,您可以使用它提供的三种标准渲染方法中的任何一种:render()
、setVar()
和 setData()
。您还可以通过 setDelimiters()
方法直接指定分隔符。
重要
使用 Parser
,您的视图模板仅由解析器本身处理,而不是像传统的视图 PHP 脚本一样。此类脚本中的 PHP 代码被解析器忽略,只执行替换。
这是有目的的:没有 PHP 的视图文件。
它做了什么
Parser
类处理存储在应用程序视图路径中的“PHP/HTML 脚本”。这些脚本不能包含任何 PHP。
每个视图参数(我们称之为伪变量)都会触发一个替换,基于您为其提供的值的类型。伪变量不会被提取到 PHP 变量中;而是通过伪变量语法访问其值,其中其名称在括号内引用。
解析器类在内部使用一个关联数组,来累积伪变量设置,直到您调用其 render()
。这意味着您的伪变量名称需要是唯一的,否则后面的参数设置将覆盖前面的设置。
这也会影响在脚本中不同上下文中转义参数值的方式。您需要为每个转义值提供唯一的参数名称。
解析器模板
您可以使用 render()
方法解析(或渲染)简单的模板,如下所示
<?php
$data = [
'blog_title' => 'My Blog Title',
'blog_heading' => 'My Blog Heading',
];
return $parser->setData($data)->render('blog_template');
视图参数作为关联数组传递给 setData()
,用于替换模板中的数据。在上面的示例中,模板将包含两个变量:{blog_title}
和 {blog_heading}
。传递给 render()
的第一个参数包含 视图文件 的名称,其中 blog_template 是您的视图文件的名称。
重要
如果省略文件扩展名,则预计视图以 .php 扩展名结尾。
解析器配置选项
可以将多个选项传递给 render()
或 renderString()
方法。
cache
- 保存视图结果的时间(以秒为单位);对于 renderString() 忽略。cache_name
- 用于保存/检索缓存的视图结果的 ID;默认为视图路径;对于 renderString() 忽略。saveData
- 如果应保留视图数据参数以供后续调用;默认值为 **true**。cascadeData
- 如果应将伪变量设置传递给嵌套替换;默认值为 **true**。
<?php
return $parser->render('blog_template', [
'cache' => HOUR,
'cache_name' => 'something_unique',
]);
替换变体
支持三种类型的替换:简单、循环和嵌套。替换按添加伪变量的相同顺序执行。
解析器执行的 **简单替换** 是对伪变量的一对一替换,其中对应的数据参数具有标量或字符串值,如以下示例所示
<?php
$template = '<head><title>{blog_title}</title></head>';
$data = ['blog_title' => 'My ramblings'];
return $parser->setData($data)->renderString($template);
// Result: <head><title>My ramblings</title></head>
解析器通过“变量对”将替换功能扩展到更深层次,用于嵌套替换或循环,并提供一些用于条件替换的高级构造。
解析器执行时,通常会
处理所有条件替换
处理所有嵌套/循环替换
处理剩余的单个替换
循环替换
当伪变量的值是一个顺序数组,例如行设置数组时,就会发生循环替换。
上面的示例代码允许替换简单的变量。如果想要重复整个变量块,并且每次迭代都包含新的值,该怎么办?考虑我们在页面顶部展示的模板示例
<html>
<head>
<title>{blog_title}</title>
</head>
<body>
<h3>{blog_heading}</h3>
{blog_entries}
<h5>{title}</h5>
<p>{body}</p>
{/blog_entries}
</body>
</html>
在上面的代码中,您会注意到一对变量:{blog_entries}
数据… {/blog_entries}
。在这种情况下,这两对变量之间的数据块将被重复多次,对应于参数数组中“blog_entries”元素的行数。
解析变量对使用与解析单个变量相同的代码,只是您需要添加一个与变量对数据相对应的多维数组。考虑以下示例
<?php
$data = [
'blog_title' => 'My Blog Title',
'blog_heading' => 'My Blog Heading',
'blog_entries' => [
['title' => 'Title 1', 'body' => 'Body 1'],
['title' => 'Title 2', 'body' => 'Body 2'],
['title' => 'Title 3', 'body' => 'Body 3'],
['title' => 'Title 4', 'body' => 'Body 4'],
['title' => 'Title 5', 'body' => 'Body 5'],
],
];
return $parser->setData($data)->render('blog_template');
伪变量blog_entries
的值是一个顺序的关联数组。外部级别没有与每个嵌套“行”关联的键。
如果您的“对”数据来自数据库结果,该结果已经是多维数组,您可以直接使用数据库的getResultArray()
方法
<?php
$query = $db->query('SELECT * FROM blog');
$data = [
'blog_title' => 'My Blog Title',
'blog_heading' => 'My Blog Heading',
'blog_entries' => $query->getResultArray(),
];
return $parser->setData($data)->render('blog_template');
如果您尝试循环遍历的数组包含对象而不是数组,解析器将首先在对象上查找asArray()
方法。如果存在,该方法将被调用,然后对返回的数组进行循环,就像上面描述的那样。如果不存在asArray()
方法,对象将被转换为数组,并且它的公共属性将被提供给解析器。
这在 Entity 类中特别有用,Entity 类有一个asArray()
方法,该方法返回所有公共和受保护的属性(减去 _options 属性),并将它们提供给解析器。
嵌套替换
当伪变量的值是一个关联数组时,就会发生嵌套替换,例如来自数据库的记录
<?php
$data = [
'blog_title' => 'My Blog Title',
'blog_heading' => 'My Blog Heading',
'blog_entries' => [
[
'title' => 'Title 1',
'body' => 'Body 1',
],
],
];
return $parser->setData($data)->render('blog_template');
伪变量blog_entries
的值是一个关联数组。在该变量的变量对循环中,将公开其中定义的键值对。
一个可能适用于上述情况的blog_template.php
<h1>{blog_title} - {blog_heading}</h1>
{blog_entries}
<div>
<h2>{title}</h2>
<p>{body}</p>
</div>
{/blog_entries}
如果您希望在 blog_entries
范围内访问其他伪变量,请确保 cascadeData
选项设置为 true。
级联数据
在嵌套和循环替换中,您可以选择将数据对级联到内部替换中。
以下示例不受级联影响
<?php
$template = '{name} lives in {locations}{city} on {planet}{/locations}.';
$data = [
'name' => 'George',
'locations' => [
['city' => 'Red City', 'planet' => 'Mars'],
],
];
return $parser->setData($data)->renderString($template);
// Result: George lives in Red City on Mars.
此示例根据级联给出不同的结果
<?php
$template = '{locations}{name} lives in {city} on {planet}{/locations}.';
$data = [
'name' => 'George',
'locations' => [
['city' => 'Red City', 'planet' => 'Mars'],
],
];
return $parser->setData($data)->renderString($template, ['cascadeData' => false]);
// Result: {name} lives in Red City on Mars.
// or
return $parser->setData($data)->renderString($template, ['cascadeData' => true]);
// Result: George lives in Red City on Mars.
防止解析
您可以使用 {noparse}
{/noparse}
标签对指定页面中不需要解析的部分。此部分中的任何内容都将保持原样,不会进行变量替换、循环等操作。
{noparse}
<h1>Untouched Code</h1>
{/noparse}
条件逻辑
解析器类支持一些基本的条件语句来处理 if
、else
和 elseif
语法。所有 if
块必须用 endif
标签关闭
{if $role=='admin'}
<h1>Welcome, Admin!</h1>
{endif}
此简单块在解析过程中转换为以下内容
<?php if ($role === 'admin'): ?>
<h1>Welcome, Admin!</h1>
<?php endif ?>
在 if 语句中使用的所有变量必须事先使用相同的名称设置。除此之外,它与标准 PHP 条件语句完全相同,所有标准 PHP 规则都适用。您可以使用任何您通常使用的比较运算符,例如 ==
、===
、!==
、<
、>
等。
{if $role=='admin'}
<h1>Welcome, Admin</h1>
{elseif $role=='moderator'}
<h1>Welcome, Moderator</h1>
{else}
<h1>Welcome, User</h1>
{endif}
警告
在后台,条件语句使用 eval()
解析,因此您必须确保小心使用条件语句中使用的用户数据,否则可能会使您的应用程序面临安全风险。
更改条件分隔符
如果您的模板中有以下 JavaScript 代码,解析器会引发语法错误,因为存在可以解释为条件语句的字符串
<script type="text/javascript">
var f = function() {
if (hasAlert) {
alert('{message}');
}
}
</script>
在这种情况下,您可以使用 setConditionalDelimiters()
方法更改条件语句的分隔符,以避免误解
<?php
$parser->setConditionalDelimiters('{%', '%}');
在这种情况下,您将在模板中编写代码
{% if $role=='admin' %}
<h1>Welcome, Admin</h1>
{% else %}
<h1>Welcome, User</h1>
{% endif %}
转义数据
默认情况下,所有变量替换都会被转义,以帮助防止您的页面遭受 XSS 攻击。CodeIgniter 的 esc()
方法支持几种不同的上下文,例如通用 html
、在 HTML attr
中、在 css
中等等。如果没有指定其他内容,则数据将被假定为在 HTML 上下文中。您可以使用 esc()
过滤器来指定使用的上下文。
{ user_styles | esc(css) }
<a href="{ user_link | esc(attr) }">{ title }</a>
有时您绝对需要使用某些内容并且不希望它被转义。您可以通过在开始和结束大括号中添加感叹号来实现这一点。
{! unescaped_var !}
过滤器
任何单个变量替换都可以应用一个或多个过滤器来修改其呈现方式。这些过滤器并非旨在彻底改变输出,而是提供了一种方法来重复使用相同的变量数据,但以不同的方式呈现。上面讨论的 esc
过滤器就是一个例子。日期是另一个常见的用例,您可能需要在同一页面的多个部分以不同的格式显示相同的数据。
过滤器是伪变量名称后面的命令,并用管道符号 |
分隔。
// -55 is displayed as 55
{ value|abs }
如果参数需要任何参数,则必须用逗号分隔并用括号括起来。
{ created_at|date(Y-m-d) }
可以通过将多个过滤器串联起来,将多个过滤器应用于值。它们按从左到右的顺序处理。
{ created_at|date_modify(+5 days)|date(Y-m-d) }
提供的过滤器
使用解析器时,可以使用以下过滤器。
过滤器 |
参数 |
描述 |
示例 |
---|---|---|---|
abs |
显示数字的绝对值。 |
{ v|abs } |
|
capitalize |
以句子大小写显示字符串:所有字母小写,首字母大写。 |
{ v|capitalize} |
|
date |
格式 (Y-m-d) |
与 PHP date 兼容的格式字符串。 |
{ v|date(Y-m-d) } |
date_modify |
要添加/减去的数值 |
一个与 **strtotime** 兼容的字符串,用于修改日期,例如 |
{ v|date_modify(+1 day) } |
默认 |
默认值 |
如果变量为空,则显示默认值。 empty(). |
{ v|default(just in case) } |
转义 |
html, attr, css, js |
指定要转义数据的上下文。 |
{ v|esc(attr) } |
摘录 |
短语,半径 |
返回给定短语中一定半径内的文字。与 **excerpt** 辅助函数相同。 |
{ v|excerpt(green giant, 20) } |
高亮 |
短语 |
使用 ‘<mark></mark>’ 标签突出显示文本中的给定短语。 |
{ v|highlight(view parser) } |
高亮代码 |
使用 HTML/CSS 突出显示代码示例。 |
{ v|highlight_code } |
|
限制字符 |
限制 |
将字符数量限制为 $limit。 |
{ v|limit_chars(100) } |
限制词语 |
限制 |
将词语数量限制为 $limit。 |
{ v|limit_words(20) } |
本地货币 |
货币,区域设置,小数 |
显示货币的本地化版本。“currency” 值是任何 3 个字母的 ISO 4217 货币代码。 |
{ v|local_currency(EUR,en_US) } |
本地数字 |
类型,精度,区域设置 |
显示数字的本地化版本。“type” 可以是以下之一:decimal,currency,percent,scientific,spellout,ordinal,duration。 |
{ v|local_number(decimal,2,en_US) } |
小写 |
将字符串转换为小写。 |
{ v|lower } |
|
nl2br |
将所有换行符 (n) 替换为 HTML <br/> 标签。 |
{ v|nl2br } |
|
数字格式 |
位数 |
包装 PHP **number_format** 函数以在解析器中使用。 |
{ v|number_format(3) } |
散文 |
获取一段文本,并使用 **auto_typography()** 方法将其转换为更漂亮、更易读的散文。 |
{ v|prose } |
|
舍入 |
位数,类型 |
将数字舍入到指定位数。可以传递 **ceil** 和 **floor** 类型以使用这些函数。 |
{ v|round(3) } { v|round(ceil) } |
去除标签 |
允许的字符 |
包装 PHP **strip_tags**。可以接受允许的标签字符串。 |
{ v|strip_tags(<br>) } |
标题 |
显示字符串的“标题大小写”版本,所有字母小写,每个单词首字母大写。 |
{ v|title } |
|
大写 |
将字符串显示为全部大写。 |
{ v|upper } |
有关“local_number”过滤器的详细信息,请参阅 PHP 的 NumberFormatter。
自定义过滤器
您可以通过编辑 app/Config/View.php 并向 $filters
数组添加新条目来轻松创建自己的过滤器。每个键都是过滤器在视图中调用的名称,其值是任何有效的 PHP 可调用函数。
<?php
namespace Config;
use CodeIgniter\Config\View as BaseView;
class View extends BaseView
{
public $filters = [
'foo' => '\Some\Class::methodName',
'str_repeat' => 'str_repeat', // native php function
];
// ...
}
解析器插件
插件允许您扩展解析器,为每个项目添加自定义功能。它们可以是任何 PHP 可调用函数,使其非常易于实现。在模板中,插件由 {+ +}
标签指定。
{+ foo +} inner content {+ /foo +}
此示例显示了一个名为 foo 的插件。它可以操作其开始和结束标签之间的任何内容。在此示例中,它可以与文本“ inner content ”一起使用。插件在任何伪变量替换发生之前进行处理。
虽然插件通常由标签对组成,如上所示,但它们也可以是单个标签,没有结束标签。
{+ foo +}
开始标签还可以包含参数,这些参数可以自定义插件的工作方式。参数表示为键值对。
{+ foo bar=2 baz="x y" +}
参数也可以是单个值。
{+ include somefile.php +}
提供的插件
使用解析器时,可以使用以下插件。
插件 |
参数 |
描述 |
示例 |
---|---|---|---|
current_url |
当前_url 辅助函数的别名。 |
{+ current_url +} |
|
previous_url |
previous_url 辅助函数的别名。 |
{+ previous_url +} |
|
siteURL |
site_url 辅助函数的别名。 |
{+ siteURL “login” +} |
|
mailto |
电子邮件、标题、属性 |
mailto 辅助函数的别名。 |
{+ mailto email=foo@example.com title=”Stranger Things” +} |
safe_mailto |
电子邮件、标题、属性 |
safe_mailto 辅助函数的别名。 |
{+ safe_mailto email=foo@example.com title=”Stranger Things” +} |
lang |
语言字符串 |
lang 辅助函数的别名。 |
{+ lang number.terabyteAbbr +} |
validation_errors |
fieldname(可选) |
返回字段的错误字符串(如果指定)或所有验证错误。 |
{+ validation_errors +} , {+ validation_errors field=”email” +} |
route |
路由名称 |
route_to 辅助函数的别名。 |
{+ route “login” +} |
csp_script_nonce |
csp_script_nonce 辅助函数的别名。 |
{+ csp_script_nonce +} |
|
csp_style_nonce |
csp_style_nonce 辅助函数的别名。 |
{+ csp_style_nonce +} |
注册插件
最简单的方法是,将新插件添加到 **app/Config/View.php** 中的 $plugins
数组中,使其可以使用。键是模板文件中使用的插件名称。值可以是任何有效的 PHP 可调用函数,包括静态类方法
<?php
namespace Config;
use CodeIgniter\Config\View as BaseView;
class View extends BaseView
{
public $plugins = [
'foo' => '\Some\Class::methodName',
];
// ...
}
您也可以使用闭包,但这些闭包只能在配置文件的构造函数中定义
<?php
namespace Config;
use CodeIgniter\Config\View as BaseView;
class View extends BaseView
{
public $plugins = [];
public function __construct()
{
$this->plugins['bar'] = static fn (array $params = []) => $params[0] ?? '';
parent::__construct();
}
// ...
}
如果可调用函数是单独的,则它被视为单个标签,而不是打开/关闭标签。它将被插件的返回值替换
<?php
namespace Config;
use CodeIgniter\Config\View as BaseView;
class View extends BaseView
{
public $plugins = [
'foo' => '\Some\Class::methodName',
];
// ...
}
/*
* Tag is replaced by the return value of Some\Class::methodName() static function.
* {+ foo +}
*/
如果可调用函数被包装在一个数组中,则它被视为一个打开/关闭标签对,可以对标签之间的任何内容进行操作
<?php
namespace Config;
use CodeIgniter\Config\View as BaseView;
class View extends BaseView
{
public $plugins = [
'foo' => ['\Some\Class::methodName'],
];
// ...
}
// {+ foo +} inner content {+ /foo +}
使用说明
如果您包含模板中未引用的替换参数,则会忽略它们
<?php
$template = 'Hello, {firstname} {lastname}';
$data = [
'title' => 'Mr',
'firstname' => 'John',
'lastname' => 'Doe',
];
return $parser->setData($data)->renderString($template);
// Result: Hello, John Doe
如果您未包含模板中引用的替换参数,则结果中将显示原始伪变量
<?php
$template = 'Hello, {firstname} {initials} {lastname}';
$data = [
'title' => 'Mr',
'firstname' => 'John',
'lastname' => 'Doe',
];
return $parser->setData($data)->renderString($template);
// Result: Hello, John {initials} Doe
如果您在期望数组的情况下提供字符串替换参数,例如,对于变量对,则替换将对打开的变量对标签进行,但关闭的变量对标签不会正确渲染
<?php
$template = 'Hello, {firstname} {lastname} ({degrees}{degree} {/degrees})';
$data = [
'degrees' => 'Mr',
'firstname' => 'John',
'lastname' => 'Doe',
'titles' => [
['degree' => 'BSc'],
['degree' => 'PhD'],
],
];
return $parser->setData($data)->renderString($template);
// Result: Hello, John Doe (Mr{degree} {/degrees})
视图片段
您不必使用变量对来实现视图中的迭代效果。可以使用视图片段来代替变量对中的内容,并在控制器中控制迭代,而不是在视图中。
在视图中控制迭代的示例
$template = '<ul>{menuitems}
<li><a href="{link}">{title}</a></li>
{/menuitems}</ul>';
$data = [
'menuitems' => [
['title' => 'First Link', 'link' => '/first'],
['title' => 'Second Link', 'link' => '/second'],
]
];
return $parser->setData($data)->renderString($template);
结果
<ul>
<li><a href="/first">First Link</a></li>
<li><a href="/second">Second Link</a></li>
</ul>
在控制器中控制迭代的示例,使用视图片段
<?php
$temp = '';
$template1 = '<li><a href="{link}">{title}</a></li>';
$data1 = [
['title' => 'First Link', 'link' => '/first'],
['title' => 'Second Link', 'link' => '/second'],
];
foreach ($data1 as $menuItem) {
$temp .= $parser->setData($menuItem)->renderString($template1);
}
$template2 = '<ul>{menuitems}</ul>';
$data = [
'menuitems' => $temp,
];
return $parser->setData($data)->renderString($template2);
结果
<ul>
<li><a href="/first">First Link</a></li>
<li><a href="/second">Second Link</a></li>
</ul>
类参考
- class CodeIgniter\View\Parser
- render($view[, $options[, $saveData]])
- 参数:
$view (
string
) – 视图源文件的名称$options (
array
) – 选项数组,以键值对形式$saveData (
boolean
) – 如果为 true,将保存数据以供其他调用使用,如果为 false,将在渲染视图后清除数据。
- 返回值:
所选视图的渲染文本
- 返回类型:
string
根据文件名和已设置的任何数据构建输出
<?php return $parser->render('myview');
支持的选项
cache
- 视图结果保存的时间(以秒为单位)cache_name
- 用于保存/检索缓存视图结果的 ID;默认为视图路径cascadeData
- 如果嵌套或循环替换发生时生效的数据对应该被传播saveData
- 如果视图数据参数应该保留以供后续调用使用
首先执行所有条件替换,然后对每个数据对执行剩余的替换。
- renderString($template[, $options[, $saveData]])
- 参数:
$template (
string
) – 作为字符串提供的视图源$options (
array
) – 选项数组,以键值对形式$saveData (
boolean
) – 如果为 true,将保存数据以供其他调用使用,如果为 false,将在渲染视图后清除数据。
- 返回值:
所选视图的渲染文本
- 返回类型:
string
根据提供的模板源和已设置的任何数据构建输出
<?php return $parser->renderString('<ul><li>Item 1</li><li>Item 2</li></ul>');
支持的选项和行为,如上所述。
- setData([$data[, $context = null]])
- 参数:
$data (
array
) – 视图数据字符串数组,以键值对形式$context (
string
) – 用于数据转义的上下文。
- 返回值:
渲染器,用于方法链
- 返回类型:
CodeIgniter\View\RendererInterface。
一次设置多个视图数据。
<?php $parser->setData(['name' => 'George', 'position' => 'Boss']);
支持的转义上下文:html、css、js、url、attr 或 raw。如果为“raw”,则不会进行转义。
- setVar($name[, $value = null[, $context = null]])
- 参数:
$name (
string
) – 视图数据变量的名称$value (
mixed
) – 此视图数据的的值$context (
string
) – 用于数据转义的上下文。
- 返回值:
渲染器,用于方法链
- 返回类型:
CodeIgniter\View\RendererInterface。
设置单个视图数据。
<?php $parser->setVar('name', 'Joe', 'html');
支持的转义上下文:html、css、js、url、attr 或 raw。如果为“raw”,则不会进行转义。
- setDelimiters($leftDelimiter = '{', $rightDelimiter = '}')
- 参数:
$leftDelimiter (
string
) – 替换字段的左定界符$rightDelimiter (
string
) – 替换字段的右定界符
- 返回值:
渲染器,用于方法链
- 返回类型:
CodeIgniter\View\RendererInterface。
覆盖替换字段定界符。
<?php $parser->setDelimiters('[', ']');
- setConditionalDelimiters($leftDelimiter = '{', $rightDelimiter = '}')
- 参数:
$leftDelimiter (
string
) – 条件语句的左定界符$rightDelimiter (
string
) – 条件语句的右定界符
- 返回值:
渲染器,用于方法链
- 返回类型:
CodeIgniter\View\RendererInterface。
覆盖条件语句定界符。
<?php $parser->setConditionalDelimiters('{%', '%}');
注释
您可以在模板中放置注释,这些注释在解析过程中会被忽略并删除,方法是在注释周围使用
{# #}
符号。