第6章-控制器层内部
在syob娱乐下载mfony中,包含连接业务逻辑和表示的代码的控制器层被分为几个用于不同目的的组件:< / p >
- 前端控制器是应用程序的唯一入口点。它加载配置并确定要执行的操作。
- 操作包含应用逻辑。它们检查请求的完整性,并准备表示层所需的数据。
- 请求、响应和会话对象允许访问请求参数、响应标头和持久用户数据。它们经常用于控制器层。
- 过滤器是为每个请求执行的代码的一部分,在操作之前或之后。例如,安全性和验证过滤器通常用于web应用程序。您可以通过创建自己的过滤器来扩展框架。
本章描述了所有这些组件,但不要被它们的数量吓倒。对于一个基本的页面,您可能只需要在action类中编写几行代码,仅此而已。其他控制器组件只在特定情况下使用。< / p >
前置控制器
所有web请求都由一个前端控制器处理,它是给定环境中整个应用程序的唯一入口点。< / p >
当前端控制器接收到请求时,它使用路由系统将动作名和模块名与用户键入(或单击)的URL匹配。例如,下面的请求URL调用index . php
脚本(即前端控制器),并将被理解为对动作的调用myAction
模块的mymodule里
:< / p >
http://localhost/index.php/mymodule/myAction
如果您对symfony的内部结构不感兴趣,那么这就是您需要ob娱乐下载了解的关于前端控制器的全部内容。它是symfony MVC体系结构中不可缺少的组件,但您很少需要更改它。ob娱乐下载所以你可以跳到下一节,除非你真的想知道前端控制器的内部结构。< / p >
前端控制器的工作细节
前端控制器负责请求的调度,但这不仅仅意味着决定要执行的动作。事实上,它执行所有操作的通用代码,包括以下代码:< / p >
- 加载项目配置类和symfony库。ob娱乐下载
- 创建应用程序配置和symfony上下文。ob娱乐下载
- 加载并初始化核心框架类。
- 加载配置。
- 解码请求URL以确定要执行的动作和请求参数。
- 如果该操作不存在,则重定向到404错误操作。
- 激活过滤器(例如,如果请求需要身份验证)。
- 执行过滤器,第一次。
- 执行操作并呈现视图。
- 执行过滤器,第二遍。
- 输出响应。
默认前端控制器
默认的前端控制器index . php
位于web /
目录是一个简单的PHP文件,如清单6-1所示。< / p >
清单6-1 -默认生产前端控制器< / p >
<?phprequire_once(目录名(__FILE__).“/ . . / config / ProjectConfiguration.class.php”);美元配置= ProjectConfiguration::getApplicationConfiguration(“前端”,“刺激”,假);sfContext::createInstance除外(美元配置)->调度();
前端控制器创建一个应用程序配置实例,它负责步骤2到4。调用调度()
方法sfController
对象(symfony MVC体系结构的核心控制器对象)分派请求,负责步骤5到7。ob娱乐下载最后的步骤由过滤器链处理,如本章后面解释的那样。< / p >
呼叫另一个前端控制器切换环境
每个环境有一个前端控制器。事实上,正是前端控制器的存在定义了一个环境。对象的第二个参数定义了环境ProjectConfiguration: getApplicationConfiguration ()
方法调用。< / p >
要更改浏览应用程序的环境,只需选择另一个前端控制器。控件创建新应用程序时可用的默认前端控制器生成:应用程序
任务index . php
用于生产环境和frontend_dev.php
对于开发环境(假设您的应用程序被调用)前端
).默认的mod_rewrite
配置将使用index . php
当URL不包含前端控制器脚本名称时。因此这两个url显示相同的页面(mymodule里/索引
)在生产环境中:< / p >
http://localhost/index.php/mymodule/index http://localhost/mymodule/index
这个URL在开发环境中显示相同的页面:< / p >
http://localhost/frontend_dev.php/mymodule/index
创建一个新环境就像创建一个新的前端控制器一样简单。例如,您可能需要一个登台环境来允许客户在进入生产环境之前对应用程序进行测试。要创建这个登台环境,只需复制web / frontend_dev.php
成web / frontend_staging.php
的第二个参数的值ProjectConfiguration: getApplicationConfiguration ()
调用暂存
.现在,在所有配置文件中,您可以添加一个新的分段:
节为该环境设置特定的值,如清单6-2所示。< / p >
清单6-2 -示例app.yml
使用登台环境的特定设置< / p >
分期:邮件:webmaster: dummy@mysite.com contact: dummy@mysite.com all:邮件:webmaster: webmaster@mysite.com contact: contact@mysite.com
如果你想看看应用程序在这个新环境中的反应,调用相关的前端控制器:< / p >
http://localhost/frontend_staging.php/mymodule/index
行动
操作是应用程序的核心,因为它们包含应用程序的所有逻辑。它们调用模型并为视图定义变量。当您在symfony应用程序中发出web请求时,URL定义了一个动ob娱乐下载作和请求参数。< / p >
行动课
动作是命名为executeActionName
一个叫做moduleNameActions
从sfActions
类,并按模块分组。模块的操作类存储在actions.class.php
文件,在模块的行为/
目录中。< / p >
清单6-3显示了一个示例actions.class.php
仅使用指数
为整体而行动mymodule里
模块。< / p >
清单6-3 -示例动作类,在应用程序/前端/模块/ mymodule里/行动/ actions.class.php
类mymoduleActions扩展sfActions{公共函数executeIndex(美元的请求){/ /……}}
谨慎< / p >
即使方法名在PHP中不区分大小写,它们也在symfony中。ob娱乐下载所以不要忘记动作方法必须以小写字母开始执行
,后面跟着准确的动作名称,第一个字母大写。< / p >
为了请求一个动作,需要以模块名和动作名作为参数调用前端控制器脚本。默认情况下,这是通过附加一对来完成的module_name
/action_name
到脚本。这意味着清单6-4中定义的操作可以通过以下URL调用:< / p >
http://localhost/index.php/mymodule/index
增加更多的动作就意味着增加更多执行
方法sfActions
对象,如清单6-4所示。< / p >
清单6-4 -带有两个动作的动作类,在前端/模块/ mymodule里/行动/ actions.class.php
类mymoduleActions扩展sfActions{公共函数executeIndex(美元的请求){/ /……}公共函数executeList(美元的请求){/ /……}}
如果操作类的大小增长太多,您可能需要进行一些重构,并将一些代码移到模型层。操作通常应该保持简短(不超过几行),并且所有的业务逻辑通常都应该在模型中。< / p >
尽管如此,一个模块中的操作数量可能非常重要,以至于需要将其分成两个模块。< / p >
侧边栏< / p >
ob娱乐下载Symfony编码标准< / p >
在本书给出的代码示例中,您可能注意到,开始和结束大括号({
而且}
)每人各占一行。这个标准使代码更容易阅读。< / p >
在框架的其他编码标准中,缩进总是由两个空格完成;不使用制表符。这是因为根据您使用的文本编辑器,制表符具有不同的空格值,并且因为混合了制表符和空白缩进的代码无法阅读。< / p >
核心和生成的symfony PHP文件ob娱乐下载不会以通常的方式结束? >
关闭标签。这是因为实际上并不需要它,而且如果在该标记之后有空格,则会在输出中产生问题。< / p >
如果你真的注意的话,你会发现在symfony中,一行永远不会以空格结束。ob娱乐下载这一次的原因更加平淡无奇:在法比安的文本编辑器中,以空格结尾的行看起来很难看。< / p >
可选动作类语法
另一种操作语法可用于在单独的文件中分派操作,每个操作一个文件。在这种情况下,每个动作类都进行了扩展sfAction
(而不是sfActions
),并以actionNameAction
.实际操作方法的名称很简单执行
.文件名与类名相同。这意味着可以使用清单6-5和清单6-6所示的两个文件编写与清单6-4相同的代码。< / p >
清单6-5 -单个操作文件,在前端/模块/ mymodule里/行动/ indexAction.class.php
类indexAction扩展sfAction{公共函数执行(美元的请求){/ /……}}
清单6-6 -单个操作文件,在前端/模块/ mymodule里/行动/ listAction.class.php
类listAction扩展sfAction{公共函数执行(美元的请求){/ /……}}
在操作中检索信息
动作类提供了一种访问控制器相关信息和核心symfony对象的方法。ob娱乐下载清单6-7演示了如何使用它们。< / p >
清单6-7 -sfActions
常用的方法< / p >
类mymoduleActions扩展sfActions{公共函数executeIndex(sfWebRequest美元的请求){//获取请求参数美元的密码=美元的请求->getParameter(“密码”);//获取控制器信息moduleName美元=这个美元->getModuleName();actionName美元=这个美元->getActionName();//获取框架核心对象userSession美元=这个美元->getUser();美元的反应=这个美元->getResponse();美元的控制器=这个美元->getController();美元的上下文=这个美元->getContext();//设置动作变量,将信息传递给模板这个美元->setVar(“foo”,“酒吧”);这个美元->喷火=“酒吧”;//短版本}}
侧边栏< / p >
context单例< / p >
你已经看到,在前端控制器中,调用这里sfContext: createInstance ()
.在一个动作中,getContext ()
方法返回相同的单例。它是一个非常有用的对象,存储了与给定请求相关的所有symfony核心对象的引用,并为每个对象提供了一个访问器:ob娱乐下载< / p >
sfController
:控制器对象(- > getController ()
)< / p >
sfRequest
:请求对象(- > getRequest ()
)< / p >
sfResponse
:响应对象(- > getResponse ()
)< / p >
sfUser
:用户会话对象(- > getUser ()
)< / p >
sfRouting
:路由对象(- > getRouting ()
)< / p >
sfMailer
:邮件对象(- > getMailer ()
)< / p >
sfI18N
:国际化对象(- > getI18N ()
)< / p >
sfLogger
:日志记录器对象(- > getlog ()
)< / p >
sfDatabaseConnection
:数据库连接(- > getDatabaseConnection ()
)< / p >
所有这些核心对象都可以通过sfContext: getInstance ()
从代码的任何部分获取单例。然而,这是一个非常糟糕的做法,因为这会产生一些硬依赖,使您的代码非常难以测试、重用和维护。你将在这本书中学习如何避免使用sfContext: getInstance ()
.< / p >
行动终止
在动作执行结束时,各种行为都是可能的。action方法返回的值决定如何呈现视图。常量sfView
类用于指定将使用哪个模板来显示操作的结果。< / p >
如果有一个默认视图要调用(这是最常见的情况),动作应该以如下方式结束:< / p >
返回sfView::成功;
ob娱乐下载然后Symfony将寻找一个名为actionNameSuccess.php
.这被定义为默认操作行为,因此如果省略返回
语句中的动作方法,symfony也会查找ob娱乐下载actionNameSuccess.php
模板。空操作也会触发该行为。有关成功终止操作的示例,请参见清单6-8。< / p >
清单6-8 -调用indexSuccess.php
而且listSuccess.php
模板< / p >
公共函数executeIndex(){返回sfView::成功;}公共函数executeList(){}
如果有一个要调用的错误视图,动作应该像这样结束:< / p >
返回sfView::错误;
ob娱乐下载然后Symfony将寻找一个名为actionNameError.php
.< / p >
要调用一个自定义视图,使用下面的结尾:< / p >
返回“MyResult”;
ob娱乐下载然后Symfony将寻找一个名为actionNameMyResult.php
.< / p >
如果没有视图可以调用——例如,在批处理过程中执行的操作——该操作应该以如下方式结束:< / p >
返回sfView::没有一个;
在这种情况下不会执行模板。这意味着您可以完全绕过视图层,直接从操作设置响应HTML代码。如清单6-9所示,symfony提供了一个特定的ob娱乐下载renderText ()
方法。当您需要操作的极端响应性时,例如Ajax交互,这将在第11章中讨论。< / p >
清单6-9 -通过回显响应和返回绕过视图sfView::没有
公共函数executeIndex(){这个美元->getResponse()->setContent(“< html > <身体>你好,世界!< /身体> < / html >”);返回sfView::没有一个;}//等价于公共函数executeIndex(){返回这个美元->renderText(“< html > <身体>你好,世界!< /身体> < / html >”);}
在某些情况下,您需要发送一个空响应,但其中定义了一些头信息(特别是X-JSON
头)。方法定义标头sfResponse
对象,将在下一章中讨论sfView: HEADER_ONLY
常量,如清单6-10所示。< / p >
清单6-10 -转义视图呈现和只发送头信息< / p >
公共函数executeRefresh(){输出美元=<"title","My basic letter"],["name"," Brown先生"> ";这个美元->getResponse()->setHttpHeader(“X-JSON”,“(”.输出美元.“)”);返回sfView::HEADER_ONLY;}
如果操作必须由特定模板呈现,则忽略返回
语句,并使用setTemplate ()
方法相反。< / p >
公共函数executeIndex(){这个美元->setTemplate(“myCustomTemplate”);}
使用这段代码,symfony将查ob娱乐下载找myCustomTemplateSuccess.php
文件,而不是indexSuccess.php
.< / p >
跳转到另一个动作
在某些情况下,操作执行以请求新的操作执行结束。例如,POST请求中处理表单提交的操作通常在更新数据库后重定向到另一个操作。< / p >
动作类提供了两个方法来执行另一个动作:< / p >
如果动作将调用转发给另一个动作:< / p >
这个美元->向前(“otherModule”,“指数”);
如果该操作导致web重定向:< / p >
这个美元->重定向(“otherModule /指数”);这个美元->重定向(“http://www.google.com/”);
请注意< / p >
位于动作中的转发或重定向之后的代码永远不会执行。你可以认为这些调用等价于a返回
声明。他们会扔一个sfStopException
停止动作的执行;这个异常稍后会被symfony捕获并忽略。ob娱乐下载< / p >
在重定向和转发之间做出选择有时很棘手。要选择最佳的解决方案,请记住forward是应用程序内部的,对用户是透明的。就用户而言,显示的URL与请求的URL相同。相反,重定向是发送给用户浏览器的消息,包括来自浏览器的新请求和最终结果URL的更改。< / p >
如果从提交的表单中调用该操作方法=“发布”
,你应该总是做一个重定向。主要的好处是,如果用户刷新结果页面,表单将不会再次提交;此外,返回按钮按预期工作,显示表单,而不是提醒用户是否要重新提交POST请求。< / p >
有一种特殊的forward非常常用。的forward404 ()
方法转发到“未找到页面”操作。当动作执行所需的参数没有出现在请求中(因此检测到错误输入的URL)时,通常会调用此方法。清单6-11显示了一个示例显示
期待的行动id
参数。< / p >
清单6-11 -的使用forward404 ()
方法< / p >
公共函数executeShow(sfWebRequest美元的请求){/ /学说美元的文章=学说::可以获得的(“文章”)->找到(美元的请求->getParameter(“id”));/ /推动美元的文章= ArticlePeer::retrieveByPK(美元的请求->getParameter(“id”));如果(!美元的文章){这个美元->forward404();}}
提示< / p >
如果您正在寻找错误404操作和模板,则可以在美元sfob娱乐下载_symfony_ lib_dir /控制器/违约
目录中。您可以通过添加一个新的默认的
模块,覆盖位于框架中的模块,并通过定义那么error404
action和一个error404Success模板。或者,您可以设置error_404_module
而且error_404_action
常量settings.yml
文件来使用现有操作。< / p >
经验表明,大多数情况下,一个操作在测试之后会进行重定向或转发,如清单6-12所示。这就是为什么sfActions
类还有一些方法,命名为forwardIf ()
,forwardUnless ()
,forward404If ()
,forward404Unless ()
,redirectIf ()
,redirectUnless ()
.这些方法只接受一个表示条件的附加参数,如果测试为真,则触发执行(对于xxxIf ()
方法)或false(对于xxxUnless ()
方法),如清单6-12所示。< / p >
清单6-12 -的使用forward404If ()
方法< / p >
//此操作与清单6-11中所示的操作相同公共函数executeShow(sfWebRequest美元的请求){美元的文章=学说::可以获得的(“文章”)->找到(美元的请求->getParameter(“id”));这个美元->forward404If(!美元的文章);}//这个也是公共函数executeShow(sfWebRequest美元的请求){美元的文章=学说::可以获得的(“文章”)->找到(美元的请求->getParameter(“id”));这个美元->forward404Unless(美元的文章);}
使用这些方法不仅可以使您的代码简短,而且还可以使代码更具可读性。< / p >
提示< / p >
当动作召唤时forward404 ()
或者它的同类方法,symfony抛出一个ob娱乐下载sfError404Exception
它管理404响应。这意味着如果您需要从不想访问控制器的某个位置显示404消息,则可以抛出类似的异常。< / p >
重复一个模块的几个动作的代码
命名动作的约定executeActionName ()
(对于sfActions
类)或execute ()
(对于sfAction
类)保证symfony将找到动作方法。ob娱乐下载它使您能够添加您自己的其他方法,这些方法不会被视为操作,只要它们不开始执行
.< / p >
当您需要在实际操作执行之前在每个操作中重复几个语句时,还有另一个有用的约定。然后可以将它们提取到preExecute ()
你的动作类的方法。您可能已经猜到如何在每个操作执行之后重复语句:将它们包装在语句中postExecute ()
方法。这些方法的语法如清单6-13所示。< / p >
清单6-13 -使用preExecute ()
,postExecute ()
,以及动作类中的自定义方法< / p >
类mymoduleActions扩展sfActions{公共函数preExecute(){//这里插入的代码在每个action调用开始时执行...}公共函数executeIndex(美元的请求){...}公共函数executeList(美元的请求){...这个美元->myCustomMethod();//可以访问action类的方法}公共函数postExecute(){//这里插入的代码在每个action调用结束时执行...}受保护的函数myCustomMethod(){//你也可以添加你自己的方法,只要它们不以"execute"开头//在这种情况下,最好将它们声明为受保护或私有...}}
提示< / p >
当调用前/后执行方法时每一个当前模块的操作,请确保您确实需要执行此代码所有你的行动,以避免不必要的副作用。< / p >
访问请求
传递给任何操作方法的第一个参数是请求对象sfWebRequest
在syob娱乐下载mfony。你们已经熟悉了getParameter(“myparam”)
方法,用于按名称检索请求参数的值。表6-1列出了最有用的方法sfWebRequest
方法。< / p >
表6-1 -使用方法sfWebRequest
对象< / p >
的名字 | 函数 | 样例输出 |
---|---|---|
请求的信息 | ||
isMethod(方法) |
是post还是get? | 对或错 |
getMethod () |
请求方法名称 | “职位” |
getHttpHeader(服务器) |
给定HTTP头的值 | 'Apache/2.0.59 (Unix) DAV/2 PHP/5.1.6' |
getCookie(“foo”) |
命名cookie的值 | “酒吧” |
isXmlHttpRequest () * |
它是Ajax请求吗? | 真正的 |
isSecure () |
它是SSL请求吗? | 真正的 |
请求参数 | ||
hasParameter(“foo”) |
请求中是否有参数? | 真正的 |
getParameter(“foo”) |
命名参数的值 | “酒吧” |
getParameterHolder () - > getAll () |
所有请求参数的数组 | |
URI-Related信息 | ||
getUri () |
完整的URI | “http://localhost/frontend_dev.php/mymodule/myaction” |
getPathInfo () |
路径信息 | “/ mymodule里/ myaction” |
getReferer () ** |
介绍人 | “http://localhost/frontend_dev.php/” |
getHost () |
主机名 | “localhost” |
getScriptName () |
前置控制器路径和名称 | “frontend_dev.php” |
客户端浏览器信息 | ||
getLanguages () |
接受的语言数组 | 阵列( [0] => fr [1] => fr_FR [2] => en_US [3] => en) |
getCharsets () |
接受的字符集数组 | 阵列( [0] => iso-8859-1 [1] => utf-8 [2] => *) |
getAcceptableContentTypes () | 接受的内容类型数组 | 数组([0]=> text/xml [1] => text/html . |
*
使用原型,原型,Mootools和jQuery
**
有时被代理阻止
您不必担心您的服务器是否支持$ _SERVER
或者是_ENV美元
变量,或关于默认值或服务器兼容性问题的sfWebRequest
方法为您完成这一切。此外,它们的名称非常明显,您将不再需要浏览PHP文档来了解如何从请求中获取信息。欧宝官网下载app< / p >
用户会话
ob娱乐下载Symfony自动管理用户会话,并能够在用户请求之间保存持久数据。它使用内置的PHP会话处理机制,并对其进行了增强,使其更易于配置和使用。< / p >
访问用户会话
属性在操作中访问当前用户的会话对象getUser ()
方法的实例sfUser
类。该类包含一个参数holder,允许您在其中存储任何用户属性。在用户会话结束之前,此数据将对其他请求可用,如清单6-14所示。用户属性可以存储任何类型的数据(字符串、数组和关联数组)。它们可以为每个用户设置,即使该用户没有被识别。< / p >
清单6-14 - ThesfUser
对象可以保存跨请求存在的自定义用户属性< / p >
类mymoduleActions扩展sfActions{公共函数executeFirstPage(美元的请求){美元的昵称=美元的请求->getParameter(“昵称”);//在用户会话中存储数据这个美元->getUser()->setAttribute(“昵称”,美元的昵称);}公共函数executeSecondPage(){//从默认值的用户会话中检索数据美元的昵称=这个美元->getUser()->getAttribute(“昵称”,“匿名的懦夫”);}}
谨慎< / p >
您可以在用户会话中存储对象,但强烈不建议这样做。这是因为会话对象在请求之间被序列化了。当反序列化会话时,存储对象的类必须已经加载,但情况并非总是如此。此外,如果你存储了推进或学说对象,也可以有“停滞”对象。< / p >
像symfony中的许多getter一样,ob娱乐下载thegetAttribute ()
方法接受第二个参数,指定未定义属性时使用的默认值。要检查是否为用户定义了属性,请使用hasAttribute ()
方法。属性存储在可由getAttributeHolder ()
方法。它允许使用常用的参数持有者方法轻松地清除用户属性,如清单6-15所示。< / p >
清单6-15 -从User会话中删除数据< / p >
类mymoduleActions扩展sfActions{公共函数executeRemoveNickname(){这个美元->getUser()->getAttributeHolder()->删除(“昵称”);}公共函数executeCleanup(){这个美元->getUser()->getAttributeHolder()->清晰的();}}
默认情况下,用户会话属性也可在模板中通过sf_user美元
变量,用于存储当前sfUser
如清单6-16所示。< / p >
清单6-16 -模板也可以访问User会话属性< / p >
< p >你好,<?php回声sf_user美元->getAttribute(“昵称”)? >< / p >
Flash属性
用户属性经常出现的问题是,一旦不再需要该属性,就会清理用户会话。例如,您可能希望在通过表单更新数据后显示确认信息。当表单处理操作进行重定向时,将信息从此操作传递到其重定向到的操作的唯一方法是将信息存储在用户会话中。但是一旦显示确认消息,就需要清除该属性;否则,它将保持在会话中,直到过期。< / p >
flash属性是一个可以定义和忘记的临时属性,知道它将在下一个请求之后消失,并为未来留下干净的用户会话。在你的动作中,像这样定义flash属性:< / p >
这个美元->getUser()->setFlash(“通知”,美元的价值);
模板将被呈现并交付给用户,然后用户将向另一个操作发出新的请求。在第二个动作中,像这样获取flash属性的值:< / p >
美元的价值=这个美元->getUser()->getFlash(“通知”);
那就忘了它吧。在交付第二页之后,请注意
Flash属性将被刷新。即使在第二个操作期间不需要它,flash也会从会话中消失。< / p >
如果需要从模板中访问flash属性,请使用sf_user美元
对象:< / p >
<?php如果(sf_user美元->hasFlash(“通知”)):? ><?php回声sf_user美元->getFlash(“通知”)? ><?phpendif;? >
或者是:< / p >
<?php回声sf_user美元->getFlash(“通知”)? >
Flash属性是一种将信息传递到下一个请求的简洁方式。< / p >
会话管理
ob娱乐下载Symfony的会话处理特性对开发人员完全屏蔽了会话id的客户端和服务器存储。但是,如果您想修改会话管理机制的默认行为,仍然是可以的。这主要适用于高级用户。< / p >
在客户端,会话由cookie处理。symfob娱乐下载ony会话cookie被调用ob娱乐下载
,但可以通过编辑factories.yml
配置文件,如清单6-17所示。< / p >
清单6-17 -修改会话Cookie名称,在应用程序/前端/ config / factories.yml
all: storage: class: sfSessionStorage param: session_name: my_cookie
提示< / p >
会话启动(使用PHP函数)session_start ()
),只在auto_start
参数被设置为truefactories.yml
(这是默认情况)。如果需要手动启动用户会话,请禁用存储工厂的此设置。< / p >
ob娱乐下载Symfony的会话处理是基于PHP会话的。这意味着,如果您希望会话的客户端管理由URL参数而不是cookie处理,您只需要更改use_trans_sid
在php.ini中设置。请注意,不建议这样做。< / p >
会话。Use_trans_sid = 1
在服务器端,symfony默认将用户会话ob娱乐下载存储在文件中。的值可以将它们存储在数据库中类
参数factories.yml
,如清单6-18所示。< / p >
清单6-18 -更改服务器会话存储,在应用程序/前端/ config / factories.yml
all: storage: class: sfmysqlessionstorage param: db_table: session #会话存储表名database: propel #要使用的数据库连接名#可选参数db_id_col: sess_id #会话id存储列名db_data_col: sess_data #会话数据存储列名db_time_col: sess_time #会话时间戳存储列名
的数据库
设置定义要使用的数据库连接。ob娱乐下载Symfony将使用databases.yml
(参见第8章)来确定该连接的连接设置(主机、数据库名、用户和密码)。< / p >
可用的会话存储类为sfCacheSessionStorage
,sfMySQLSessionStorage
,sfMySQLiSessionStorage
,sfPostgreSQLSessionStorage
,sfPDOSessionStorage
;后者是首选。要完全禁用会话存储,可以使用sfNoStorage
类。< / p >
会话过期在30分钟后自动发生。此默认设置可以在同一环境中为每个环境修改factories.yml
配置文件,但这次在用户
如清单6-19所示。< / p >
清单6-19 - change Session Lifetime, in应用程序/前端/ config / factories.yml
all: user: class: myUser param: timeout: 1800 #会话超时时间(单位:秒
要了解更多关于工厂的知识,请参阅第19章。< / p >
操作安全
可以将执行操作的能力限制为具有某些特权的用户。symfony为此目的提供的工具允许创建安全ob娱乐下载的应用程序,其中用户需要在访问应用程序的某些特性或部分之前进行身份验证。保护应用程序需要两个步骤:声明每个操作的安全需求,并登录具有特权的用户,以便他们可以访问这些安全操作。< / p >
访问限制
在执行之前,每个操作都要经过一个特殊的过滤器,该过滤器检查当前用户是否有权限访问所请求的操作。在syob娱乐下载mfony中,特权由两部分组成:< / p >
- 安全操作要求对用户进行身份验证。
- 凭据被命名为安全特权,允许按组组织安全性。
通过创建和编辑名为YAML配置文件来限制对操作的访问security.yml
在模块中配置/
目录中。在此文件中,您可以指定用户必须为每个操作或目的满足的安全需求所有
行动。清单6-20给出了一个示例security.yml
.< / p >
清单6-20—设置访问限制,在应用程序/前端/模块/ mymodule里/ config / security.yml
read: is_secure: false #所有用户都可以请求read操作update: is_secure: true #更新操作仅针对已认证的用户delete: is_secure: true #仅针对已认证的用户凭据:admin #使用admin凭据All: is_secure: false #无论如何,false是默认值
操作在默认情况下是不安全的,因此当没有security.yml
或者没有提到一个动作,每个人都可以访问动作。如果有security.yml
, ob娱乐下载symfony将查找所请求操作的名称,如果它存在,则检查安全需求是否满足。当用户尝试访问受限制的操作时会发生什么取决于他的凭据:< / p >
- 如果用户通过了身份验证,并且拥有正确的凭据,则执行该操作。
- 如果用户没有被识别,他将被重定向到默认登录操作。
- 如果标识了用户,但没有适当的凭据,他将被重定向到默认的安全操作,如图6-1所示。
默认的登录和安全页面非常简单,您可能希望自定义它们。您可以配置在应用程序中权限不足时调用哪些操作settings.yml
通过更改如清单6-21所示的属性值。< / p >
图6-1 -默认安全动作界面< / p >
清单6-21 -默认安全动作定义在应用程序/前端/ config / settings.yml
All: .actions: login_module: default login_action: login secure_module: default secure_action: secure
授予访问
要访问受限制的操作,用户需要经过身份验证和/或具有某些凭据。类的方法来扩展用户的权限sfUser
对象。用户的身份验证状态由setAuthenticated ()
方法,可与isAuthenticated ()
.清单6-22显示了一个简单的用户身份验证示例。< / p >
清单6-22 -设置用户的认证状态< / p >
类myAccountActions扩展sfActions{公共函数executeLogin(美元的请求){如果(美元的请求->getParameter(“登录”)===“foobar”){这个美元->getUser()->setAuthenticated(真正的);}}公共函数executeLogout(){这个美元->getUser()->setAuthenticated(假);}}
凭证处理起来有点复杂,因为您可以检查、添加、删除和清除凭证。类的凭据方法如清单6-23所示sfUser
类。< / p >
清单6-23 -在Action中处理用户凭证< / p >
类myAccountActions扩展sfActions{公共函数executeDoThingsWithCredentials(){$ user=这个美元->getUser();//添加一个或多个凭证$ user->addCredential(“foo”);$ user->addCredentials(“foo”,“酒吧”);//检查用户是否有证书回声$ user->hasCredential(“foo”);= >真正的//检查用户是否拥有两个凭证回声$ user->hasCredential(数组(“foo”,“酒吧”));= >真正的//检查用户是否拥有其中一个凭据回声$ user->hasCredential(数组(“foo”,“酒吧”),假);= >真正的//删除证书$ user->removeCredential(“foo”);回声$ user->hasCredential(“foo”);= >假//删除所有凭据(在注销过程中有用)$ user->clearCredentials();回声$ user->hasCredential(“酒吧”);= >假}}
如果用户具有喷火
证书时,该用户将能够访问security.yml
需要证书。凭据还可以用于仅显示模板中经过授权的内容,如清单6-24所示。< / p >
清单6-24 -在模板中处理用户凭证< / p >
李< ul > < > < ?php回声link_to(“section1”,的内容/ section1 ')李李? > < / > < > < ?php回声link_to(“section2”,的内容/ section2 ')李? > < / ><?php如果(sf_user美元->hasCredential(“section3”)):? ><李> < ?php回声link_to(“section3”,的内容/ section3 ')李? > < / ><?phpendif;? >< / ul >
至于身份验证状态,通常在登录过程中向用户提供凭据。这就是为什么sfUser
对象经常被扩展为添加登录和注销方法,以便将用户的安全状态设置在一个集中的地方。< / p >
提示< / p >
在symfony插件ob娱乐下载中,sfGuardPlugin
而且sfDoctrineGuardPlugin
扩展会话类以方便登录和注销。更多信息请参阅第17章。< / p >
复杂的凭证
中使用的YAML语法security.yml
file允许您使用and类型或or类型关联将访问权限限制为具有组合凭据的用户。通过这样的组合,您可以构建一个复杂的工作流和用户权限管理系统——例如,一个内容管理系统(CMS)后台仅对具有管理员凭据的用户访问,其中文章只能由具有管理员凭据的用户编辑编辑器
证书,仅由拥有证书的人发布出版商
证书。示例如清单6-25所示。< / p >
清单6-25 -凭证组合语法< / p >
editArticle: credentials: [admin, editor] # admin AND editor publishArticle: credentials: [admin, publisher] # admin AND publisher userManagement: credentials: [[admin,超级用户]]# admin或超级用户
每当您添加新的方括号级别时,逻辑就会在AND和OR之间进行交换。所以你可以创建非常复杂的凭证组合,比如:< / p >
凭证:[[root,[供应商,[所有者,准所有者]],帐户]]# root或(供应商和(所有者或准所有者))或帐户
过滤器
安全流程可以理解为一个过滤器,所有请求在执行操作之前都必须通过该过滤器。根据在筛选器中执行的一些测试,请求的处理被修改了——例如,通过更改执行的操作(默认的
/安全
而不是在安全过滤器的情况下所请求的操作)。ob娱乐下载Symfony将这个想法扩展到筛选类。您可以指定在动作执行之前或在响应呈现之前执行的任意数量的筛选器类,并对每个请求执行此操作。您可以将过滤器视为打包某些代码的一种方式,类似于preExecute ()
而且postExecute ()
,但是在更高的级别上(针对整个应用程序而不是整个模块)。< / p >
过滤链
ob娱乐下载Symfony实际上将请求的处理视为一个过滤器链。当框架接收到请求时,第一个筛选器(通常是sfRenderingFilter
)执行。在某个时刻,它调用链中的下一个过滤器,然后是下一个,依此类推。当最后一个过滤器(总是sfExecutionFilter
)执行后,前一个过滤器可以完成,以此类推返回渲染过滤器。图6-3用一个序列图说明了这个想法,使用了一个人为的小过滤器链(真正的过滤器包含更多过滤器)。< / p >
图6-3 -样本过滤器链< / p >
这个过程证明了过滤器类的结构是正确的。它们都延伸了sfFilter
类,并包含一个execute ()
方法,期望filterChain美元
对象作为参数。在此方法的某个地方,过滤器通过调用filterChain - > execute ()
.示例请参见清单6-26。所以基本上,过滤器分为两部分:< / p >
- 调用之前的代码
filterChain - > execute ()
在操作执行之前执行。 - 调用后的代码
filterChain - > execute ()
在操作执行之后、呈现之前执行。
清单6-26 -过滤器类结构< / p >
类myFilter扩展sfFilter{公共函数执行(filterChain美元){//动作执行前要执行的代码...//执行链中的下一个过滤器filterChain美元->执行();//在动作执行之后,呈现之前执行的代码...}}
默认过滤器链定义在名为filters.yml
,如清单6-27所示。该文件列出了为每个请求执行的过滤器。< / p >
清单6-27 - Default Filter Chain, in前端/ config / filters.yml
通常,你会想要在这里插入你自己的过滤器cache: ~ execution: ~
这些声明没有参数(波浪号字符,~
,在YAML中表示“null”),因为它们继承了symfony核心中定义的参数。ob娱乐下载在核心,symfony定义ob娱乐下载类
而且参数
每个过滤器的设置。例如,清单6-28显示了呈现
过滤器。< / p >
清单6-28 -渲染过滤器的默认参数,在sfConfig: get (sf_ob娱乐下载symfony_lib_dir) / config / config / filters.yml
渲染:类:sfRenderingFilter #过滤器类参数:#过滤器参数类型:渲染
通过保留空值(~
)在申请表内filters.yml
,您告诉symfonyob娱乐下载使用内核中定义的默认设置应用筛选器。< / p >
你可以用多种方式自定义过滤器链:< / p >
属性来禁用链中的某些过滤器
启用:假
参数。例如,禁用安全
过滤器,写:< / p >Security: enabled: false
中不删除条目
filters.yml
禁用过滤器;ob娱乐下载在这种情况下,Symfony会抛出一个异常。< / p >- 在链中的某个地方添加自己的声明(通常在
安全
Filter)来添加自定义过滤器(如下一节所述)。请注意呈现
过滤器必须是第一个条目,并且执行
Filter必须是过滤器链的最后一个条目。 - 重写默认过滤器的默认类和参数(特别是修改安全系统和使用自己的安全过滤器)。
创建自己的过滤器
构建过滤器非常简单。创建类似于清单6-26所示的类定义,并将其放置在项目的一个类定义中lib /
文件夹,以利用自动加载功能。< / p >
由于操作可以转发或重定向到另一个操作,从而重新启动整个过滤器链,因此您可能希望将自己的过滤器的执行限制在请求的第一个操作调用上。的isFirstCall ()
方法sfFilter
类为此目的返回一个布尔值。这个调用只在操作执行之前才有意义。< / p >
通过一个示例,这些概念更加清晰。清单6-29显示了一个过滤器,用于自动记录具有特定MyWebSite
Cookie,它应该是由登录操作创建的。这是实现登录表单中提供的“记住我”功能的一种基本但有效的方法。< / p >
清单6-29 -样本过滤器类文件,保存在应用程序/前端/ lib / rememberFilter.class.php
类rememberFilter扩展sfFilter{公共函数执行(filterChain美元){//只执行一次过滤器如果(这个美元->isFirstCall()){//过滤器不能直接访问请求和用户对象。//你需要使用context对象来获取它们美元的请求=这个美元->getContext()->getRequest();$ user=这个美元->getContext()->getUser();如果(美元的请求->getCookie(“MyWebSite”)){//登录$ user->setAuthenticated(真正的);}}//执行下一个过滤器filterChain美元->执行();}}
在某些情况下,不是继续执行过滤器链,而是需要转发到过滤器末尾的特定操作。sfFilter
没有转发()
方法,但sfController
,所以你可以简单地通过调用以下命令来实现:< / p >
返回这个美元->getContext()->getController()->向前(“mymodule里”,“myAction”);
请注意< / p >
的sfFilter
类有一个初始化()
方法,在创建筛选器对象时执行。如果需要处理过滤器参数(定义在filters.yml
(如下文所述)以你自己的方式。< / p >
过滤器激活和参数
创建筛选器文件不足以激活它。需要将筛选器添加到筛选器链中,为此,必须在filters.yml
,位于应用程序或模块中配置/
目录,如清单6-30所示。< / p >
清单6-30 -样本过滤器激活文件,保存在应用程序/前端/ config / filters.yml
渲染:~安全:~记住:#过滤器需要一个唯一的名称类:remember filter参数:cookie_name: MyWebSite条件:%APP_ENABLE_REMEMBER_ME%缓存:~执行:~
激活后,将对每个请求执行筛选器。筛选器配置文件下可以包含一个或多个参数定义参数
关键。类可以获取这些参数的值getParameter ()
方法。清单6-31演示了如何获取筛选器参数值。< / p >
清单6-31 -获取参数值,in应用程序/前端/ lib / rememberFilter.class.php
类rememberFilter扩展sfFilter{公共函数执行(filterChain美元){/ /……如果(美元的请求->getCookie(这个美元->getParameter(“cookie_name”))){/ /……}/ /……}}
的条件
参数将由过滤器链测试,以确定是否必须执行过滤器。因此,过滤器声明可以依赖于应用程序配置,如清单6-30所示。只有当您的应用程序app.yml
显示了这个:< / p >
所有:enable_remember_me: true
样品过滤器
过滤器特性对于为每个操作重复代码非常有用。例如,如果您使用远程分析系统,则可能需要在每个页面中放入调用远程跟踪器脚本的代码片段。你可以把这段代码放在全局布局中,但它会对整个应用程序都是活动的。或者,您可以将它放在一个过滤器中,如清单6-32所示,并在每个模块的基础上激活它。< / p >
清单6-32 -谷歌分析过滤器< / p >
类sfGoogleAnalyticsFilter扩展sfFilter{公共函数执行(filterChain美元){//在行动之前没有什么事情要做filterChain美元->执行();//用跟踪器代码装饰响应googleCode美元='