ホームページ  >  記事  >  バックエンド開発  >  Symfony コントロール層のさらなる分析

Symfony コントロール層のさらなる分析

不言
不言オリジナル
2018-06-08 14:54:571841ブラウズ

この記事では主に Symfony コントロール層を紹介し、多数のサンプルコードと Symfony コントローラーの一般的な使用スキルおよび関連する注意事項の詳細な分析を組み合わせています。必要な方は次のリンクを参照してください。

この記事では、Symfony コントロール層の詳細な分析を提供します。参考までに皆さんと共有してください。詳細は次のとおりです。

Symfony のコントロール層には、ビジネス ロジックとパフォーマンスを接続するコードが含まれています。コントロール層は、さまざまな用途に応じていくつかの異なる部分に分割されています。

1. フロントエンド コントローラーはアプリケーションへの唯一のエントリ ポイントです。

2. アクションには、リクエストの整合性をチェックし、プレゼンテーション層に必要なデータを準備します。
3. リクエスト、レスポンス、およびセッション オブジェクトは、コントロール層で一般的に使用されるリクエスト パラメータ、レスポンス パラメータ、および永続的なユーザー データへのアクセスを提供します。
4 フィルタは、リクエストごとに実行されるコードの一部です。 、アクションの前でもアクションの後でも。独自のフィルターを作成できます。

フロントエンド コントローラー

すべての WEB リクエストはフロントエンド コントローラーによってキャプチャされます。フロントエンド コントロールは唯一の入り口です。特定の環境ポイントのアプリケーション全体に適用されます。フロントエンド コントロールはリクエストを受信すると、ルーティング システムを使用して、ユーザーが入力した URL のアクション名とモジュール名を照合します。例:

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

URL は、index.php スクリプト (つまり、フロントエンド コントローラー) を呼び出します。これは次のように理解されます。 : action-myAction 、 module-mymodule

フロントエンド コントローラーの動作の詳細

フロントエンド コントローラーはリクエストを分散し、共通のコードのみを実行します。

1. コア定数を定義します

2. コア フレームワーク クラスをロードして初期化します
4. リクエストをデコードします。 URL を取得し、実行するコンテンツを取得します。 アクションとリクエスト パラメーター
6. アクションが存在しない場合の特別な 404 エラー
7. フィルターを有効化します (例: 認証が必要な場合)
8. フィルターを初めて実行します。
9. アクションを実行し、ビューを送信します。##10. 2 回目のフィルターを実行します。##11.


デフォルトのフロントエンド コントローラー

デフォルトのフロントエンド コントローラーは、プロジェクトの 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 のdispatch() を呼び出します (これは、 symfony MVC アーキテクチャ) メソッド。最後のステップはフィルター チェーンによってキャプチャされます。

別のフロントエンド コントローラーを呼び出して環境を置き換える

環境ごとにフロントエンド コントローラーがあり、環境は SF_ENVIRONMENT 定数で定義されます。

新しい環境の作成は、新しいフロントエンド コントローラーを作成するのと同じくらい簡単です。たとえば、アプリケーションをオンラインにする前に顧客がテストできるように、ステージング環境が必要です。ステージング環境を作成するには、web/myapp_dev.php を web/myapp_staging.php にコピーし、SF_ENVIRONMENT 定数を 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;);
// 添加批处理代码

アクション

アクションにはすべてのアプリケーション ロジックが含まれているため、アプリケーションの中心となります。彼らはモデルを使用し、ビューに変数を定義します。アプリケーションでリクエストを使用する場合、アクションとリクエスト パラメータは URL で定義されます。 Action クラス

アクションは、moduleNameActions クラスのexecuteActionName という名前のメソッドです (sfActions クラスから継承)。モジュール アクション クラスは次のように構成されます。アクションディレクトリに保存されます。

Web ディレクトリ内のファイルのみが外部からアクセスできます。PHP のメソッドは大文字と小文字が区別されませんが、symfony ではフロントエンド制御スクリプト、画像、スタイルシート、および JS ファイルが公開されます。そのため、アクション メソッドは小文字の「execute」で始まり、その後に最初の文字が大文字のアクション名が続くことを忘れないでください。

アクション クラスが大きくなる場合は、分解を行ってコードをモデル層に配置し、アクションをできるだけ短くし (数行が最適です)、すべてのビジネス ロジックを配置する必要があります。モデル層の真ん中にあります。

オプションのアクション クラスの構文

ファイルの名前は、アクション名に Action.class.php とクラスを加えたものになります。 name はアクション名に Action を加えたものです。クラスは sfActions ではなく sfAction を継承することに注意してください。

アクションでの情報の取得

アクション クラスは、コントローラー関連の情報とコア 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
 }
}
の使用方法を以下に示します。 Context:

フロント コントローラーの 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对象的方法

RequestsfRequest::GET または sfRequest::POST 定数を返しますgetMethodName()##isSecure()リクエストは 本当ですか#hasParameter('foo')#リクエスト内のパラメータです##getParameter(' foo')# がありますgetUri()フル##getLanguages()受け入れ可能なコンテンツ タイプの配列

方法名

#関数

##入力例 ###########################要請情報################## ## #

##getMethod()

Object

##Request

オブジェクト名

'POST'

#getHttpHeader('サーバー')

HTTP

ヘッダーの値

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

##getCookie('foo')

名前の値を指定します Cookie

「Is bar」

##isXmlHttpRequest()*

はい AJAXリクエストしますか?

#true

SSLリクエスト

##リクエストパラメータ

##true

##指定されたパラメータの値

'bar'

getParameterHolder()->getAll()

すべてのリクエスト パラメータ # の配列

#URI 関連情報

URI

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

##getPathInfo()

パス情報

'/mymodule/myaction'

getReferer()* *##################それはどこから来たのですか?

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

getHost()

#ホスト名

'localhost'

getScriptName()

フロントエンド コントローラーのパスと名前

'myapp_dev.php'

##クライアント ブラウザ情報

使用可能な言語のリスト

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

##getCharsets()

使用可能な文字セットのリスト

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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。