なさけなや...2007年09月10日 01時35分15秒

日中はあまり気乗りがしてなかったため見てなかったが、今全取り組みをみた。白鵬、横綱の負け方じゃないな。安馬がよかったのは確かだが、よりによって首投げかよ。

白鵬はどうもメンタルが脆い。まだ幼いのかもしらんが。

ZendFramework入門・その4 フォームを取り扱う・その22007年09月10日 04時18分44秒

未実装のアクションを呼び出す

さて、今回は例外処理からはじめたいと思います。とはいってもそう難しいことをやるわけではなく、Zend_Controllerが標準的に用意している仕組みの導入方法を説明するだけですが。

まずは例外を発生させるところからはじめましょう。やりかたは至極簡単、前回作成した「zf2」アプリケーションで、存在しないアクションを呼び出すだけです。 前回までの実装では、IndexControllerに実装したアクションメソッドは「index」アクションと「dump」アクションの2つのみでした。この実装状況のままで、存在しないアクションメソッド、そうたとえば「hoge」を呼び出してみましょう。

http://<host名>/zf2/index/hoge/ へアクセスしてみてください。いきなり「Fatal error」が表示されるはずです(表示されない場合はdisplay_errorsがfalseになっていると思われるので、PHP.iniを見直すか、index.phpでini_setを行うようにしてください)。

エラーハンドラを作成する

次にこの例外をキャッチして、エラーの内容を表示するようにしてみます。やり方は簡単、専用のアクションコントローラ「ErrorController」にアクションメソッド「errorAction()」を実装し、対応するビュースクリプト「error.phtml」をviews/scripts/error/に設置するだけです。

まずはErrorControllerを作成しましょう。内容は以下のように、結構シンプルなものになります。

<?php
require_once 'Zend/Controller/Action.php';

class ErrorController extends Zend_Controller_Action {
	public function errorAction() {
		// レスポンスオブジェクトから取得した
		// 例外情報をビューへassignする
		$this->view->assign(
			'errors',
			$this->getResponse()->getException()
		);
	}
}
ErrorController.php
「$this->getResponse()」はレスポンスオブジェクト「Zend_Controller_Response_Abstract」を取得するメソッドです。Zend_Controllerを使用している場合、処理中に発生したエラーはレスポンスオブジェクトに格納されるため、レスポンスオブジェクトを取得し、そのあとにgetException()メソッドで例外を取得します。そしてそれをビューへassignしています。

エラーの内容を表示する

このエラーアクションを表示するerror.phtmlは以下のようにしてみましょう。

<html>
  <head>
  	<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
  	<base href="<?php echo $this->baseUrl; ?>/"></base>
  	<title>フォーム テスト - エラー</title>
  </head>
  <body>
  	<h3>以下のエラーが発生しました</h3>
  	<ul>
  	<?php
  	foreach($this->errors as $error) {
  		echo '<li>' . $error->getMessage() . '</li>';
  	}
  	?>
  	</ul>
  </body>
</html>
error.phtml
errorAction内でassignされた「errros」を処理しているのですが、Zend_Controller_Response::getException()が例外の配列を返すためforeachで処理しています。配列の内容は例外クラス「Exception」およびその派生の例外クラスですので、getMessage()メソッドでエラーメッセージを取得することができます。Exceptionの詳細はPHPのマニュアルの「第20章 例外(exceptions)」を参照してください。

エラー処理を追加したフォルダツリー

ここまででフォルダツリーは以下のようになります。

  • htdocs/
    • zf2/
      • application/
        • controllers/
          • IndexController.php
          • ErrorController.php
        • views/
          • scripts/
            • index/
              • index.phtml
              • dump.phtml
            • error/
              • error.phtml
      • index.php
      • .htaccess

この状態でもう一度 http://<host名>/zf2/index/hoge/ へアクセスすると、今度は以下のような表示になるはずです。

以下のエラーが発生しました

  • IndexController::hogeAction() does not exist and was not trapped in __call()
index/hoge
「hogeActionが存在しない」旨のメッセージが表示されたはずです。

このように、発生したエラーはErrorController::errorActionで処理させるのがZend_Controllerの標準的な例外処理になります。この仕組みは、Zend Frameworkに標準で添付されている「エラーハンドラ」コントロールプラグインの機能により提供されています。詳細はリファレンスガイドの「7.12. MVC での例外」や「7.10.5. 標準の配布パッケージに含まれるプラグイン」を参照してください。

未実装のアクションをデフォルトアクションに振り分ける

さて、上の例では、未実装のアクションを呼び出すことで例外を発生させましたが、場合によっては未実装のアクションを例外にせず、他の動作に付け替えたい場合もでてくるかもしれません。

このような場合は、PHP5の標準的な機能である「__call」マジックメソッドを利用できます。__callは、未定義のメソッドが呼び出された場合にオーバーロードとして呼び出されるメソッドです。(「オーバーロード」の意味合いが、たとえばC#のようなほかのプログラミング言語と違う点に注意してください)

__callメソッドのシグニチャは以下のように定義されています。

mixed __call ( string $name, array $arguments )
第一引数の「$name」は呼び出された(存在しない)メソッドの名前、第二引数は呼び出しに使用された引数のリストになります。

ここでは、「未定義のアクションが呼び出されたら、デフォルトアクション(=indexAction)を代わりに実行する」ようにしてみます。追加するコードはシンプルです。

<?php
require_once 'Zend/Controller/Action.php';

class IndexController extends Zend_Controller_Action {
	// 初期化処理
	public function init() {
		// BASE要素向けのベースURL
		$this->view->assign(
			'baseUrl',
			getApplicationUrl( $this->getRequest() )
		);
	}
	
	// indexアクション
	public function indexAction() {
	}
	
	// dumpアクション
	public function dumpAction() {
		$this->view->assign(
			'postData',
			$this->getRequest()->getPost()
		);
	}
	
	// __callオーバーロード
	public function __call($name, $args) {
		// indexアクションへ処理を委譲
		$this->_forward( 'index' );
	}
}

IndexController.php(__callを追加)
上記の__callをIndexControllerに追加してhttp://<host名>/zf2/index/hoge/ へアクセスすると、先ほどはエラーが表示されていたのが、今度はindex/indexにアクセスした場合と同じ表示になります。つまりindex/hoge の呼び出しを index/index に付け替えたということです。

_forward プロテクトメソッド

アクションコントローラ内で、あるメソッドから他のアクションメソッドへ処理を委譲する場合は「_forward」メソッドを使用します。「_forward」メソッドはZend_Controller_Actionに実装されたプロテクトメソッドで、シグニチャは以下のようになっています。

final protected function _forward($action, $controller = null, $module = null, array $params = null)

第二引数でコントローラ名も指定できるのですが、通常は同じコントローラ内のほかのアクションメソッドを呼び出すと思われるためたいていは第一引数にターゲットのアクション名を指定するだけです。

ここで注意が必要なのは、_forwardの呼び出し実行時は、呼び出されるアクションコントローラが再度初期化される点です。 たとえば

public function hogeAction() {
	// 自身のhogeParamプロパティに値を設定
	$this->hogeParam = 'ほげほげ';
	// fugaアクションへ_forward
	$this->_forward( 'fuga' );
}
とした場合、その後呼び出されたfugaAction()内で「$this->hogeParam」を参照してもhogeAction内で設定した値は格納されていません。このあたりはZend_Controllerの内部における「ディスパッチ」処理の動作によるもので、いずれ解説をしたいと思いますが、ここでは「_forwardはメソッド呼び出しではなく、次のアクションの予約を入れている」ようなもの、と認識してください。

まとめ

今回の要点は以下のとおりです。

  • ErrorController::errorActionが定義されている場合、それが標準のエラーハンドラになる
  • エラー情報の取得はZend_Controller_Response::getException()で行う。戻り値はExceptionの配列である
  • __callメソッドで未定義のアクション呼び出しをカバーできる
  • Zend_Controller_Action::_forwardメソッドで他のアクションメソッドへ処理を委譲できる

あとがき&次回予告

またも予想より記事が長くなってしまったので、今回はフォーム処理周りに触れられませんでした(タイトルに偽りあり、ですね^^;)。次回こそは前回のフォームを拡張し、入力内容によって処理を分岐させるようなサンプルを行いたいと思います。また今回でてきた「_forward」の親戚筋にあたる「_redirect」についても解説してみたいと思います。

トラックバックスパムが増えてる2007年09月10日 23時19分58秒

先週くらいから、結構トラックバックスパムが増えてる。

googleに引っかかりづらい(8月に入ってからまたも嫌われてるらしいのだ)こんなしょぼブログでも、多い日は1日50件くらいのスパムがやってくる。

ほとんどがしょーもないアダルト系でとりあげるまでもないのだが、この週末からなぜか海外からのトラックバックが増えてきた。

今日なぞはnew mexico radioだのindiana weatherだの、何を意図しているのかさっぱりわからんのがやってくる。

機械処理なんだろうけど、効果あるのかな?