Symfony控制層深入詳解
本文深入分析了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ì)有所幫助。
- Symfony2之session與cookie用法小結(jié)
- Symfony2實(shí)現(xiàn)從數(shù)據(jù)庫獲取數(shù)據(jù)的方法小結(jié)
- Symfony2實(shí)現(xiàn)在controller中獲取url的方法
- Symfony2框架學(xué)習(xí)筆記之表單用法詳解
- Symfony2框架學(xué)習(xí)筆記之HTTP Cache用法詳解
- Symfony核心類概述
- 使用symfony命令創(chuàng)建項(xiàng)目的方法
- Symfony模板的快捷變量用法實(shí)例
- Symfony2框架創(chuàng)建項(xiàng)目與模板設(shè)置實(shí)例詳解
- Symfony學(xué)習(xí)十分鐘入門經(jīng)典教程
- 高性能PHP框架Symfony2經(jīng)典入門教程
- symfony2.4的twig中date用法分析
相關(guān)文章
PHP實(shí)現(xiàn)單條sql執(zhí)行多個(gè)數(shù)據(jù)的insert語句方法
今天小編就為大家分享一篇PHP實(shí)現(xiàn)單條sql執(zhí)行多個(gè)數(shù)據(jù)的insert語句方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-10-10DEDE實(shí)現(xiàn)轉(zhuǎn)跳屬性文檔在模板上調(diào)用出轉(zhuǎn)跳地址
這篇文章主要介紹了DEDE實(shí)現(xiàn)轉(zhuǎn)跳屬性文檔在模板上調(diào)用出轉(zhuǎn)跳地址,有需要的朋友可以參考一下。2016-11-11分享一個(gè)PHP數(shù)據(jù)流應(yīng)用的簡(jiǎn)單例子
分享一個(gè)PHP數(shù)據(jù)流應(yīng)用的簡(jiǎn)單例子,需要的朋友可以參考下2012-06-06PHP指定截取字符串中的中英文或數(shù)字字符的實(shí)例分享
這篇文章主要介紹了PHP指定截取字符串中的中英文或數(shù)字字符的實(shí)例,還附帶介紹了過濾字符串中空格的方法,需要的朋友可以參考下2016-03-03laravel 實(shí)現(xiàn)上傳圖片到本地和前臺(tái)訪問示例
今天小編就為大家分享一篇laravel 實(shí)現(xiàn)上傳圖片到本地和前臺(tái)訪問示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-10-10PHP+jQuery+Ajax實(shí)現(xiàn)用戶登錄與退出
本文使用Ajax無刷新登錄和退出,從而提升了用戶體驗(yàn)。 若用戶為登錄狀態(tài),則顯示用戶相關(guān)登錄信息,否則顯示登錄表單。2015-04-04