欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Symfony控制層深入詳解

 更新時(shí)間:2016年03月17日 15:03:43   作者:wow_1904  
這篇文章主要介紹了Symfony控制層,結(jié)合大量實(shí)例代碼深入分析了Symfony控制器的常見使用技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下

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

Symfony中控制層包含了連接業(yè)務(wù)邏輯與表現(xiàn)的代碼,控制層為不同的使用分成了幾個(gè)不同的部分。

1. 前端控制器是指向應(yīng)用的唯一入口
2. 動(dòng)作包含了應(yīng)用的邏輯,他們檢查請(qǐng)求的完整性并準(zhǔn)備好表示層需要的數(shù)據(jù)
3. 請(qǐng)求、響應(yīng)和Session對(duì)象提供訪問請(qǐng)求參數(shù)、響應(yīng)參數(shù)以及持久的用戶數(shù)據(jù),這些數(shù)據(jù)在控制層使用的很普遍
4. 過濾器是每個(gè)請(qǐng)求都要執(zhí)行的代碼的一部分,無論在動(dòng)作前還是在動(dòng)作后。可以自創(chuàng)過濾器。

前端控制器

所有WEB請(qǐng)求都將被前端控制器捕獲,前端控制是給定環(huán)境下整個(gè)應(yīng)用的唯一入口點(diǎn)。當(dāng)前端控制接到一個(gè)請(qǐng)求,他使用路由系統(tǒng)匹配用戶輸入的URL的動(dòng)作名和模塊名。例如:

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

URL調(diào)用了index.php腳本(也就是前端控制器),他被理解為:動(dòng)作-myAction,模塊-mymodule

前端控制器的工作細(xì)節(jié)

前端控制器分發(fā)請(qǐng)求,他僅執(zhí)行那些通用的和共同的代碼,包括:

1. 定義核心常量
2. 定位symfony庫
3. 載入和初始化核心框架類
4. 載入配置信息
5. 解碼請(qǐng)求URL,獲取要執(zhí)行的動(dòng)作和請(qǐng)求參數(shù)
6. 如果動(dòng)作不存在則專項(xiàng)404錯(cuò)誤
7. 激活過濾器(比如,如果需要身份認(rèn)證)
8. 執(zhí)行過濾器,第一次
9. 執(zhí)行動(dòng)作,遞交視圖
10. 執(zhí)行過濾器,第二次
11. 輸出響應(yīng)。

默認(rèn)前端控制器

默認(rèn)前端控制器叫作index.php,在項(xiàng)目的WEB/目錄,他是一個(gè)簡(jiǎn)單的PHP文件,如下:

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

這個(gè)文件在前面已經(jīng)介紹過了:首先定義幾個(gè)變量,然后引入應(yīng)用的配置config.php,最后調(diào)用sfController(這是symfony MVC架構(gòu)中的核心控制器對(duì)象)的dispatch()方法。最后一步將被過濾器鏈捕獲。

調(diào)用另一個(gè)前端控制器來更換環(huán)境

每個(gè)環(huán)境存在一個(gè)前端控制器,環(huán)境定義在SF_ENVIRONMENT常量中。

創(chuàng)建新環(huán)境就和創(chuàng)建新的前端控制器一樣簡(jiǎn)單,比如,你需要一個(gè)staging環(huán)境以使你的應(yīng)用上線之前可以被客戶測(cè)試。要?jiǎng)?chuàng)建staging環(huán)境,拷貝web/myapp_dev.php到web/myapp_staging.php,然后修改SF_ENVIRONMENT常量為staging?,F(xiàn)在,你可以在所有的配置文件中增加staging段了設(shè)置新環(huán)境所需要的東西,看下面的示例:

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

批處理文件

在命令行或者計(jì)劃任務(wù)中訪問symfony類和特性的時(shí)候需要使用批處理文件。批處理文件的開頭與前端控制器的開頭一樣——除了前端控制器的分發(fā)部分不要。

<?php
define('SF_ROOT_DIR',  realpath(dirname(__FILE__).'/..'));
define('SF_APP',     'myapp');
define('SF_ENVIRONMENT', 'prod');
define('SF_DEBUG',    false);
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');
// 添加批處理代碼

動(dòng)作(Actions)

動(dòng)作是應(yīng)用的心臟,因?yàn)樗怂袘?yīng)用的邏輯。他們使用模型并定義變量給視圖。當(dāng)在應(yīng)用中使用一個(gè)請(qǐng)求,URL中定義了一個(gè)動(dòng)作和請(qǐng)求的參數(shù)。

動(dòng)作類

動(dòng)作是moduleNameActions類(繼承自sfActions類)中名為executeActionName的方法,以模塊組織在一起,模塊動(dòng)作類存儲(chǔ)在actions目錄的actions.class.php文件中。

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

如果動(dòng)作類變得很大,你應(yīng)該做一些分解并把代碼放在模型層,動(dòng)作應(yīng)該盡量的保證短?。◣仔凶詈茫?,所有的業(yè)務(wù)邏輯都應(yīng)該放在模型層中。

可選的動(dòng)作類語法

可以一個(gè)動(dòng)作一個(gè)文件,文件的名稱為動(dòng)作名加Action.class.php,類名為動(dòng)作名加Action,只是記得類繼承自sfAction而非sfActions。

在動(dòng)作中獲取信息

動(dòng)作類提供了一種訪問控制器相關(guān)信息與核心symfony對(duì)象的方法,下面演示了如何使用:

<?php
define('SF_ROOT_DIR',  realpath(dirname(__FILE__).'/..'));
define('SF_APP',     'myapp');
define('SF_ENVIRONMENT', 'prod');
define('SF_DEBUG',    false);
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');
class mymoduleActions extends sfActions
{
 public function executeIndex()
 {
  // Retrieving request parameters
  $password  = $this->getRequestParameter('password');
  // 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('foo', 'bar');
  $this->foo = 'bar';      // Shorter version
 }
}

上下文:

在前端控制器中一個(gè)sfContext::getInstance()的調(diào)用。在動(dòng)作中,getContext()方法是單例模式(即所有的調(diào)用都是第一個(gè)實(shí)例,這對(duì)于存儲(chǔ)指向與給定請(qǐng)求相關(guān)的symfony核心對(duì)象的索引的情況是非常有用的)。

sfController:控制器對(duì)象 (->getController())
sfRequest:請(qǐng)求對(duì)象 (->getRequest())
sfResponse:響應(yīng)對(duì)象 (->getResponse())
sfUser:用戶Session對(duì)象 (->getUser())
sfDatabaseConnection:數(shù)據(jù)庫鏈接 (->getDatabaseConnection())
sfLogger:日志對(duì)象 (->getLogger())
sfI18N:國際化對(duì)象(->getI18N())
可以在代碼的任何位置放置sfContext::getInstance()。

動(dòng)作終止

當(dāng)動(dòng)作執(zhí)行完成后會(huì)出現(xiàn)各種行為,動(dòng)作方法的返回值決定視圖如何被實(shí)施。sfView類的常量經(jīng)常被指定與模板來顯示動(dòng)作的結(jié)果。如果一個(gè)默認(rèn)視圖被調(diào)用,動(dòng)作應(yīng)該以下面的代碼結(jié)束:

return sfView::SUCCESS;

Symfony將尋找actionNameSuccess.php的模板,這是默認(rèn)的行為,所以如果你忽略了return語句,symfony也將查找actionNameSuccess.php模板,空動(dòng)作也將觸發(fā)同樣的行為,如下:

# 將調(diào)用indexSuccess.php模板
public function executeIndex()
{
 return sfView::SUCCESS;
}
# 將調(diào)用listSuccess.php模板
public function executeList()
{
}

如果要調(diào)用錯(cuò)誤視圖,動(dòng)作將以下面語句結(jié)束:

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

調(diào)用自定義視圖

# symfony將查找actionNameMyResult.php模板
return 'MyResult';

如果動(dòng)作不想調(diào)用模板(比如批處理和任務(wù)計(jì)劃cron),可以使用下面的語句

return sfView::NONE;

上面情況下,視圖層將被忽略,HTML代碼可以直接在動(dòng)作中輸出,symfony提供了一個(gè)特殊的方法renderText()來實(shí)現(xiàn)。這種情況比較適用于AJAX交互。

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

有些時(shí)候你需要發(fā)送空的響應(yīng)但包含定義的頭信息(特別是X-JSON頭),定義頭通過sfResponse對(duì)象,并且放回sfView::HEADER_ONLY常量:

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

如果動(dòng)作必須呈交特定模板,使用setTemplate()方法來忽略return語句:

$this->setTemplate('myCustomTemplate');

跳向另一個(gè)動(dòng)作

有些情況下,動(dòng)作以請(qǐng)求一個(gè)新的動(dòng)作作為結(jié)束,例如,一個(gè)動(dòng)作處理表單的POST提交在更新數(shù)據(jù)庫后通常會(huì)轉(zhuǎn)向到另一個(gè)動(dòng)作。另一個(gè)例子是動(dòng)作別名:index動(dòng)作經(jīng)常是來完成顯示,所以實(shí)際上是跳向了list動(dòng)作。

有兩種方式來實(shí)現(xiàn)另一個(gè)動(dòng)作的執(zhí)行:

① 向前(Forwards)方式

$this->forward('otherModule','otherAction');

② 重定向(Redirection)方式

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

Forward和redirect后面的語句將不會(huì)被執(zhí)行,你可以理解為他們等價(jià)于return語句。他們拋出sfStopException異常來停止動(dòng)作的執(zhí)行

兩者的區(qū)別:forward是內(nèi)部的處理,對(duì)用戶是透明的,即用戶不會(huì)感覺到動(dòng)作發(fā)生了變化,URL也不會(huì)更改。相反,redirection是動(dòng)作的真正跳轉(zhuǎn),URL的改變是最直接的反映。

如果動(dòng)作是被POST提交表單調(diào)用的,你最好使用redirect。這樣,如果用戶刷新了結(jié)果頁面,POST表單不會(huì)被重復(fù)提交;另外,后退按鈕也能很好的返回到表單顯示頁面而不是一個(gè)警告窗口詢問用戶是否重新提交POST請(qǐng)求。

Forward404()方法是一種常用的特殊forward,他跳到“頁面無法找到”動(dòng)作。

經(jīng)驗(yàn)說明,很多時(shí)候一個(gè)動(dòng)作會(huì)在驗(yàn)證一些東西后redirect或者forward另一個(gè)動(dòng)作。這就是為什么sfActions類有很多的方法命名為forwardIf(), forwardUnless(), forward404If(), forward404Unless(), redirectIf(), 和 redirectUnless(),這些方法簡(jiǎn)單地使用一個(gè)測(cè)試結(jié)果參數(shù)true(那些xxxIf()方法)或false(那些xxxUnless()方法):

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

這些方法不僅僅是減少你的代碼行數(shù),他們還使得你的程序更加易讀。

當(dāng)動(dòng)作調(diào)用forward404()或者其他類似方法,symfony拋出管理404響應(yīng)的sfError404Exception異常,也就是說如果你想顯示404信息,你無需訪問控制器,你只是拋出這個(gè)異常即可。

模塊中多個(gè)動(dòng)作重復(fù)代碼的處理方式

preExecute()與postExecute()方法是一個(gè)模塊中多個(gè)動(dòng)作共同的東西??梢栽谡{(diào)用executeAction()之前和之后執(zhí)行。

class mymoduleActions extends sfActions
{
 public function preExecute()
 {
  // 這里的代碼在每一個(gè)動(dòng)作調(diào)用之前執(zhí)行
  ...
 }
 public function executeIndex()
 {
  ...
 }
 public function executeList()
 {
  ...
  $this->myCustomMethod(); // 調(diào)用自定義的方法
 }
 public function postExecute()
 {
  // 這里的代碼會(huì)在每個(gè)動(dòng)作結(jié)束后執(zhí)行
  ...
 }
 protected function myCustomMethod()
 {
  // 添加自己的方法,雖然他們沒有以execute開頭
  // 在這里,最好將方法定義為protected(保護(hù)的)或者private(私有的)
  ...
 }
}

訪問請(qǐng)求

getRequestParameter(“myparam”)方法常用來獲取myparam參數(shù)的值,實(shí)際上這個(gè)方法是一系列請(qǐng)求調(diào)用參數(shù)倉庫的代理:getRequest()->getParameter(“myparam”)。動(dòng)作類使用sfWebRequest訪問請(qǐng)求對(duì)象,通過getRequest()訪問他們的方法。

sfWebRequest對(duì)象的方法

方法名
功能
輸入示例
Request Information
 
 
getMethod()
Request對(duì)象
Returns sfRequest::GET or sfRequest::POST constants
getMethodName()
Request對(duì)象名
'POST'
getHttpHeader('Server')
給定HTTP頭的值
'Apache/2.0.59 (Unix) DAV/2 PHP/5.1.6'
getCookie('foo')
指定名稱Cookie的值
'bar'
isXmlHttpRequest()*
是否是AJAX請(qǐng)求?
true
isSecure()
是否是SSL請(qǐng)求
true
Request Parameters
 
 
hasParameter('foo')
參數(shù)是否在請(qǐng)求中有
true
getParameter('foo')
指定參數(shù)的值
'bar'
getParameterHolder()->getAll()
所有請(qǐng)求參數(shù)的數(shù)組
 
URI-Related Information
 
 
getUri()
完整URI
'http://localhost/myapp_dev.php/mymodule/myaction'
getPathInfo()
路徑信息
'/mymodule/myaction'
getReferer()**
來自那里?
'http://localhost/myapp_dev.php/'
getHost()
主機(jī)名
'localhost'
getScriptName()
前端控制器路徑與名稱
'myapp_dev.php'
Client Browser Information
 
 
getLanguages()
可接受語言的列表
Array( [0] => fr [1] => fr_FR [2] => en_US [3] => en )
getCharsets()
可接受字符集的列表
Array( [0] => ISO-8859-1 [1] => UTF-8 [2] => * )
getAcceptableContentTypes()
可接受內(nèi)容類型數(shù)組
 

sfActions類提供了一些代理來快速地訪問請(qǐng)求方法

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

對(duì)于文件上傳的請(qǐng)求,sfWebRequest對(duì)象提供了訪問和移動(dòng)這些文件的手段:

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('sf_upload_dir');
    $this->getRequest()->moveFile('file', $uploadDir.'/'.$fileName);
   }
  }
 }
}

用戶Session

Symfony自動(dòng)管理用戶Session并且能在請(qǐng)求之間為用戶保留持久數(shù)據(jù)。他使用PHP內(nèi)置的Session處理機(jī)制并提升了此機(jī)制,這使得symfony的用戶Session更好配置更容易使用。

訪問用戶Session

當(dāng)前用戶的Session對(duì)象在動(dòng)作中使用getUser()方法訪問,他是sfUser類的一個(gè)實(shí)例。sfUser類包含了允許存儲(chǔ)任何用戶屬性的參數(shù)倉庫。用戶屬性能夠存放任何類型的數(shù)據(jù)(字符串、數(shù)組、關(guān)聯(lián)數(shù)組等)。

sfUser對(duì)象能夠跨請(qǐng)求地保存用戶屬性

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

可以把對(duì)象存放在用戶Session中,但這往往讓人氣餒,因?yàn)镾ession在請(qǐng)求之間被序列化了并且存儲(chǔ)在文件中,當(dāng)Session序列化時(shí),存儲(chǔ)對(duì)象的類必須已經(jīng)被加載,而這很難被保證。另外,如果你存儲(chǔ)了Propel對(duì)象,他們可能是“延遲”的對(duì)象。

與symfony中的getter方法一樣,getAttribute()方法接受第二個(gè)參數(shù)作為默認(rèn)值(如果屬性沒有被定義時(shí))使用。判斷屬性是否被定義使用hasAttribute()方法。屬性存儲(chǔ)在參數(shù)倉庫可使用getAttributeHolder()方法訪問,可以使用基本參數(shù)倉庫方法很簡(jiǎn)單的清除用戶屬性:

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

用戶Session屬性在模板中默認(rèn)通過$sf_user變量訪問,他存儲(chǔ)了當(dāng)前的sfUser對(duì)象

<p>
 Hello, <?php echo $sf_user->getAttribute('nickname') ?>
</p>

如果只想在當(dāng)前請(qǐng)求中存儲(chǔ)信息,你更應(yīng)該使用sfRequest類,他也有g(shù)etAttribute()和setAttribute()方法,只有在請(qǐng)求之間持久存儲(chǔ)信息時(shí)才適合sfUser對(duì)象。

Flash屬性

Flash屬性是一種短命屬性,他會(huì)在最近的一次請(qǐng)求后消失,這樣可以保持你的Session清潔

$this->setFlash('attrib', $value);

在另一個(gè)動(dòng)作中獲取Flash屬性:

$value = $this->getFlash('attrib');

一旦傳入了另一個(gè)動(dòng)作,F(xiàn)lash屬性就消失了,即使你的Session還不過期。在模板中訪問flash屬性使用$sf_flash對(duì)象。

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

或者

<?php echo $sf_flash->get('attrib') ?>

Session管理

Symfony的Session處理特性對(duì)開發(fā)者完全掩蓋了客戶端與服務(wù)端的SessionID存儲(chǔ),當(dāng)然,如果你非要去改Session管理機(jī)制的默認(rèn)行為也不是不可能,高級(jí)用戶經(jīng)常這么干。

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

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

Symfony的Session基于PHP的Session設(shè)置,也就是說如果希望客戶端使用URL參數(shù)方式取代Cookies,你必須修改php.ini文件的use_trans_sid參數(shù),不建議這么做。

在服務(wù)器端,symfony默認(rèn)使用文件存儲(chǔ)用戶Session,可以通過修改factories.yml文件承擔(dān)class參數(shù)來使用數(shù)據(jù)庫存儲(chǔ):apps/myapp/config/factories.yml

all:
 storage:
  class: sfMySQLSessionStorage
  param:
   db_table: SESSION_TABLE_NAME   # 存儲(chǔ)Session的表
   database: DATABASE_CONNECTION   # 數(shù)據(jù)庫的名稱
Class名稱可以是:fMySQLSessionStorage, sfPostgreSQLSessionStorage和sfPDOSessionStorage;后面是首選方式。
Session超時(shí)的修改和調(diào)整:apps/myapp/config/settings.yml
default:
 .settings:
  timeout:   1800      #Session超時(shí) 單位秒

動(dòng)作的安全

動(dòng)作的執(zhí)行可以被限定在具有一定權(quán)限的用戶。Symfony為此提供的工作允許創(chuàng)建安全的應(yīng)用,用戶只有在通過認(rèn)證之后才能防偽應(yīng)用的某些特性或部分。設(shè)置安全的應(yīng)用需要兩步:定義動(dòng)作需要的安全和使用具有權(quán)限的用戶登錄。

訪問限制

在每個(gè)動(dòng)作執(zhí)行前都會(huì)被一個(gè)特定的過濾器來檢查當(dāng)前用戶是否具有訪問的權(quán)限,symfony中權(quán)限有兩個(gè)部分組成:

① 安全動(dòng)作只有被授權(quán)用戶可以訪問
② 憑證允許分組管理權(quán)限

通過創(chuàng)建或者修改config目錄下的security.yml文件即可簡(jiǎn)單的完成安全訪問限制。在文件中可以設(shè)置某個(gè)動(dòng)作或者所有動(dòng)作是否需要授權(quán)。

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

read:
 is_secure:  off    # 所有用戶都可以請(qǐng)求Read動(dòng)作
update:
 is_secure:  on    # update動(dòng)作只能有認(rèn)證的用戶訪問
delete:
 is_secure:  on    # 同update
 credentials: admin   # 具有admin憑證
all:
 is_secure: off    # 無需授權(quán)

用戶訪問一個(gè)需要授權(quán)的動(dòng)作將依據(jù)用戶的權(quán)限:

① 用戶已登錄并且憑證符合則動(dòng)作能執(zhí)行
② 如果用戶沒有登錄則轉(zhuǎn)向默認(rèn)登錄動(dòng)作
③ 如果用戶已登錄但憑證不符合則會(huì)轉(zhuǎn)向默認(rèn)的安全動(dòng)作

轉(zhuǎn)向?qū)⒏鶕?jù)apps/myapp/config/settings.yml文件來決定

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

授權(quán)

為某用戶授權(quán):

class myAccountActions extends sfActions
{
 public function executeLogin()
 {
  if ($this->getRequestParameter('login') == 'foobar') //判斷根據(jù)具體需求而定
  {
   $this->getUser()->setAuthenticated(true);  //設(shè)置用戶為已認(rèn)證
  }
 }
 public function executeLogout()
 {
  $this->getUser()->setAuthenticated(false); //設(shè)置用戶為未認(rèn)證
 }
}

在動(dòng)作中處理憑證:

class myAccountActions extends sfActions
{
 public function executeDoThingsWithCredentials()
 {
  $user = $this->getUser();
  // 添加憑證
  $user->addCredential('foo');
  $user->addCredentials('foo', 'admin');  //添加多個(gè)憑證
  // 判斷是否具有憑證
  echo $user->hasCredential('foo');           =>  true
  // 判斷是否具有多個(gè)憑證
  echo $user->hasCredential(array('foo', 'admin'));    =>  true
  // Check if the user has one of the credentials
  echo $user->hasCredential(array('foo', 'admin'), false); =>  true
  // 刪除憑證
  $user->removeCredential('foo');
  echo $user->hasCredential('foo');           =>  false
  // 刪除所有憑證 (被用在注銷處理過程中)
  $user->clearCredentials();
  echo $user->hasCredential('admin');           =>  false
 }
}

如果用戶具有'admin'憑證,他就可以訪問security.yml文件中設(shè)定只有admin可以訪問的動(dòng)作。憑證也經(jīng)常用來在模板中顯示授權(quán)信息

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

sfGuardPlugin插件擴(kuò)展了session類,使得登錄與注銷的處理變得簡(jiǎn)單。

復(fù)雜的憑證

在security.yml文件中,可以使用AND或者OR來組合各種憑證,這樣就可以建立復(fù)雜的業(yè)務(wù)流和用戶權(quán)限管理系統(tǒng)。例如,CMS系統(tǒng)后臺(tái)辦公自由具有admin憑證用戶訪問,文章的編輯必須有editor憑證,發(fā)布只能有有publisher憑證的用戶,代碼如下:

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

每次添加新的[],憑證之間的關(guān)系在AND和OR之間切換,這樣可以創(chuàng)建及其復(fù)雜的憑證組合關(guān)系:

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

注:【和】所有憑證都滿足,【或】滿足其中的一個(gè)憑證。

驗(yàn)證和錯(cuò)誤處理方法

驗(yàn)證輸入是重復(fù)且單調(diào)的事情,symfony提供了內(nèi)置的請(qǐng)求驗(yàn)證系統(tǒng),使用動(dòng)作類的方法。

看個(gè)例子,當(dāng)一個(gè)用戶請(qǐng)求myAction,symfony首先去查找validateMyAction()方法,如果找到了就執(zhí)行,根據(jù)返回結(jié)果來決定如何往下走:如果返回真則executeMyAction()被執(zhí)行,否則handleErrorMyAction()被執(zhí)行,并且,如果找不到handleErrorMyAction,symfony則去查找普通handleError方法,如果還不存在則簡(jiǎn)單返回sfView::ERROR并遞交myActionError.php模板,看下圖:

說明:

① validateActionName是驗(yàn)證方法,是ActionName被請(qǐng)求的第一個(gè)查找方法,如果不存在則直接執(zhí)行動(dòng)作方法。

② handleErrorActionName方法,如果驗(yàn)證失敗則查找此方法,如果不存在則Error模板被顯示

③ executeActionName是動(dòng)作方法,對(duì)于動(dòng)作他必須存在。

看段代碼:

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

可以在驗(yàn)證方法中加入任何代碼,但最終只要返回true或者false即可。因?yàn)槭莝fActions類的方法,所以可以訪問sfRequest和sfUser對(duì)象,這樣將對(duì)于輸入與上下文驗(yàn)證非常有利。

過濾器

安全處理可以被認(rèn)為是請(qǐng)求到動(dòng)作執(zhí)行之前必須經(jīng)過的一個(gè)過濾器。實(shí)際上可以在動(dòng)作執(zhí)行前(后)設(shè)置任意多個(gè)的過濾器。

過濾器鏈

Symfony實(shí)際上將請(qǐng)求處理看作是過濾器鏈??蚣苁盏秸?qǐng)求后第一個(gè)過濾器(通常是sfRenderingFilter)被執(zhí)行,在某些時(shí)候他調(diào)用下一個(gè)過濾器,依次下去。當(dāng)最后一個(gè)過濾器(通常是sfExecutionFilter)執(zhí)行后,前一個(gè)過濾器結(jié)束,依次返回去知道rending過濾器。

所有的過濾器均繼承自sfFilter類并且都包含了execute()方法,此方法之前的代碼在動(dòng)作(action)之前執(zhí)行,此方法之后的代碼在動(dòng)作之后執(zhí)行,看一段代碼(下一節(jié)中要用到,取名myFilter代碼):

class myFilter extends sfFilter
{
 public function execute ($filterChain)
 {
  // 在動(dòng)作之前執(zhí)行的代碼
  ...
  // 執(zhí)行下一個(gè)過濾器
  $filterChain->execute();
  // 在動(dòng)作之后執(zhí)行的代碼
  ...
 }
}

過濾器鏈在/myapp/config/filters.yml文件中定義:

rendering: ~
web_debug: ~
security: ~
# 在這里插入你自己的過濾器
cache:   ~
common:  ~
flash:   ~
execution: ~

這些聲明都沒有參數(shù)(~在symfony中的意思是null,將采用默認(rèn)的值),他們都繼承自symfony核心定義的參數(shù),symfony為每一個(gè)過濾器(除了自定義過濾器)定義了類和參數(shù),他們?cè)?sf_symfony_data_dir/config/filter.yml文件。

自定義過濾器鏈的方法:

① 設(shè)置過濾器的enabled參數(shù)為off將禁用過濾器,例如:

Web_debug:
 enabled: off

你還可以通過settings.yml文件達(dá)到此目的,修改web_deug、use_security、cache和use_flash的設(shè)置即可,應(yīng)為每一個(gè)默認(rèn)過濾器都有一個(gè)condition參數(shù)來自上面的配置。

② 不要通過刪除filters.yml文件中的過濾器來禁用該過濾器,symfony將拋出異常

③ 可以自定義過濾器,但無論如何rendering必須是第一個(gè),而execution必須是最后一個(gè)

④ 為默認(rèn)過濾器重寫默認(rèn)類和參數(shù)(特別是修改security系統(tǒng)和用戶的安全驗(yàn)證過濾器)

創(chuàng)建自定義過濾器

通過創(chuàng)建myFilter的類可以非常簡(jiǎn)單的常見自定義的過濾器,把類文件放在項(xiàng)目的lib文件夾可以充分利用symfony提供的自動(dòng)加載特性。

由于動(dòng)作之間可以互相跳轉(zhuǎn),因此過濾器鏈會(huì)在每一個(gè)請(qǐng)求中循序執(zhí)行。但更多的時(shí)候可能需要是第一次請(qǐng)求的時(shí)候執(zhí)行自定義的過濾器,這時(shí)候使用sfFilter類的isFirstCall()方法??聪旅娲a:apps/myapp/lib/rememberFilter.class.php(例子)

class rememberFilter extends sfFilter
{
 public function execute($filterChain)
 {
  // 通過調(diào)用isFirstCall方法保證只執(zhí)行一次
  if ($this->isFirstCall())
  {
   // 過濾器不直接訪問請(qǐng)求和用戶對(duì)象,你需要使用context對(duì)象獲取
   // You will need to use the context object to get them
   $request = $this->getContext()->getRequest();
   $user  = $this->getContext()->getUser();
   if ($request->getCookie('MyWebSite'))
   {
    // 登入
    $user->setAuthenticated(true);
   }
  }
  // 執(zhí)行下一個(gè)過濾器
  $filterChain->execute();
 }
}

有時(shí)候需要在一個(gè)過濾器執(zhí)行之后跳往另一個(gè)動(dòng)作而不是下一個(gè)過濾器。sfFilter不包含forward方法,但sfController包含,使用下面的語句:

return $this->getContext()->getController()->forward('mymodule', 'myAction');

sfFilter類有一個(gè)initialize方法,在對(duì)象創(chuàng)建的時(shí)候執(zhí)行,可以在自定義的過濾器中覆蓋此方法以達(dá)到更加靈活地設(shè)置參數(shù)的目的。

過濾器激活及參數(shù)

過濾器創(chuàng)建后還必須進(jìn)行激活,在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: ~

自定義過濾器中的參數(shù)可以在過濾器代碼中使用getParameter方法獲取:

apps/myapp/lib/rememberFilter.class.php

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

Condition參數(shù)被過濾器鏈測(cè)試來決定是否必須被執(zhí)行。因此自定義過濾器聲明能夠依賴一個(gè)應(yīng)用配置。要是過濾器執(zhí)行,記得在應(yīng)用的app.yml中加入:

all:
 enable_remember_me: on

過濾器示例

如果想在項(xiàng)目中包含特定的代碼,你可以通過過濾器實(shí)現(xiàn)(layout方式需要在每一個(gè)應(yīng)用中都要設(shè)置),看下面的代碼:

class sfGoogleAnalyticsFilter extends sfFilter
{
 public function execute($filterChain)
 {
  // 在動(dòng)作之前什么也不做
  $filterChain->execute();
  // 使用下面的代碼修飾響應(yīng)
  $googleCode = '
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
 _uacct="UA-'.$this->getParameter('google_id').'";urchinTracker();
</script>';
  $response = $this->getContext()->getResponse();
  $response->setContent(str_ireplace('</body>', $googleCode.'</body>',$response->getContent()));
  }
}

這不是一種好的方式,因?yàn)樵诜荋TML響應(yīng)中不適用,只是一個(gè)例子而已。

下個(gè)例子是轉(zhuǎn)換http請(qǐng)求到https請(qǐng)求

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

過濾器廣泛地應(yīng)用于插件,允許全面地?cái)U(kuò)展應(yīng)用的特性。

模塊配置

一些模塊行為依賴配置,要修改他們必須在模塊的config目錄下建立module.yml并為每一個(gè)環(huán)境(或者all)定義設(shè)置。

看個(gè)例子:apps/myapp/modules/mymodule/config/module.yml

all:         #對(duì)所有環(huán)境
 enabled:   true
 is_internal: false
 view_name:  sfPHP

Enable參數(shù)允許你在模塊中禁用所有動(dòng)作,這樣所有動(dòng)作都將專項(xiàng)到module_disabled_module/module_disabled_action動(dòng)作(定義在settings.yml)

Is_internal參數(shù)定義動(dòng)作只能內(nèi)部調(diào)用,比如發(fā)送郵件只能有另一個(gè)動(dòng)作調(diào)用而不是外部的直接調(diào)用。

View_name參數(shù)定義了view類,類必須繼承自sfView。覆蓋他后你將可以使用具有其他模板引擎其他的view系統(tǒng),比如smarty。

希望本文所述對(duì)大家基于Symfony框架的PHP程序設(shè)計(jì)有所幫助。

相關(guān)文章

最新評(píng)論