程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> PHP綜合 >> Symfony控制層深入詳解

Symfony控制層深入詳解

編輯:PHP綜合

本文深入分析了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('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();

這個文件在前面已經介紹過了:首先定義幾個變量,然後引入應用的配置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:  [email protected]
  contact:   [email protected]
all:
 mail:
  webmaster:  [email protected]
  contact:   [email protected]
##查看結果
http://localhost/myapp_staging.php/mymodule/index

批處理文件

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

<?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');
// 添加批處理代碼

動作(Actions)

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

動作類

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

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

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

可選的動作類語法

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

在動作中獲取信息

動作類提供了一種訪問控制器相關信息與核心symfony對象的方法,下面演示了如何使用:

<?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
 }
}

上下文:

在前端控制器中一個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 'MyResult';

如果動作不想調用模板(比如批處理和任務計劃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 = '<"title","My basic letter"],["name","Mr Brown">';
 $this->getResponse()->setHttpHeader("X-JSON", '('.$output.')');
 return sfView::HEADER_ONLY;
}

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

$this->setTemplate('myCustomTemplate');

跳向另一個動作

有些情況下,動作以請求一個新的動作作為結束,例如,一個動作處理表單的POST提交在更新數據庫後通常會轉向到另一個動作。另一個例子是動作別名:index動作經常是來完成顯示,所以實際上是跳向了list動作。

有兩種方式來實現另一個動作的執行:

① 向前(Forwards)方式

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

② 重定向(Redirection)方式

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

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('id'));
 $this->forward404If(!$article);
}
public function executeShow()
{
 $article = ArticlePeer::retrieveByPK($this->getRequestParameter('id'));
 $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對象的方法

方法名 功能 輸入示例 Request Information     getMethod() Request對象 Returns sfRequest::GET or sfRequest::POST constants getMethodName() Request對象名 'POST' getHttpHeader('Server') 給定HTTP頭的值 'Apache/2.0.59 (Unix) DAV/2 PHP/5.1.6' getCookie('foo') 指定名稱Cookie的值 'bar' isXmlHttpRequest()* 是否是AJAX請求? 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 ) getCharsets() 可接受字符集的列表 Array( [0] => ISO-8859-1 [1] => UTF-8 [2] => * ) getAcceptableContentTypes() 可接受內容類型數組  

sfActions類提供了一些代理來快速地訪問請求方法

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
 }
}

對於文件上傳的請求,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('sf_upload_dir');
    $this->getRequest()->moveFile('file', $uploadDir.'/'.$fileName);
   }
  }
 }
}

用戶Session

Symfony自動管理用戶Session並且能在請求之間為用戶保留持久數據。他使用PHP內置的Session處理機制並提升了此機制,這使得symfony的用戶Session更好配置更容易使用。

訪問用戶Session

當前用戶的Session對象在動作中使用getUser()方法訪問,他是sfUser類的一個實例。sfUser類包含了允許存儲任何用戶屬性的參數倉庫。用戶屬性能夠存放任何類型的數據(字符串、數組、關聯數組等)。

sfUser對象能夠跨請求地保存用戶屬性

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');
 }
}

可以把對象存放在用戶Session中,但這往往讓人氣餒,因為Session在請求之間被序列化了並且存儲在文件中,當Session序列化時,存儲對象的類必須已經被加載,而這很難被保證。另外,如果你存儲了Propel對象,他們可能是“延遲”的對象。

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

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

用戶Session屬性在模板中默認通過$sf_user變量訪問,他存儲了當前的sfUser對象

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

如果只想在當前請求中存儲信息,你更應該使用sfRequest類,他也有getAttribute()和setAttribute()方法,只有在請求之間持久存儲信息時才適合sfUser對象。

Flash屬性

Flash屬性是一種短命屬性,他會在最近的一次請求後消失,這樣可以保持你的Session清潔

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

在另一個動作中獲取Flash屬性:

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

一旦傳入了另一個動作,Flash屬性就消失了,即使你的Session還不過期。在模板中訪問flash屬性使用$sf_flash對象。

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

或者

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

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('login') == 'foobar') //判斷根據具體需求而定
  {
   $this->getUser()->setAuthenticated(true);  //設置用戶為已認證
  }
 }
 public function executeLogout()
 {
  $this->getUser()->setAuthenticated(false); //設置用戶為未認證
 }
}

在動作中處理憑證:

class myAccountActions extends sfActions
{
 public function executeDoThingsWithCredentials()
 {
  $user = $this->getUser();
  // 添加憑證
  $user->addCredential('foo');
  $user->addCredentials('foo', 'admin');  //添加多個憑證
  // 判斷是否具有憑證
  echo $user->hasCredential('foo');           =>  true
  // 判斷是否具有多個憑證
  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文件中設定只有admin可以訪問的動作。憑證也經常用來在模板中顯示授權信息

<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插件擴展了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('id') > 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('MyWebSite'))
   {
    // 登入
    $user->setAuthenticated(true);
   }
  }
  // 執行下一個過濾器
  $filterChain->execute();
 }
}

有時候需要在一個過濾器執行之後跳往另一個動作而不是下一個過濾器。sfFilter不包含forward方法,但sfController包含,使用下面的語句:

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

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('cookie_name')))
   ...
 }
}

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

all:
 enable_remember_me: on

過濾器示例

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

class sfGoogleAnalyticsFilter extends sfFilter
{
 public function execute($filterChain)
 {
  // 在動作之前什麼也不做
  $filterChain->execute();
  // 使用下面的代碼修飾響應
  $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()));
  }
}

這不是一種好的方式,因為在非HTML響應中不適用,只是一個例子而已。

下個例子是轉換http請求到https請求

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();
  }
 }
}

過濾器廣泛地應用於插件,允許全面地擴展應用的特性。

模塊配置

一些模塊行為依賴配置,要修改他們必須在模塊的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。

希望本文所述對大家基於Symfony框架的PHP程序設計有所幫助。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved