搜尋
首頁後端開發php教程Symfony控制層的進一步解析

Symfony控制層的進一步解析

Jun 08, 2018 pm 02:54 PM
symfony

這篇文章主要介紹了Symfony控制層,結合大量實例代碼深入分析了Symfony控制器的常見使用技巧與相關注意事項,需要的朋友可以參考下

本文深入分析了Symfony控制層。分享給大家供大家參考,具體如下:

Symfony中控制層包含了連接業務邏輯與表現的程式碼,控制層為不同的使用分成了幾個不同的部分。

1. 前端控制器是指向應用的唯一入口
2. 動作包含了應用的邏輯,他們檢查請求的完整性並準備好表示層需要的資料
3. 請求、回應和Session物件提供存取請求參數、回應參數以及持久的使用者數據,這些資料在控制層使用的很普遍
4. 過濾器是每個請求都要執行的程式碼的一部分,無論在動作前還是在動作後。可以自創過濾器。

前端控制器

所有WEB請求都將被前端控制器捕獲,前端控制是給定環境下整個應用的唯一入口點。當前端控制接到一個請求,他使用路由系統來匹配使用者輸入的URL的動作名稱和模組名稱。例如:

http://localhost/index.php/mymodule/myAction

URL呼叫了index.php腳本(也就是前端控制器),他被理解為:動作-myAction ,模組-mymodule

前端控制器的工作細節

前端控制器分發請求,他只執行那些通用的和共同的程式碼,包括:

1. 定義核心常數
2. 定位symfony函式庫
3. 載入與初始化核心框架類別
4. 載入設定資訊
5. 解碼請求URL,取得要執行的動作與請求參數
6. 如果動作不存在則專項404錯誤
7. 啟動過濾器(例如,如果需要身份認證)
8. 執行過濾器,第一次
9.執行動作,遞交視圖
10. 執行過濾器,第二次
11. 輸出回應。

預設前端控制器

預設前端控制器叫作index.php,在專案的WEB/目錄,他是一個簡單的PHP文件,如下:

<?php
define(&#39;SF_ROOT_DIR&#39;,  realpath(dirname(__FILE__).&#39;/..&#39;));
define(&#39;SF_APP&#39;,     &#39;myapp&#39;);
define(&#39;SF_ENVIRONMENT&#39;, &#39;prod&#39;);
define(&#39;SF_DEBUG&#39;,    false);
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.&#39;apps&#39;.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.&#39;config&#39;.DIRECTORY_SEPARATOR.&#39;config.php&#39;);
sfContext::getInstance()->getController()->dispatch();

這個檔案在前面已經介紹過了:先定義幾個變量,然後引入應用的配置config.php,最後呼叫sfController(這是symfony MVC架構中的核心控制器物件)的dispatch()方法。最後一步將被過濾器鏈捕獲。

呼叫另一個前端控制器來更換環境

每個環境存在一個前端控制器,環境定義在SF_ENVIRONMENT常數中。

創建新環境就和創建新的前端控制器一樣簡單,例如,你需要一個staging環境以使你的應用上線之前可以被客戶測試。若要建立staging環境,拷貝web/myapp_dev.php到web/myapp_staging.php,然後修改SF_ENVIRONMENT常數為staging。現在,你可以在所有的設定檔中增加staging段了設定新環境所需要的東西,看看下面的範例:

## app.yml
staging:
 mail:
  webmaster:  dummy@mysite.com
  contact:   dummy@mysite.com
all:
 mail:
  webmaster:  webmaster@mysite.com
  contact:   contact@mysite.com
##查看结果
http://localhost/myapp_staging.php/mymodule/index

批次檔

在命令列或排程任務中存取symfony類別和特性的時候需要使用批次檔。批次檔的開頭與前端控制器的開頭一樣-除了前端控制器的分發部分不要。

<?php
define(&#39;SF_ROOT_DIR&#39;,  realpath(dirname(__FILE__).&#39;/..&#39;));
define(&#39;SF_APP&#39;,     &#39;myapp&#39;);
define(&#39;SF_ENVIRONMENT&#39;, &#39;prod&#39;);
define(&#39;SF_DEBUG&#39;,    false);
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.&#39;apps&#39;.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.&#39;config&#39;.DIRECTORY_SEPARATOR.&#39;config.php&#39;);
// 添加批处理代码

動作(Actions)

#動作是應用程式的心臟,因為他包含了所有應用程式的邏輯。他們使用模型並定義變數給視圖。當在應用程式中使用一個請求,URL中定義了一個動作和請求的參數。

動作類別

動作是moduleNameActions類別(繼承自sfActions類別)中名為executeActionName的方法,以模組組織在一起,模組動作類別儲存在actions目錄的actions.class.php檔案中。

只有WEB目錄下的檔案能夠被外部訪問,前端控制腳本、圖片、樣式表和JS檔案是公開的,即使PHP中方法不區分大小寫,但symfony中區分,所以不要忘了動作方法必須以小寫execute開始,緊跟著是首字母大寫的動作名稱。

如果動作類別變得很大,你應該做一些分解並把程式碼放在模型層,動作應該盡量的保證短小(幾行最好),所有的業務邏輯都應該放在模型層中。

可選的動作類別語法

可以一個動作一個文件,文件的名稱為動作名稱加Action.class.php,類別名稱為動作名稱加Action ,只是記得類別繼承自sfAction而非sfActions。

在動作中取得資訊

動作類別提供了一個存取控制器相關資訊與核心symfony物件的方法,以下示範如何使用:

<?php
define(&#39;SF_ROOT_DIR&#39;,  realpath(dirname(__FILE__).&#39;/..&#39;));
define(&#39;SF_APP&#39;,     &#39;myapp&#39;);
define(&#39;SF_ENVIRONMENT&#39;, &#39;prod&#39;);
define(&#39;SF_DEBUG&#39;,    false);
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.&#39;apps&#39;.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.&#39;config&#39;.DIRECTORY_SEPARATOR.&#39;config.php&#39;);
class mymoduleActions extends sfActions
{
 public function executeIndex()
 {
  // Retrieving request parameters
  $password  = $this->getRequestParameter(&#39;password&#39;);
  // Retrieving controller information
  $moduleName = $this->getModuleName();
  $actionName = $this->getActionName();
  // Retrieving framework core objects
  $request   = $this->getRequest();
  $userSession = $this->getUser();
  $response  = $this->getResponse();
  $controller = $this->getController();
  $context   = $this->getContext();
  // Setting action variables to pass information to the template
  $this->setVar(&#39;foo&#39;, &#39;bar&#39;);
  $this->foo = &#39;bar&#39;;      // Shorter version
 }
}

上下文:

在前端控制器中一個sfContext::getInstance()的呼叫。在動作中,getContext()方法是單例模式(即所有的呼叫都是第一個實例,這對於儲存指向與給定請求相關的symfony核心物件的索引的情況是非常有用的)。

sfController:控制器对象 (->getController())
sfRequest:请求对象 (->getRequest())
sfResponse:响应对象 (->getResponse())
sfUser:用户Session对象 (->getUser())
sfDatabaseConnection:数据库链接 (->getDatabaseConnection())
sfLogger:日志对象 (->getLogger())
sfI18N:国际化对象(->getI18N())
可以在代码的任何位置放置sfContext::getInstance()。

动作终止

当动作执行完成后会出现各种行为,动作方法的返回值决定视图如何被实施。sfView类的常量经常被指定与模板来显示动作的结果。如果一个默认视图被调用,动作应该以下面的代码结束:

return sfView::SUCCESS;

Symfony将寻找actionNameSuccess.php的模板,这是默认的行为,所以如果你忽略了return语句,symfony也将查找actionNameSuccess.php模板,空动作也将触发同样的行为,如下:

# 将调用indexSuccess.php模板
public function executeIndex()
{
 return sfView::SUCCESS;
}
# 将调用listSuccess.php模板
public function executeList()
{
}

如果要调用错误视图,动作将以下面语句结束:

# symfony将查找actionNameError.php模板
return sfView::ERROR;

调用自定义视图

# symfony将查找actionNameMyResult.php模板
return &#39;MyResult&#39;;

如果动作不想调用模板(比如批处理和任务计划cron),可以使用下面的语句

return sfView::NONE;

上面情况下,视图层将被忽略,HTML代码可以直接在动作中输出,symfony提供了一个特殊的方法renderText()来实现。这种情况比较适用于AJAX交互。

public function executeIndex()
{
 echo "<html><body>Hello, World!</body></html>";
 return sfView::NONE;
}
// 等价方法
public function executeIndex()
{
 return $this->renderText("<html><body>Hello, World!</body></html>");
}

有些时候你需要发送空的响应但包含定义的头信息(特别是X-JSON头),定义头通过sfResponse对象,并且放回sfView::HEADER_ONLY常量:

public function executeRefresh()
{
 $output = &#39;<"title","My basic letter"],["name","Mr Brown">&#39;;
 $this->getResponse()->setHttpHeader("X-JSON", &#39;(&#39;.$output.&#39;)&#39;);
 return sfView::HEADER_ONLY;
}

如果动作必须呈交特定模板,使用setTemplate()方法来忽略return语句:

$this->setTemplate(&#39;myCustomTemplate&#39;);

跳向另一个动作

有些情况下,动作以请求一个新的动作作为结束,例如,一个动作处理表单的POST提交在更新数据库后通常会转向到另一个动作。另一个例子是动作别名:index动作经常是来完成显示,所以实际上是跳向了list动作。

有两种方式来实现另一个动作的执行:

① 向前(Forwards)方式

$this->forward(&#39;otherModule&#39;,&#39;otherAction&#39;);

② 重定向(Redirection)方式

$this->redirect(&#39;otherModule/otherAction&#39;);
$this->redirect(&#39;http://www.google.cn&#39;);

Forward和redirect后面的语句将不会被执行,你可以理解为他们等价于return语句。他们抛出sfStopException异常来停止动作的执行

两者的区别:forward是内部的处理,对用户是透明的,即用户不会感觉到动作发生了变化,URL也不会更改。相反,redirection是动作的真正跳转,URL的改变是最直接的反映。

如果动作是被POST提交表单调用的,你最好使用redirect。这样,如果用户刷新了结果页面,POST表单不会被重复提交;另外,后退按钮也能很好的返回到表单显示页面而不是一个警告窗口询问用户是否重新提交POST请求。

Forward404()方法是一种常用的特殊forward,他跳到“页面无法找到”动作。

经验说明,很多时候一个动作会在验证一些东西后redirect或者forward另一个动作。这就是为什么sfActions类有很多的方法命名为forwardIf(), forwardUnless(), forward404If(), forward404Unless(), redirectIf(), 和 redirectUnless(),这些方法简单地使用一个测试结果参数true(那些xxxIf()方法)或false(那些xxxUnless()方法):

public function executeShow()
{
 $article = ArticlePeer::retrieveByPK($this->getRequestParameter(&#39;id&#39;));
 $this->forward404If(!$article);
}
public function executeShow()
{
 $article = ArticlePeer::retrieveByPK($this->getRequestParameter(&#39;id&#39;));
 $this->forward404Unless($article);
}

这些方法不仅仅是减少你的代码行数,他们还使得你的程序更加易读。

当动作调用forward404()或者其他类似方法,symfony抛出管理404响应的sfError404Exception异常,也就是说如果你想显示404信息,你无需访问控制器,你只是抛出这个异常即可。

模块中多个动作重复代码的处理方式

preExecute()与postExecute()方法是一个模块中多个动作共同的东西。可以在调用executeAction()之前和之后执行。

class mymoduleActions extends sfActions
{
 public function preExecute()
 {
  // 这里的代码在每一个动作调用之前执行
  ...
 }
 public function executeIndex()
 {
  ...
 }
 public function executeList()
 {
  ...
  $this->myCustomMethod(); // 调用自定义的方法
 }
 public function postExecute()
 {
  // 这里的代码会在每个动作结束后执行
  ...
 }
 protected function myCustomMethod()
 {
  // 添加自己的方法,虽然他们没有以execute开头
  // 在这里,最好将方法定义为protected(保护的)或者private(私有的)
  ...
 }
}

访问请求

getRequestParameter(“myparam”)方法常用来获取myparam参数的值,实际上这个方法是一系列请求调用参数仓库的代理:getRequest()->getParameter(“myparam”)。动作类使用sfWebRequest访问请求对象,通过getRequest()访问他们的方法。

sfWebRequest对象的方法

 getMethod()Request#Request#getHttpHeader('Server')#給定HTTPCookieAJAX##  getCharsets()

方法名

功能

#輸入範例

Request Information

 

getMethod()

物件

Returns sfRequest::GET or sfRequest::POST constants

#getMethodName()

物件名稱

'POST'

頭的值

##'Apache /2.0.59 (Unix) DAV/2 PHP/5.1.6'

getCookie('foo')

指定名稱
的值

' bar'

isXmlHttpRequest()*

是否是
請求?

true

#isSecure()

是否是######SSL######請求##################true# #####

Request Parameters

## 

hasParameter('foo')

參數是否在請求中有

true

getParameter(' foo')

指定參數的值

'bar'

getParameterHolder()->getAll()

所有請求參數的陣列

 

URI-Related Information

 

getUri()

完整URI

#'http://localhost/ myapp_dev.php/mymodule/myaction'

getPathInfo()

#路徑資訊

'/mymodule/myaction'

##getReferer()**

來自那裡?

'http://localhost/myapp_dev.php/'

getHost()

#

主機名稱

'localhost'

getScriptName()

#前端控制器路徑與名稱

'myapp_dev.php'

#Client Browser Information

getLanguages()

#可接受語言的列表

#Array( [0] => fr [1] => fr_FR [2] => en_US [3] => en )

##########可接受字元集的清單###################Array( [0] => ISO-8859-1 [1] => UTF-8 [2] => * )#########################getAcceptableContentTypes()########## #########可接受內容類型陣列############### ####################### ###############

sfActions类提供了一些代理来快速地访问请求方法

class mymoduleActions extends sfActions
{
 public function executeIndex()
 {
  $hasFoo = $this->getRequest()->hasParameter(&#39;foo&#39;);
  $hasFoo = $this->hasRequestParameter(&#39;foo&#39;); // Shorter version
  $foo  = $this->getRequest()->getParameter(&#39;foo&#39;);
  $foo  = $this->getRequestParameter(&#39;foo&#39;); // Shorter version
 }
}

对于文件上传的请求,sfWebRequest对象提供了访问和移动这些文件的手段:

class mymoduleActions extends sfActions
{
 public function executeUpload()
 {
  if ($this->getRequest()->hasFiles())
  {
   foreach ($this->getRequest()->getFileNames() as $fileName)
   {
    $fileSize = $this->getRequest()->getFileSize($fileName);
    $fileType = $this->getRequest()->getFileType($fileName);
    $fileError = $this->getRequest()->hasFileError($fileName);
    $uploadDir = sfConfig::get(&#39;sf_upload_dir&#39;);
    $this->getRequest()->moveFile(&#39;file&#39;, $uploadDir.&#39;/&#39;.$fileName);
   }
  }
 }
}

用户Session

Symfony自动管理用户Session并且能在请求之间为用户保留持久数据。他使用PHP内置的Session处理机制并提升了此机制,这使得symfony的用户Session更好配置更容易使用。

访问用户Session

当前用户的Session对象在动作中使用getUser()方法访问,他是sfUser类的一个实例。sfUser类包含了允许存储任何用户属性的参数仓库。用户属性能够存放任何类型的数据(字符串、数组、关联数组等)。

sfUser对象能够跨请求地保存用户属性

class mymoduleActions extends sfActions
{
 public function executeFirstPage()
 {
  $nickname = $this->getRequestParameter(&#39;nickname&#39;);
  // Store data in the user session
  $this->getUser()->setAttribute(&#39;nickname&#39;, $nickname);
 }
 public function executeSecondPage()
 {
  // Retrieve data from the user session with a default value
  $nickname = $this->getUser()->getAttribute(&#39;nickname&#39;, &#39;Anonymous Coward&#39;);
 }
}

可以把对象存放在用户Session中,但这往往让人气馁,因为Session在请求之间被序列化了并且存储在文件中,当Session序列化时,存储对象的类必须已经被加载,而这很难被保证。另外,如果你存储了Propel对象,他们可能是“延迟”的对象。

与symfony中的getter方法一样,getAttribute()方法接受第二个参数作为默认值(如果属性没有被定义时)使用。判断属性是否被定义使用hasAttribute()方法。属性存储在参数仓库可使用getAttributeHolder()方法访问,可以使用基本参数仓库方法很简单的清除用户属性:

class mymoduleActions extends sfActions
{
 public function executeRemoveNickname()
 {
  $this->getUser()->getAttributeHolder()->remove(&#39;nickname&#39;);
 }
 public function executeCleanup()
 {
  $this->getUser()->getAttributeHolder()->clear();
 }
}

用户Session属性在模板中默认通过$sf_user变量访问,他存储了当前的sfUser对象

<p>
 Hello, <?php echo $sf_user->getAttribute(&#39;nickname&#39;) ?>
</p>

如果只想在当前请求中存储信息,你更应该使用sfRequest类,他也有getAttribute()和setAttribute()方法,只有在请求之间持久存储信息时才适合sfUser对象。

Flash属性

Flash属性是一种短命属性,他会在最近的一次请求后消失,这样可以保持你的Session清洁

$this->setFlash(&#39;attrib&#39;, $value);

在另一个动作中获取Flash属性:

$value = $this->getFlash(&#39;attrib&#39;);

一旦传入了另一个动作,Flash属性就消失了,即使你的Session还不过期。在模板中访问flash属性使用$sf_flash对象。

<?php if ($sf_flash->has(&#39;attrib&#39;)): ?>
 <?php echo $sf_flash->get(&#39;attrib&#39;) ?>
<?php endif; ?>

或者

<?php echo $sf_flash->get(&#39;attrib&#39;) ?>

Session管理

Symfony的Session处理特性对开发者完全掩盖了客户端与服务端的SessionID存储,当然,如果你非要去改Session管理机制的默认行为也不是不可能,高级用户经常这么干。

客户端,Session被Cookies处理(handle)。Symfony的Session Cookie叫做symfony,可以在factories.yml中修改。

all:
 storage:
  class: sfSessionStorage
  param:
   session_name: 自定义Cookie名字

Symfony的Session基于PHP的Session设置,也就是说如果希望客户端使用URL参数方式取代Cookies,你必须修改php.ini文件的use_trans_sid参数,不建议这么做。

在服务器端,symfony默认使用文件存储用户Session,可以通过修改factories.yml文件承担class参数来使用数据库存储:apps/myapp/config/factories.yml

all:
 storage:
  class: sfMySQLSessionStorage
  param:
   db_table: SESSION_TABLE_NAME   # 存储Session的表
   database: DATABASE_CONNECTION   # 数据库的名称
Class名称可以是:fMySQLSessionStorage, sfPostgreSQLSessionStorage和sfPDOSessionStorage;后面是首选方式。
Session超时的修改和调整:apps/myapp/config/settings.yml
default:
 .settings:
  timeout:   1800      #Session超时 单位秒

动作的安全

动作的执行可以被限定在具有一定权限的用户。Symfony为此提供的工作允许创建安全的应用,用户只有在通过认证之后才能防伪应用的某些特性或部分。设置安全的应用需要两步:定义动作需要的安全和使用具有权限的用户登录。

访问限制

在每个动作执行前都会被一个特定的过滤器来检查当前用户是否具有访问的权限,symfony中权限有两个部分组成:

① 安全动作只有被授权用户可以访问
② 凭证允许分组管理权限

通过创建或者修改config目录下的security.yml文件即可简单的完成安全访问限制。在文件中可以设置某个动作或者所有动作是否需要授权。

Apps/myapp/modules/mymodule/config/security.yml

read:
 is_secure:  off    # 所有用户都可以请求Read动作
update:
 is_secure:  on    # update动作只能有认证的用户访问
delete:
 is_secure:  on    # 同update
 credentials: admin   # 具有admin凭证
all:
 is_secure: off    # 无需授权

用户访问一个需要授权的动作将依据用户的权限:

① 用户已登录并且凭证符合则动作能执行
② 如果用户没有登录则转向默认登录动作
③ 如果用户已登录但凭证不符合则会转向默认的安全动作

转向将根据apps/myapp/config/settings.yml文件来决定

all:
 .actions:
  login_module:      default
  login_action:      login
  secure_module:     default
  secure_action:     secure

授权

为某用户授权:

class myAccountActions extends sfActions
{
 public function executeLogin()
 {
  if ($this->getRequestParameter(&#39;login&#39;) == &#39;foobar&#39;) //判断根据具体需求而定
  {
   $this->getUser()->setAuthenticated(true);  //设置用户为已认证
  }
 }
 public function executeLogout()
 {
  $this->getUser()->setAuthenticated(false); //设置用户为未认证
 }
}

在动作中处理凭证:

class myAccountActions extends sfActions
{
 public function executeDoThingsWithCredentials()
 {
  $user = $this->getUser();
  // 添加凭证
  $user->addCredential(&#39;foo&#39;);
  $user->addCredentials(&#39;foo&#39;, &#39;admin&#39;);  //添加多个凭证
  // 判断是否具有凭证
  echo $user->hasCredential(&#39;foo&#39;);           =>  true
  // 判断是否具有多个凭证
  echo $user->hasCredential(array(&#39;foo&#39;, &#39;admin&#39;));    =>  true
  // Check if the user has one of the credentials
  echo $user->hasCredential(array(&#39;foo&#39;, &#39;admin&#39;), false); =>  true
  // 删除凭证
  $user->removeCredential(&#39;foo&#39;);
  echo $user->hasCredential(&#39;foo&#39;);           =>  false
  // 删除所有凭证 (被用在注销处理过程中)
  $user->clearCredentials();
  echo $user->hasCredential(&#39;admin&#39;);           =>  false
 }
}

如果用户具有'admin'凭证,他就可以访问security.yml文件中设定只有admin可以访问的动作。凭证也经常用来在模板中显示授权信息

<ul>
 <li><?php echo link_to(&#39;section1&#39;, &#39;content/section1&#39;) ?></li>
 <li><?php echo link_to(&#39;section2&#39;, &#39;content/section2&#39;) ?></li>
 <?php if ($sf_user->hasCredential(&#39;section3&#39;)): ?>
 <li><?php echo link_to(&#39;section3&#39;, &#39;content/section3&#39;) ?></li>
 <?php endif; ?>
</ul>

sfGuardPlugin插件扩展了session类,使得登录与注销的处理变得简单。

复杂的凭证

在security.yml文件中,可以使用AND或者OR来组合各种凭证,这样就可以建立复杂的业务流和用户权限管理系统。例如,CMS系统后台办公自由具有admin凭证用户访问,文章的编辑必须有editor凭证,发布只能有有publisher凭证的用户,代码如下:

editArticle:
 credentials: [ admin, editor ]       # admin 和 editor
publishArticle:
 credentials: [ admin, publisher ]      # admin 和 publisher
userManagement:
 credentials: [[ admin, superuser ]]     # admin 或者 superuser

每次添加新的[],凭证之间的关系在AND和OR之间切换,这样可以创建及其复杂的凭证组合关系:

credentials: [[root, [supplier, [owner, quasiowner]], accounts]]
       # root 或者 (supplier 和 (owner 或者 quasiowner)) 或者 accounts

注:【和】所有凭证都满足,【或】满足其中的一个凭证。

验证和错误处理方法

验证输入是重复且单调的事情,symfony提供了内置的请求验证系统,使用动作类的方法。

看个例子,当一个用户请求myAction,symfony首先去查找validateMyAction()方法,如果找到了就执行,根据返回结果来决定如何往下走:如果返回真则executeMyAction()被执行,否则handleErrorMyAction()被执行,并且,如果找不到handleErrorMyAction,symfony则去查找普通handleError方法,如果还不存在则简单返回sfView::ERROR并递交myActionError.php模板,看下图:

说明:

① validateActionName是验证方法,是ActionName被请求的第一个查找方法,如果不存在则直接执行动作方法。

② handleErrorActionName方法,如果验证失败则查找此方法,如果不存在则Error模板被显示

③ executeActionName是动作方法,对于动作他必须存在。

看段代码:

class mymoduleActions extends sfActions
{
 public function validateMyAction()
 {
  return ($this->getRequestParameter(&#39;id&#39;) > 0);
 }
 public function handleErrorMyAction()
 {
  $this->message = "Invalid parameters";
  return sfView::SUCCESS;
 }
 public function executeMyAction()
 {
  $this->message = "The parameters are correct";
 }
}

可以在验证方法中加入任何代码,但最终只要返回true或者false即可。因为是sfActions类的方法,所以可以访问sfRequest和sfUser对象,这样将对于输入与上下文验证非常有利。

过滤器

安全处理可以被认为是请求到动作执行之前必须经过的一个过滤器。实际上可以在动作执行前(后)设置任意多个的过滤器。

过滤器链

Symfony实际上将请求处理看作是过滤器链。框架收到请求后第一个过滤器(通常是sfRenderingFilter)被执行,在某些时候他调用下一个过滤器,依次下去。当最后一个过滤器(通常是sfExecutionFilter)执行后,前一个过滤器结束,依次返回去知道rending过滤器。

所有的过滤器均继承自sfFilter类并且都包含了execute()方法,此方法之前的代码在动作(action)之前执行,此方法之后的代码在动作之后执行,看一段代码(下一节中要用到,取名myFilter代码):

class myFilter extends sfFilter
{
 public function execute ($filterChain)
 {
  // 在动作之前执行的代码
  ...
  // 执行下一个过滤器
  $filterChain->execute();
  // 在动作之后执行的代码
  ...
 }
}

过滤器链在/myapp/config/filters.yml文件中定义:

rendering: ~
web_debug: ~
security: ~
# 在这里插入你自己的过滤器
cache:   ~
common:  ~
flash:   ~
execution: ~

这些声明都没有参数(~在symfony中的意思是null,将采用默认的值),他们都继承自symfony核心定义的参数,symfony为每一个过滤器(除了自定义过滤器)定义了类和参数,他们在$sf_symfony_data_dir/config/filter.yml文件。

自定义过滤器链的方法:

① 设置过滤器的enabled参数为off将禁用过滤器,例如:

Web_debug:
 enabled: off

你还可以通过settings.yml文件达到此目的,修改web_deug、use_security、cache和use_flash的设置即可,应为每一个默认过滤器都有一个condition参数来自上面的配置。

② 不要通过删除filters.yml文件中的过滤器来禁用该过滤器,symfony将抛出异常

③ 可以自定义过滤器,但无论如何rendering必须是第一个,而execution必须是最后一个

④ 为默认过滤器重写默认类和参数(特别是修改security系统和用户的安全验证过滤器)

创建自定义过滤器

通过创建myFilter的类可以非常简单的常见自定义的过滤器,把类文件放在项目的lib文件夹可以充分利用symfony提供的自动加载特性。

由于动作之间可以互相跳转,因此过滤器链会在每一个请求中循序执行。但更多的时候可能需要是第一次请求的时候执行自定义的过滤器,这时候使用sfFilter类的isFirstCall()方法。看下面代码:apps/myapp/lib/rememberFilter.class.php(例子)

class rememberFilter extends sfFilter
{
 public function execute($filterChain)
 {
  // 通过调用isFirstCall方法保证只执行一次
  if ($this->isFirstCall())
  {
   // 过滤器不直接访问请求和用户对象,你需要使用context对象获取
   // You will need to use the context object to get them
   $request = $this->getContext()->getRequest();
   $user  = $this->getContext()->getUser();
   if ($request->getCookie(&#39;MyWebSite&#39;))
   {
    // 登入
    $user->setAuthenticated(true);
   }
  }
  // 执行下一个过滤器
  $filterChain->execute();
 }
}

有时候需要在一个过滤器执行之后跳往另一个动作而不是下一个过滤器。sfFilter不包含forward方法,但sfController包含,使用下面的语句:

return $this->getContext()->getController()->forward(&#39;mymodule&#39;, &#39;myAction&#39;);

sfFilter类有一个initialize方法,在对象创建的时候执行,可以在自定义的过滤器中覆盖此方法以达到更加灵活地设置参数的目的。

过滤器激活及参数

过滤器创建后还必须进行激活,在apps/myapp/config/filters.yml文件中:

rendering: ~
web_debug: ~
security: ~
remember:         # Filters need a unique name
 class: rememberFilter
 param:
  cookie_name: MyWebSite
  condition:  %APP_ENABLE_REMEMBER_ME%
cache:   ~
common:  ~
flash:   ~
execution: ~

自定义过滤器中的参数可以在过滤器代码中使用getParameter方法获取:

apps/myapp/lib/rememberFilter.class.php

class rememberFilter extends sfFilter
{
 public function execute ($filterChain)
 {
   ...
   if ($request->getCookie($this->getParameter(&#39;cookie_name&#39;)))
   ...
 }
}

Condition参数被过滤器链测试来决定是否必须被执行。因此自定义过滤器声明能够依赖一个应用配置。要是过滤器执行,记得在应用的app.yml中加入:

all:
 enable_remember_me: on

过滤器示例

如果想在项目中包含特定的代码,你可以通过过滤器实现(layout方式需要在每一个应用中都要设置),看下面的代码:

class sfGoogleAnalyticsFilter extends sfFilter
{
 public function execute($filterChain)
 {
  // 在动作之前什么也不做
  $filterChain->execute();
  // 使用下面的代码修饰响应
  $googleCode = &#39;
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
 _uacct="UA-&#39;.$this->getParameter(&#39;google_id&#39;).&#39;";urchinTracker();
</script>&#39;;
  $response = $this->getContext()->getResponse();
  $response->setContent(str_ireplace(&#39;</body>&#39;, $googleCode.&#39;</body>&#39;,$response->getContent()));
  }
}

这不是一种好的方式,因为在非HTML响应中不适用,只是一个例子而已。

下个例子是转换http请求到https请求

class sfSecureFilter extends sfFilter
{
 public function execute($filterChain)
 {
  $context = $this->getContext();
  $request = $context->getRequest();
  if (!$request->isSecure())
  {
   $secure_url = str_replace(&#39;http&#39;, &#39;https&#39;, $request->getUri());
   return $context->getController()->redirect($secure_url);
   // We don&#39;t continue the filter chain
  }
  else
  {
   // The request is already secure, so we can continue
   $filterChain->execute();
  }
 }
}

过滤器广泛地应用于插件,允许全面地扩展应用的特性。

模块配置

一些模块行为依赖配置,要修改他们必须在模块的config目录下建立module.yml并为每一个环境(或者all)定义设置。

看个例子:apps/myapp/modules/mymodule/config/module.yml

all:         #对所有环境
 enabled:   true
 is_internal: false
 view_name:  sfPHP

Enable参数允许你在模块中禁用所有动作,这样所有动作都将专项到module_disabled_module/module_disabled_action动作(定义在settings.yml)

Is_internal参数定义动作只能内部调用,比如发送邮件只能有另一个动作调用而不是外部的直接调用。

View_name参数定义了view类,类必须继承自sfView。覆盖他后你将可以使用具有其他模板引擎其他的view系统,比如smarty。

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

关于ThinkPHP的控制器解析

以上是Symfony控制層的進一步解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
如何防止會話固定攻擊?如何防止會話固定攻擊?Apr 28, 2025 am 12:25 AM

防止會話固定攻擊的有效方法包括:1.在用戶登錄後重新生成會話ID;2.使用安全的會話ID生成算法;3.實施會話超時機制;4.使用HTTPS加密會話數據,這些措施能確保應用在面對會話固定攻擊時堅不可摧。

您如何實施無會話身份驗證?您如何實施無會話身份驗證?Apr 28, 2025 am 12:24 AM

實現無會話身份驗證可以通過使用JSONWebTokens(JWT)來實現,這是一種基於令牌的認證系統,所有的必要信息都存儲在令牌中,無需服務器端會話存儲。 1)使用JWT生成和驗證令牌,2)確保使用HTTPS防止令牌被截獲,3)在客戶端安全存儲令牌,4)在服務器端驗證令牌以防篡改,5)實現令牌撤銷機制,如使用短期訪問令牌和長期刷新令牌。

PHP會議有哪些常見的安全風險?PHP會議有哪些常見的安全風險?Apr 28, 2025 am 12:24 AM

PHP會話的安全風險主要包括會話劫持、會話固定、會話預測和會話中毒。 1.會話劫持可以通過使用HTTPS和保護cookie來防範。 2.會話固定可以通過在用戶登錄前重新生成會話ID來避免。 3.會話預測需要確保會話ID的隨機性和不可預測性。 4.會話中毒可以通過對會話數據進行驗證和過濾來預防。

您如何銷毀PHP會議?您如何銷毀PHP會議?Apr 28, 2025 am 12:16 AM

銷毀PHP會話需要先啟動會話,然後清除數據並銷毀會話文件。 1.使用session_start()啟動會話。 2.用session_unset()清除會話數據。 3.最後用session_destroy()銷毀會話文件,確保數據安全和資源釋放。

如何更改PHP中的默認會話保存路徑?如何更改PHP中的默認會話保存路徑?Apr 28, 2025 am 12:12 AM

如何改變PHP的默認會話保存路徑?可以通過以下步驟實現:在PHP腳本中使用session_save_path('/var/www/sessions');session_start();設置會話保存路徑。在php.ini文件中設置session.save_path="/var/www/sessions"來全局改變會話保存路徑。使用Memcached或Redis存儲會話數據,如ini_set('session.save_handler','memcached');ini_set(

您如何修改PHP會話中存儲的數據?您如何修改PHP會話中存儲的數據?Apr 27, 2025 am 12:23 AM

tomodifyDataNaphPsession,startTheSessionWithSession_start(),然後使用$ _sessionToset,修改,orremovevariables.1)startThesession.2)setthesession.2)使用$ _session.3)setormodifysessessvariables.3)emovervariableswithunset()

舉一個在PHP會話中存儲數組的示例。舉一個在PHP會話中存儲數組的示例。Apr 27, 2025 am 12:20 AM

在PHP會話中可以存儲數組。 1.啟動會話,使用session_start()。 2.創建數組並存儲在$_SESSION中。 3.通過$_SESSION檢索數組。 4.優化會話數據以提升性能。

垃圾收集如何用於PHP會議?垃圾收集如何用於PHP會議?Apr 27, 2025 am 12:19 AM

PHP會話垃圾回收通過概率機制觸發,清理過期會話數據。 1)配置文件中設置觸發概率和會話生命週期;2)可使用cron任務優化高負載應用;3)需平衡垃圾回收頻率與性能,避免數據丟失。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器