darkstar / view
提供 TemplateView 和 TwoStepView 模式的实现,支持使用 PHP 作为模板语言,同时支持助手和闭包作为模板。
Requires
- php: >=7.2.0
This package is not auto-updated.
Last update: 2024-09-29 02:27:36 UTC
README
这是从 Aura View 分支出来的,已修改为支持 php 7.x
Aura View
此包使用 PHP 本身作为模板语言实现了 TemplateView 和 TwoStepView 模式。它支持基于文件和基于闭包的模板,以及助手和部分。
它继承了如 Savant
、Zend_View
和 Solar_View
等系统。
前言
安装
此库需要 PHP 5.4 或更高版本;原则上我们建议使用最新可用的 PHP 版本。它没有用户空间依赖。
可以通过 Composer 以 aura/view 的形式安装和自动加载。
或者,下载一个版本 或克隆此存储库,然后要求或包含其 autoload.php 文件。
质量
要运行命令行中的单元测试,请发出 composer install
,然后在包根目录中执行 ./vendor/bin/phpunit
。这要求 Composer 可用为 composer
。
此库试图遵守 PSR-1、PSR-2 和 PSR-4。如果您注意到合规性疏忽,请通过拉取请求发送补丁。
社区
要提问、提供反馈或以其他方式与 Aura 社区沟通,请加入我们的 Google Group,关注 @auraphp 或在 Freenode 上与我们一起聊天。
入门
实例化
要实例化一个 View 对象,请使用 ViewFactory
<?php $view_factory = new \Aura\View\ViewFactory; $view = $view_factory->newInstance(); ?>
输出转义
注重安全的观察者会注意到,本文件中的所有示例都使用了手动转义输出。因为此包不特定于任何特定的媒体类型,所以它不包含转义功能。
当您通过模板生成输出时,您必须出于安全目的适当地转义它。这意味着 HTML 模板应使用 HTML 转义,CSS 模板应使用 CSS 转义,XML 模板应使用 XML 转义,PDF 模板应使用 PDF 转义,RTF 模板应使用 RTF 转义,等等。
有关一组良好的 HTML 转义程序,请考虑 Aura.Html。
注册视图模板
现在我们已经有了 View,我们需要将其添加到其视图模板注册表中命名的模板。这些通常是 PHP 文件路径,但模板也可以是闭包。例如
<?php $view_registry = $view->getViewRegistry(); $view_registry->set('browse', '/path/to/views/browse.php'); ?>
browse.php
文件可能看起来像这样
<?php foreach ($this->items as $item) { $id = htmlspecialchars($item['id'], ENT_QUOTES, 'UTF-8'); $name = htmlspecialchars($item['name'], ENT_QUOTES, 'UTF-8'); echo "Item ID #{$id} is '{$name}'." . PHP_EOL; } ?>
请注意,在模板中我们使用echo
,而不是return
。
注意:模板逻辑将在View对象的作用域内执行,这意味着模板代码中的
$this
将引用View对象。对于基于闭包的模板也是如此。
设置数据
我们几乎总是希望在我们的模板中使用动态数据。要将数据集合分配给View,请使用setData()
方法以及一个数组或对象。然后我们可以将数据元素用作View对象的属性。
<?php $view->setData(array( 'items' => array( array( 'id' => '1', 'name' => 'Foo', ), array( 'id' => '2', 'name' => 'Bar', ), array( 'id' => '3', 'name' => 'Baz', ), ) )); ?>
注意:请记住,模板逻辑中的
$this
引用的是View对象,因此分配给View的数据可以像属性一样访问$this
。
setData()
方法将覆盖View对象中所有现有的数据。另一方面,addData()
方法将合并到View对象中现有的数据。
调用一步式视图
现在我们已经注册了一个模板并给View分配了一些数据,我们告诉View要使用哪个模板,然后调用View
<?php $view->setView('browse'); $output = $view->__invoke(); // or just $view() ?>
在这种情况下,$output
可能如下所示
Item #1 is 'Foo'.
Item #2 is 'Bar'.
Item #3 is 'Baz'.
使用子模板(也称为“部分”)
有时我们会想要将模板分成多个部分。我们可以使用主模板代码中的render()
方法来渲染这些“部分”模板片段。
首先,我们将子模板放置在视图注册表中(或者如果它在布局中使用,则放置在布局注册表中)。然后我们从主模板代码中渲染它。子模板可以使用我们喜欢的任何命名方案。一些系统使用在部分模板前加下划线的约定,下面的示例将使用该约定。
其次,我们可以传递一个数组到局部作用域,以便将其提取到部分模板中。(无论怎样,$this
变量总是会可用。)
例如,让我们将browse.php
模板文件拆分为使用显示项的子模板。
<?php // add templates to the view registry $view_registry = $view->getViewRegistry(); // the "main" template $view_registry->set('browse', '/path/to/views/browse.php'); // the "sub" template $view_registry->set('_item', '/path/to/views/_item.php'); ?>
我们将从browse.php
中提取出项显示代码到_item.php
<?php $id = htmlspecialchars($item['id'], ENT_QUOTES, 'UTF-8'); $name = htmlspecialchars($item['name'], ENT_QUOTES, 'UTF-8') echo "Item ID #{$id} is '{$name}'." . PHP_EOL; ?>
然后我们修改browse.php
以使用子模板
<?php foreach ($this->items as $item) { echo $this->render('_item', array( 'item' => $item, )); ?>
输出将与我们在调用视图时之前相同。
注意:作为替代,我们可以使用
include
或require
在当前模板作用域中直接执行PHP文件。
使用部分
部分与子模板(也称为“部分”)类似,但它们是捕获以供以后使用。通常,它们由视图模板用于捕获布局模板的输出。
例如,我们可以在视图模板中捕获输出到命名部分...
<?php // begin buffering output for a named section $this->beginSection('local-nav'); echo "<div>"; // ... echo the local navigation output ... echo "</div>"; // end buffering and capture the output $this->endSection(); ?>
...然后在布局模板中使用该输出
<?php if ($this->hasSection('local-nav')) { echo $this->getSection('local-nav'); } else { echo "<div>No local navigation.</div>"; } ?>
此外,可以使用setSection()
方法直接设置部分主体,而不是捕获它
<?php $this->setSection('local-nav', $this->render('_local-nav')); ?>
使用助手
ViewFactory使用空的HelperRegistry实例化View来管理助手。我们可以通过HelperRegistry注册闭包或其他可调用对象作为助手。然后我们可以像调用View上的方法一样调用这些助手。
<?php $helpers = $view->getHelpers(); $helpers->set('hello', function ($name) { return "Hello {$name}!"; }); $view_registry = $view->getViewRegistry(); $view_registry->set('index', function () { echo $this->hello('World'); }); $view->setView('index'); $output = $view(); ?>
此库不包含任何视图助手。您需要将您自己的助手作为闭包或可调用对象添加到注册表中。
自定义助手管理器
View没有指定特定的类用于其助手管理器。这意味着您可以在View构造时注入任意对象来管理助手。为此,将您自己的助手管理器传递给ViewFactory。
<?php class OtherHelperManager { public function __call($helper_name, $args) { // logic to call $helper_name with // $args and return the result } } $helpers = new OtherHelperManager; $view = $view_factory->newInstance($helpers); ?>
为了获取一组完整的HTML辅助工具,包括表单和输入辅助工具,请考虑使用Aura.Html包及其HelperLocator,作为本包中HelperRegistry的替代方案。您可以将它传递给ViewFactory,如下所示
<?php $helpers_factory = new Aura\Html\HelperLocatorFactory; $helpers = $helpers_factory->newInstance(); $view = $view_factory->newInstance($helpers); ?>
渲染两步视图
要将主要内容包装在布局中作为两步视图的一部分,我们需要使用View注册布局模板,然后调用setLayout()
来选择其中之一作为第二步。如果未设置布局,则不会执行第二步。
假设我们已将上面的browse
模板设置到我们的视图注册表中。然后我们将名为default
的布局模板设置到布局注册表中
<?php $layout_registry = $view->getLayoutRegistry(); $layout_registry->set('default', '/path/to/layouts/default.php'); ?>
default.php
布局模板可能看起来像这样
<html> <head> <title>My Site</title> </head> <body> <?= $this->getContent(); ?> </body> </html>
然后我们可以在View对象上设置视图和布局模板,然后调用它
<?php $view->setView('browse'); $view->setLayout('default'); $output = $view->__invoke(); // or just $view() ?>
内部视图模板的输出将自动保留,并通过View对象的getContent()
方法变得可用。然后布局模板调用getContent()
将内部视图结果放置在外部布局模板中。
注意。我们也可以在视图模板内部调用
setLayout()
,这样我们就可以在选择布局时将其作为视图逻辑的一部分。
视图模板和布局模板都在同一个View对象内部执行。这意味着
-
所有数据值在视图和布局之间共享。分配给视图或由视图修改的任何数据都按原样由布局使用。
-
所有辅助工具在视图和布局之间共享。这种共享情况允许视图在布局执行之前修改数据和辅助工具。
-
所有部分体在视图和布局之间共享。因此,从视图模板捕获的部分可以由布局模板使用。
闭包作为模板
视图和布局注册表接受闭包作为模板。例如,以下是基于闭包的上述browse.php
和_item.php
模板文件的等价物
<?php $view_registry->set('browse', function () { foreach ($this->items as $item) { echo $this->render('_item', array( 'item' => $item, )); } ); $view_registry->set('_item', function (array $vars) { extract($vars, EXTR_SKIP); $id = htmlspecialchars($item['id'], ENT_QUOTES, 'UTF-8'); $name = htmlspecialchars($item['name'], ENT_QUOTES, 'UTF-8') echo "Item ID #{$id} is '{$name}'." . PHP_EOL; ); ?>
在注册基于闭包的模板时,继续使用echo
而不是return
来生成输出。闭包将被重新绑定到View对象上,因此闭包中的$this
将像在基于文件的模板中一样指向View。
对于基于闭包的子模板(也称为“部分”),需要额外的工作。与基于文件的模板不同,基于文件的模板会自动将传递的变量数组提取到局部作用域中,而基于闭包的模板必须
-
定义一个函数参数来接收注入的变量(
_item
模板中的$vars
参数);并且 -
使用
extract()
提取注入的变量。或者,闭包可以直接使用注入的变量参数。
除此之外,基于闭包的模板与基于文件的模板完全一样。
注册模板搜索路径
我们还可以告诉视图和布局注册表在文件系统中搜索模板。首先,我们告诉注册表包含模板文件的目录
<?php $view_registry = $view->getViewRegistry(); $view_registry->setPaths(array( '/path/to/foo', '/path/to/bar', '/path/to/baz' )); ?>
当我们稍后引用命名模板时,注册表将从第一个目录搜索到最后一个目录。为了对搜索路径有更细粒度的控制,我们可以调用prependPath()
来将目录添加到搜索的早期位置,或者调用appendPath()
来将目录添加到搜索的较晚位置。无论如何,View会在搜索目录时自动将.php
追加到模板名称的末尾。
更改模板文件扩展名
默认情况下,每个TemplateRegistry都会自动将.php
追加到模板文件名。如果模板文件的扩展名不同,请使用setTemplateFileExtension()
方法更改它
<?php $view_registry = $view->getViewRegistry(); $view_registry->setTemplateFileExtension('.phtml'); ?>
TemplateRegistry实例用于布局与用于视图的实例不同,因此可能需要更改布局上的模板文件扩展名
<?php $layout_registry = $view->getLayoutRegistry(); $layout_registry->setTemplateFileExtension('.phtml'); ?>
高级配置
或者,您可以传递 $helpers,以及视图和布局的信息或路径,如下所示。
<?php $view_factory = new \Aura\View\ViewFactory; $view = $view_factory->newInstance( $helpers = null, [ 'browse' => '/path/to/views/browse.php' ], [ '/path/to/views/welcome', '/path/to/views/user', ], [ 'layout' => '/path/to/layouts/default.php' ], [ '/path/to/layouts', ], ); ?>
如果您传递了视图和布局的映射信息或路径,您不需要调用 getViewRegistry
或 getLayoutRegistry
,也不需要设置映射信息。
例如:
$view_registry = $view->getViewRegistry(); $view_registry->set('browse', '/path/to/views/browse.php'); $layout_registry = $view->getLayoutRegistry(); $layout_registry->set('default', '/path/to/layouts/default.php');