视图层为特定的动作输入信息,在symfony,视图包括几个部分,每一部分都设计的比较容易被用户修改和维护。
l WEB设计这通常工作在模板和布局上。这些东西由HTML组成并包含嵌入的PHP块,这些块被叫做助手方法。
l 为了重用,开发者通常对模板代码打包成片段或者组件,他们使用插槽和组件插槽到布局的多个区域,WEB设计者最好工作在这些模板片段上。
l 开发者将重点放在YAML视图的配置以及响应对象上。在处理模板中的变量时,一定要注意跨站点脚本的问题,输出不包含技术信息的且易于理解的信息对于安全记录用户信息是必须的。
无论是什么任务, 你都能发现提升你单调工作(将动作输出为表示结果)之效率的有用工具。
模板
先看个模板例子:indexSuccess.php模板例子
<h1>Welcome</h1> <p>Welcome back, <?php echo $name ?>!</p> <ul>What would you like to do? <li><?php echo link_to('Read the last articles', 'article/read') ?></li> <li><?php echo link_to('Start writing a new one', 'article/write') ?></li> </ul> |
模板中包含了HTML代码和一些基础的PHP代码,在动作中定义的变量和助手方法。
就像前面介绍的,在模板中的PHP代码对非PHP开发者应该具有可读性,你应该尽量减少模板中的PHP代码,因为那些文件是被用来设计应用的GUI,他们会被另外的团队(擅长表现但不了解业务逻辑)来创建和维护。把逻辑代码限定在动作中还可以使得一个动作很容易的使用多个模板而不用复制逻辑代码。
助手方法
助手方法是返回HTML代码并使用在模板中的PHP函数。
看代码:Link_to就是一个助手方法
<?php echo input_tag('nickname') ?> => <input type="text" name="nickname" id="nickname" value="" /> |
声明助手方法
Symfony文件包含的助手定义不会自动加载(因为他包含函数而非类)。助手们按目的分组,例如,所有的用来处理文本的助手方法被定义在TextHelper.php文件中,叫做Text助手组。如果在模板中想使用助手,必须在使用前使用use_helper()方法加载,看代码:
// 在这个模板中使用助手组 <?php use_helper('Text') ?> ... <h1>Description</h1> <p><?php echo auto_link_text($description) ?></p> |
声明多个助手时,在use_helper()方法中添加更多的参数。比如加载Text和Javascript助手则写作<?php use_helper(‘Text’,’Javascript’) ?>
一些助手默认在任何模板中均可访问(无需声明),包括下面的助手组:
l Helper:实际就是use_helper()方法自己的声明
l Tag:基本标签助手,可以被每个助手使用
l Url:链接和URL管理助手
l Asset:组装HTML的头部分,并且提供了到外部Assets(images、JS和CSS等)的简单链接。
l Partial:允许包入片段代码
l Cache:操纵缓存代码片段
l Form:表单输入助手
默认被加载的助手定义在settings.yml文件中,打开并修改standard_helpers选项。前四个是无法删除的,因为他们是模板引擎正常工作所必须的,而且他们压根儿就不再standard_helpers选项中出现。
在模板之外也可以使用助手,声明方式更换为:sfLoader::loadHelpers($helpers),变量¥helpers是助手组名或者助手组数组。例如,如果想在动作中使用auto_link_text()助手组,你需要首先调用sfLoader::loadHelpers(‘Text’)方法。
常用的助手组
默认包含的助手组
// Helper组 <?php use_helper('HelperName') ?> <?php use_helper('HelperName1', 'HelperName2', 'HelperName3') ?>
// Tag 组 <?php echo tag('input', array('name' => 'foo', 'type' => 'text')) ?> <?php echo tag('input', 'name=foo type=text') ?> // Alternative options syntax => <input name="foo" type="text" /> <?php echo content_tag('textarea', 'dummy content', 'name=foo') ?> => <textarea name="foo">dummy content</textarea>
// Url group <?php echo link_to('click me', 'mymodule/myaction') ?> => <a href="/route/to/myaction">click me</a> // Depends on the routing settings
// Asset 组 <?php echo image_tag('myimage', 'alt=foo size=200x100') ?> => <img src="/images/myimage.png" alt="foo" width="200" height="100"/> <?php echo javascript_include_tag('myscript') ?> => <script language="JavaScript" type="text/javascript" src="/js/myscript.js"></script> <?php echo stylesheet_tag('style') ?> => <link href="/stylesheets/style.css" media="screen" rel="stylesheet"type="text/css" /> |
需要了解详尽的助手信息请参阅官网API文档,助手组信息的语法、选项以及实例都有。
创建自定义助手
自定义助手方法应该存为MyhelpHerlper.php的文件中,文件放在apps/myapp/lib/hleper目录(或者lib目录下你任意定义的帮助文件下),这样他们就可以自动被Use_helper(‘Myhelp’)方法包含进来。
系统允许你覆盖存在的symfony助手,主要创建自定义助手,文件名称与要覆盖的助手组同名即可。
页面布局
布局文件:myproject/apps/myapp/templates/layout.php
布局中可以加入任何需要的HTML代码,经常用来保存导航、LOGO等,你可以有多个布局,并可以决定每个动作使用何布局。
模板快捷方式
模板中,一些symfony变量(此处成为快捷方式)总是可访问的,这些快捷方式通过核心symfony对象方便在模板中访问最常用的需求信息:
l $sf_context:全部的上下文对象(sfContext实例)
l $sf_request:请求对象(sfRequest实例)
l $sf_params:请求参数
l $sf_user:但前用户Session对象(sfUser实例)
当然,你仍可以在模板中使用核心的symfony类对象:
// 长格式 <?php echo $sf_request->getParameter('total'); ?>
// 短格式 <?php echo $sf_params->get('total'); ?>
// 等价动作中下面的代码 echo $this->getRequestParameter('total'); |
代码片段
可能需要在不同的页面中包含一些HTML或PHP代码,为了避免重复书写代码,PHP的include()方法能满足大多的要求。例如:应用中许多模板需要使用相同的代码片段,把代码片段存为myFramgment.php并放在全局模板目录(myproject/apps/myapp/templates/),然后使用的时候把它包含进来:
<?php include(sfConfig::get('sf_app_template_dir').'/myFragment.php') ?> |
但这种方式不是很干净且可能不同的模板调用此代码需要包含不同的变量。另外,symfony缓存系统没法探测到这种包含,所以代码片段不能从模板中独立的缓存。Symfony提供了三种变通的方式来替代includes:
l 如果逻辑是轻量级的,你仅仅想包含模板文件去显示你传入的数据,可以使用partial
l 如果逻辑是重量级的(例如,你需要访问模型层数据 并且/或者 通过Session修改内容),你希望从逻辑中分离表现,为此你可以使用component。
l 如果片段是要代替布局的特定部分,默认内容可能已经存在,你可以使用插槽。(????)
还有一种代码片段类型叫组件插槽,在代码片段需要依赖上下文(例如,在给定模块的动作下片段需要不一样)时用到。
这些片段的包入是有Partial助手组解决的,可以在任意的symfony模板使用而无需声明。
Partials
一个 Partial 是一块可重用的模板代码。比如,在一个发布应用中,模板代码显示文章的代码被用在文章详细显示页面,同时也最佳文章列表和最新发布列表中显示,将代码做成 Partial 将是最佳方式。和模板一样,Partial也放在templates目录下,他们包含嵌入了PHP的HTML代码,Partial文件常常以下划线(_)开头以方便辨认。模板引用Partial不管在不再一个模块下,引用其他模块的Partial或者templates目录下的Partial,使用include_partial()助手方法,并把模块和Partial名一起作为参数传递:
// 包含myapp/modules/mymodule/templates/_mypartial1.php Partial // 模板与Partial在同一个模块, // 可以忽略模块名 <?php include_partial('mypartial1') ?>
// 包含 myapp/modules/foobar/templates/_mypartial2.php Partial // 模块名必须被包括进来 <?php include_partial('foobar/mypartial2') ?>
// 包含myapp/templates/_mypartial3.php Partial // 全局被认为是'global' 模块 <?php include_partial('global/mypartial3') ?> |
Partial能够访问一般的symfony助手和模板快捷方式,但是,既然Partial可以在应用的任何地方被调用,他们不能自动访问在动作(调用包含Partial的模板的动作)中定义的变量,除非你作为参数明确传递过去。例如,如果你在Partial能访问$total变量,动作必须传递给模板,并且模板必须把变量作为第二个参数传递给include_partial()方法:
动作:mymodule/actions/actions.class.php |
class mymoduleActions extends sfActions { public function executeIndex() { $this->total = 100; } } |
模板:mymodule/templates/indexSuccess.php |
<p>Hello, world!</p> <?php include_partial('mypartial', array('mytotal' => $total) ) ?> |
Partial:mymodule/templates/_mypartial.php |
<p>Total: <?php echo $mytotal ?></p> |
调用Partial时没有使用echo,这是为了与PHP中的include类似,对应所有include_partial都有杜英的get_partial()方法,调用的时候需要echo。
组件
和MVC模式应用动作与模板一样,你可以分离Partial为逻辑部分和表现部分,这种情况下你可以使用组件。
一个组件就像一个动作,除了他更外以外。组件的逻辑部分在继承自sfComponets的类中,在action/components.class.php文件中。组件的表现部分就是一个Partial。sfComponets类的方法有execute开始,就像动作一样,并且诶他们能够传递变量到表现部分。服务于组件表现部分的Partials被命名为component。看动作和组件的区别(动作与组件命名约定):
约定 | 动作 | 组件 |
逻辑文件名称 | actions.class.php | components.class.php |
逻辑类继承自 | sfActions | sfComponents |
方法命名 | executeMyAction() | executeMyComponent() |
表现不分文件名 | myActionSuccess.php | _myComponent.php |
和动作文件一样,组件类也可以有第二种写法,就是一个组件一个文件,继承自sfComponent而不是sfComponents类。
例如,假设你有一个可重用的工具条显示指定主题最新的消息标题。获取新标题需要的查询太复杂而不好由一个简单的 Partial 实现,可以使用组件,看下图:在这个例子中,组件将被保存在自己的模块中(叫做news),但是你可以在一个模块中混合使用组件和动作,如果他能使视图的功能点有意义。
组件类:modules/news/actions/components.class.php |
<?php
class newsComponents extends sfComponents { public function executeHeadlines() { $c = new Criteria(); $c->addDescendingOrderByColumn(NewsPeer::PUBLISHED_AT); $c->setLimit(5); $this->news = NewsPeer::doSelect($c); } } |
Partial:modules/news/templates/_headlines.php |
<div> <h1>Latest news</h1> <ul> <?php foreach($news as $headline): ?> <li> <?php echo $headline->getPublishedAt() ?> <?php echo link_to($headline->getTitle(),'news/show?id='.$headline->getId()) ?> </li> <?php endforeach ?> </ul> </div> |
在模板中使用组件,直接调用下面的语句 |
<?php include_component('news', 'headlines') ?> |
和Partials一样,组件也接受额外的参数作为变量的传递。看使用方式:
调用组件:modules/news/actions/components.class.php |
<?php include_component('news', 'headlines', array('foo' => 'bar')) ?> |
在组件类中 |
echo $this->foo; => 'bar' |
在_headlines.phpPartial |
echo $foo; => 'bar |
可以在组件或者在布局中引入组件,就像在普通模板中一样。和动作一样,组件的execute方法能够传递参数到关联的Partial并能访问模板快捷方式,但相似处仅此而已!!组件不能捕获安全或验证,不能被外部调用,没有不同返回的可能性,这就是为什么组件比动作执行的快的原因。
插槽(slots)
Partials和组件能很好的解决复用,但在很多情况下,代码片段需要使用许多的动态区块填入布局。例如,假设需要在布局的<head>部分添加一些依赖于动作内容的自定义标签,或者假设布局有一个主要的动态区域,由动作的结果集和多个其他小的部分组成,这些在布局中定义的小东西有默认的内容,但可以在模板级覆盖。
这种情况就需要一个slot。基本上一个slot就是一个占位符,你可以在此放置任何的视图元素(布局、模板或Partial)。填充占位符就像给变量赋值,填充代码完整地存储在响应中,所以可以在任何地方定义(布局、模板或Partial)。只要记得在包含之前定义了插槽,并且记得布局在模板之后执行,并且Partial在模板调用的时候执行。可能很抽象,看个例子:
想象有个布局包含一个为模板准备的主要区域和两个slot:一个是工具栏,另一个是页脚。Slot的值定义在模板中。在装饰处理部分,布局代码包装模板代码并且slot也被前面定义的值填充,看下面的图:
工具栏和页脚可以与主动作相关联,就像有一个带“洞”的布局
看些代码来进一步验证这种方式。要引入一个slot使用include_slot助手方法,如果slot已经定义过则has_slot助手方法返回真。
在布局中引入一个“工具栏”
<div id="sidebar"> <?php if (has_slot('sidebar')): ?> <?php include_slot('sidebar') ?> <?php else: ?> <!—默认工具栏代码 --> <h1>Contextual zone</h1> <p>此处包含与页面主要区域相关的链接和信息.</p> <?php endif; ?> </div> |
每个模板都能定义slot的内容(实际上Partials也可以)。既然slots是用来存放HTML代码,symfony提供了一种便捷的方式来定义他们:把slot代码放在slot()和end_slot()之间即可:
<?php slot('sidebar') ?> <!—为此模板自定义工具栏--> <h1>User details</h1> <p>name: <?php echo $user->getName() ?></p> <p>email: <?php echo $user->getEmail() ?></p> <?php end_slot() ?> |
Slot助手之间的代码在模板的上下文中执行,也就是说可以访问动作中定义的所有变量。Symfony自动把代码的结果放入响应对象。但他不会在模板显示,只有在调用include_slot()的时候,就像上面在布局中引入“工具栏”中的代码所示。
Slot在定义用来显示上下文相关内容区域的时候非常游泳,也能被用来添加HTML代码到特定动作的布局中。例如,一个用来显示最新新闻列表的模板可能需要在布局的<head>部分添加一个RSS链接,简单的在布局中添加‘feed’插槽并在列表模板中覆盖即可实现。
在那儿找模板片段 | |
制作模板的通常是WEB设计人员,他们可能对symfony不太熟悉,不太容易找到模板片段(他们可以被分散在应用的各个地方)。 但对于设计者,只要关心下面的目录即可:
其它目录可以忽略。 当遇到include_partial(),Web设计者只要知道第一个参数即可——模块名/Partial名,也就是说表现代码能在 modules/模块名/templates/_partial名.php中找到。 对于include_component() 助手,模块名和Partial名是前两个参数 |
视图配置(View Configuration)
Symfony中的视图包含两个不同的部分:
l HTML展示动作的结果(存在模板、布局和模板片段中)
l 剩下的包括
n 元声明:关键字、描述、缓存时间
n 页面标题
n 文件包含:JS和CSS
n 布局:一些动作需要自定义布局(弹出窗口、广告等)或者压根儿没有布局(AJAX动作)
视图中,所有非HTML都叫做视图配置,symfony提供了两种方式来操纵它。常用的方式是通过view.yml文件,只要值不依赖上下文或者数据库查询的都可以通过view.yml配置文件实现。要设置动态值,可选的方法是直接在动作中通过sfResponse对象属性设置视图的配置。如果同时设置了view.yml和sfResponse对象,则sfResponse对象优先级高于iew.yml文件。
View.yml配置文件
每个模块都可以包含一个view.yml文件来定义模块视图的设置。这就允许你为整个模块和每一个视图在一个文件中定义设置。View.yml文件的第一级键是模块视图的名字,看代码:
editSuccess: metas: title: Edit your profile
editError: metas: title: Error in the profile edition
all: stylesheets: [my_style] metas: title: My website |
注意view.yml文件中的主要关键字是视图的名字而非动作名。提示:视图名称有动作名和结束状态组成。例如,edit动作返回sfView::SUCCESS,那么视图名称就是editSuccess。
模块的默认设置在view.yml文件的all:值下。要记得配置的级联准则:
l 在apps/myapp/modules/mymodule/config/view.yml,per-view设定至应用到一个视图并覆盖模块级设置。
l 在apps/myapp/modules/mymodule/config/view.yml,all: 设置应用到模块中的所有动作并覆盖应用级设置。
l 在apps/myapp/config/view.yml,default: 设定应用到整个应用的所有模块的所有动作。
默认不存在模块级的view.yml,如果希望在模块级应用设定,你必须首先在模块的config文件夹下建立view.yml文件。
看下应用级的设定:apps/myapp/config/view.yml
default: http_metas: content-type: text/html
metas: title: symfony project robots: index, follow description: symfony project keywords: symfony, project language: en
stylesheets: [main]
javascripts: [ ]
has_layout: on layout: layout |
响应对象
作为视图层的以部分,响应对象经常被动作修改,动作可以访问symfony的响应对象——称为sfResponse——通过getResponse()方法,下面的代码列出了一些在动作中经常使用的sfResponse方法:
class mymoduleActions extends sfActions { public function executeIndex() { $response = $this->getResponse();
// HTTP 头 $response->setContentType('text/xml'); $response->setHttpHeader('Content-Language', 'en'); $response->setStatusCode(403); $response->addVaryHttpHeader('Accept-Language'); $response->addCacheControlHttpHeader('no-cache');
// Cookies $response->setCookie($name, $content, $expire, $path, $domain);
// 元数据和页面头 $response->addMeta('robots', 'NONE'); $response->addMeta('keywords', 'foo bar'); $response->setTitle('My FooBar Page'); $response->addStyleSheet('custom_style'); $response->addJavaScript('custom_behavior'); } } |
头(header)setter非常强大,头会尽量晚的被发送(在sfRenderingFilter中),因此你可以尽量多且尽量晚的修改他们,而且头还提供了非常有用的快捷方式,比如:如果你在setContentType()中没有制定字符集,symfony会自动添加在settings.yml文件定义的默认字符集。
$response->setContentType('text/xml'); echo $response->getContentType(); => 'text/xml; charset=utf-8' |
Symfony中响应的状态服从与HTTP规范,异常返回500,页面没有找到返回404,正常状态返回200等等。但可以在动作中使用setStatusCode()方法修改响应返回的代码值,比如:内部服务器错误500的动作中使用了setStatusCode(501,’服务器维护中…’)。
视图配置设定
你会发现视图配置设定中有两种类型:
l 具有单值的设定(在veiw.yml文件中值是字符串,响应使用set方法设置)
l 具有多值的设定(在veiw.yml文件中是数组,响应使用add方法设置)
记住了:级联方式清除以前的单值设定却会堆积多值设定。
元标签配置
写在<meta>标签下的信息不会被浏览器显示但却对机器人与搜索引擎起到作用,他还会控制每页的缓存设定等。通过在view.yml文件中使用http_metas: keys 和 metas: keys 来定义这些标签,或者在动作中使用addHttpMeta()和addMeta()响应方法:
在view.yml文件中通过键:值对来设置 |
http_metas: cache-control: public
metas: description: Finance in France keywords: finance, France |
在动作中使用响应方法设定 |
$this->getResponse()->addHttpMeta('cache-control', 'public'); $this->getResponse()->addMeta('description', 'Finance in France '); $this->getResponse()->addMeta('keywords', 'finance, France '); |
多次设定一个存在的键将覆盖先前的,可以通过把第三个参数设置为false来追加 |
$this->getResponse()->addHttpMeta('accept-language', 'en'); $this->getResponse()->addHttpMeta('accept-language', 'fr', false); echo $this->getResponse()->getHttpHeader('accept-language'); => 'en, fr' |
为了将这些头设定显示在最终的页面,则必须在<head>部分调用include_http_metas()和include_metas()助手方法(基本在布局页面中包含)
标题设定
页面标题是搜索引擎索引的主要部分,对现在浏览器提供标签浏览也很有用处。
在view.yml文件中通过键:值对来设置 |
indexSuccess: metas: title: Three little piggies |
在动作中使用响应方法设定 |
$this->getResponse()->addHttpMeta('cache-control', 'public'); $this->getResponse()->setTitle(sprintf('%d little piggies', $number)); |
如果布局中引入了include_metas()助手方法,最终页面的<meta name=”title”>将会显示,如果布局中引入了include_title()助手方法,最终页面的<title>标签会显示。
文件包含配置
在视图中加入CSS和JS很简单,同样的两种方式:
在view.yml文件中通过键:值对来设置 |
indexSuccess: stylesheets: [mystyle1, mystyle2] javascripts: [myscript] |
在动作中使用响应方法设定 |
$this->getResponse()->addStylesheet('mystyle1'); $this->getResponse()->addStylesheet('mystyle2'); $this->getResponse()->addJavascript('myscript'); |
两种方式下,参数都是文件名,如果文件包括了扩展名,可以忽略不写;同样如果包括了逻辑路径也可以忽略不屑。Symfony能够智能地发现正确的文件。
不同于meta和title设定,文件包含配置无需在模板或者布局中引入任何的助手,也就是说上面两种设定会在最终页面中输出:
<head> ... <link rel="stylesheet" type="text/css" media="screen" href="/css/mystyle1.css" /> <link rel="stylesheet" type="text/css" media="screen" href="/css/mystyle2.css" /> <script language="javascript" type="text/javascript" src="/js/myscript.js"> </script> </head> |
响应中包含的CSS和JS会被叫做sfCommonFilter的过滤器执行,他查找<head>标签并且在</head>之前加入<link>和<script>,也就是说,如果布局或模板中没有<head>标签,包入将不会起作用。
级联准则依然起作用,只是追加方式:
应用的view.yml文件 |
default: stylesheets: [main] |
模块的view.yml文件 |
indexSuccess: stylesheets: [special] all: stylesheets: [additional] |
最终的结果:indexSuccess |
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.css" /> <link rel="stylesheet" type="text/css" media="screen" href="/css/additional.css" /> <link rel="stylesheet" type="text/css" media="screen" href="/css/special.css" /> |
如果希望删除一个在高级别中定义的文件,只要在地级别设定中的文件前加一个减号即可。
indexSuccess: stylesheets: [-main, special]
all: stylesheets: [additional] |
删除所有的使用:
indexSuccess: stylesheets: [-*] javascripts: [-*] |
可以制定包入文件的位置:
在view.yml文件 |
indexSuccess: stylesheets: [special: { position: first }] |
动作中 |
$this->getResponse()->addStylesheet('special', 'first'); |
还有更多的选项,可参照API
布局配置
每个模块有一个默认的布局文件myproject/apps/myapp/templates/layout.php,可以在此目录下定义更多的布局。要访问自定义布局也有两种方式:
在view.yml文件 |
indexSuccess: layout: my_layout |
动作中 |
$this->setLayout('my_layout'); |
有些视图不需要布局(比如存文字页面或者RSS,AJAX默认没有布局),这是设定为:
在view.yml文件 |
indexSuccess: has_layout: false |
动作中 |
$this->setLayout(false); |
组件插槽
组合视图组件和视图配置产生了一个新的景观(perspective):组件插槽系统。组件插槽比插槽更结构化但运行稍慢。
和插槽一样,组件插槽也在视图元素中被定义为占位符。对于插槽,代码在另一个视图元素中设置;对于组件插槽,代码结果来至组件(组件名称来自视图配置)的执行。通过在动作中查看可以更清晰的理解组件插槽。
放置组件插槽占位符使用include_component_slot()助手方法,方法需要一个标签(label)作为参数,例如,假设应用的layout.php文件包含了一个上下文相关的工具栏,看下面的代码(展示了如何包含组件插槽助手方法):
... <div id="sidebar"> <?php include_component_slot('sidebar') ?> </div> |
在视图配置中定义定义相同的组件插槽标签(label)和组件名称,例如,在应用的view.yml中(在component头下)为(工具栏)sidebar组件插槽设置默认组件。键是组件插槽的标签,值必须是一个包含了模块与组件名称的数组,代码如下:myapp/config/view.yml
default: components: sidebar: [bar, default] |
当布局执行的时候,工具栏组件插槽被barComponets类(在bar模块中)之executeDefault()方法的执行结果填充,并且将显示 modules/bar/templates/_default.php 片段。
配置级联使得你能够为给定模块覆盖这种设置。比如,在用户模块,你可能向让上下文组件显示用户名称和该用户发布的文章数目,这是,在模块的view.yml中制定工具栏插槽的设置:
all: components: sidebar: [bar, user] |
组件的定义: modules/bar/actions/components.class.php
class barComponents extends sfComponents { public function executeDefault() { }
public function executeUser() { $current_user = $this->getUser()->getCurrentUser(); $c = new Criteria(); $c->add(ArticlePeer::AUTHOR_ID, $current_user->getId()); $this->nb_articles = ArticlePeer::doCount($c); $this->current_user = $current_user; } } |
工具栏组件插槽使用的两个代码片段:
// _default.php <p>This zone contains contextual information.</p>
// _user.php <p>User name: <?php echo $current_user->getName() ?></p> <p><?php echo $nb_articles ?> articles published</p> |
组件插槽可以用在面包层(breadcrumbs),上下文相关导航、动态插入等场合。作为组件,他能用在全局布局以及常规模板,甚至其他的组件。
如果向暂停一个制定模块的组件插槽的使用,只要在view.yml的如下设置(清空 模块/组件值)即可:
all: components: sidebar: [] |
输出避让(Escaping)
当在模板中插入动态数据时要注意数据的完整性。例如,如果数据来自匿名用户的表单提交,则可能包含了危险代码,你必须避开输出数据库。
可以手动对不确信数据使用htmlentities()方法处理来实现,但这中重复性操作很枯燥并且潜在错误。相反,symfony提供了一个特殊系统——输出避让——可以自动避开模板中的每个变量的输出。可以在应用的settings.yml文件中通过修改参数简单的激活。
激活输出避让
方法一:通过文件myapp/config/setting.yml
all: .settings: escaping_strategy: both escaping_method: ESC_ENTITIES |
这将默认对输出的每个变量添加htmlentities()方法。测试代码:
动作中 |
$this->test = '<script>alert(document.cookie)</script>'; |
模板中 |
echo $test; => ><script>alert(document.cookie)</script> |
方法二:通过在模板中使用$sf_data对象
模板中 |
echo $sf_data->get('test'); => ><script>alert(document.cookie)</script> |
有些时候,你希望输出原始数据,这是使用$sf_data的getRaw()方法即可。在模块的布局中,主题部分就是这个:$sf_data->getRaw('sf_content')。
避让策略(escaping_strategy)
避让策略设置决定了默认变量输出的方式,可能的值有:
l bc(向后兼容模式):变量输出比避让,但是通过$sf_data容器可以避让。这是默认的避让模式
l both:默认对所有变量避让。这是建议采用的模式。有些时候输出包含HTML代码的片段时会出现问题,最可能的原因就是这里。
l on:只能使用$sf_data容器输出数据。
l off:$sf_data容器被禁用。
避让助手方法(escaping_method)
l ESC_RAW: Doesn't escape the value.
l ESC_ENTITIES: Applies the PHP function htmlentities() to the input with ENT_QUOTES as the quote style.
l ESC_JS: Escapes a value to be put into a JavaScript string that is going to be used as HTML. This is useful for escaping things where HTML is going to be dynamically changed using JavaScript.
l ESC_JS_NO_ENTITIES: Escapes a value to be put into a JavaScript string but does not add entities. This is useful if the value is going to be displayed using a dialog box (for example, for a myString variable used in javascript:alert(myString);).
避让数组和对象
输出避让不仅可以用在字符串,还可以应用到数组和对象。
类定义 |
class myClass { public function testSpecialChars($value = '') { return '<'.$value.'>'; } } |
动作中 |
$this->test_array = array('&', '<', '>'); $this->test_array_of_arrays = array(array('&')); $this->test_object = new myClass(); |
模板中 |
<?php foreach($test_array as $value): ?> <?php echo $value ?> <?php endforeach; ?> => & < > <?php echo $test_array_of_arrays[0][0] ?> => & <?php echo $test_object->testSpecialChars('&') ?> => <&> |
实际上,避让后的变量已不是你期望的,他们成了sfOutputEscaperArrayDecorator类的对象。所以一些PHP函数(如:array_shift()、print_r()等)不能正常工作了。但数组避让后仍能通过[]访问并能使用foreach便利,count()方法也没问题。但在模板中数组成为了只读的!
可以加入额外的参数禁用避让。
<?php echo $test_object->testSpecialChars('&') ?> => <&> // 下面的三中方式得到相同的结果 <?php echo $test_object->testSpecialChars('&', ESC_RAW) ?> <?php echo $sf_data->getRaw('test_object')->testSpecialChars('&') ?> <?php echo $sf_data->get('test_object', ESC_RAW)->testSpecialChars('&') ?> => <&> |