视图解析器

视图解析器可以对视图文件中的伪变量进行简单的文本替换。它可以解析简单的变量或变量标签对。

伪变量名或控制结构用大括号括起来,如下所示

<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。

注释

您可以在模板中放置注释,这些注释在解析过程中会被忽略并删除,方法是在注释周围使用 {#  #} 符号。

{# This comment is removed during parsing. #}
{blog_entry}
    <div>
        <h2>{title}</h2>
        <p>{body}</p>
    </div>
{/blog_entry}

级联数据

在嵌套和循环替换中,您可以选择将数据对级联到内部替换中。

以下示例不受级联影响

<?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}

条件逻辑

解析器类支持一些基本的条件语句来处理 ifelseelseif 语法。所有 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** 兼容的字符串,用于修改日期,例如 +5 day-1 week

{ 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('{%', '%}');