- ベストアンサー
Zend Frameworkについての質問
- Zend Frameworkを勉強中の質問者が、routeStartup、routeShutdown、dispatchLoopStartup、preDispatch、postDispatch、dispatchLoopShutdownというメソッド内でどのような処理を行うべきか悩んでいます。
- 質問者はDB接続、認証、PCと携帯サイトの切り替え、文字コード変換などの処理が考えられると考えていますが、具体的な実装場所について混乱しています。
- 質問者はBootstrap.phpで全ての初期設定を行っており、メソッドの増加によりソースコードが長くなってしまっています。プラグインを使って各処理を分離する考えもありますが、正しい方法かどうか不安です。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
私も、最近になってZend_Frameworkを触り始めたので、丁度勉強になったので色々調べてみました。 ちょっと、すでに分かりきっていることも含めて書きますがお付き合い下さい。 Zend_ApplicationでZend Frameworkを使う場合、Bootstrapクラス内で、今つくっているApplication内に必要なクラスやコンフィグなどを全部初期化します。 ■Bootstrapはどう動いているのか Zend_Applicationにて、Bootstrapのインスタンスが呼ばれて実行される際、PHP標準クラスのReflectionClass(または、get_calss_methods関数)にて、Bootstrap内に自分で記述した「_initXXX」という命名のものをすべて配列に書き出してきて、 それらのメソッドを全部一括で叩きます。 <?php class Bootstrap extends .......{ protected function _initBar(){ echo "Hoge!!!"; } protected function _initFoo(){ echo "Foo!!"; } } ?> このように記述してやると自動的に、_initBarと_initFooは実行され、出力の先頭に、「Hoge!!!」「Foo!!」とechoされます。 つまり、自分が必要なクラスの初期化やクラスの設定なんかを適当な命名で、_initXXXと記述して、そのメソッド内に 設定処理を書いてやれば、自動的にそれらが設定されるというものです。 この説明は質問文を見る限り、分かっているということですよね。 ■Zend_Controller_Frontはいつ呼ばれるのか これは、Zend_Application_Bootstrap_Bootstrapのコンストラクタで、予め、Zend_Application_Resource_Frontcontrollerというクラスで予約されています(左記のクラスからFrontControllerのインスタンスが返されるようになってる) これがクラス変数(配列)の中に「frontcontroller」というキーで格納されてます。 その為、前述した「_initXXX」内で、「$this->bootstrap('frontcontroller');」などと叩くと、予めfrontcontrollerが予約されているので、インスタンスが生成され、実行準備完了となり、 「$this->getResource('frontcontroller');」を実行すると、Zend_Controller_Frontのインスタンスが取得出来るようになります。 ■で、どうすればいいのか。 上記のルールから、とにかく、適当なinitメソッドを作ってやり、好きなところで記述してやればよいということになります。 <?php protected function _initRegisterPlugins(){ //frontcontrollerのインスタンスを作成 $this->bootstrap('frontcontroller'); //インスタンスが上記で作成されているので、今度は取得 $front = $this->getResource('frontcontroller'); //これでFrontが呼べているので、プラグインを登録 $front->registerPlugin(new Your_Plugin()); } ?> となります。 ■application.iniはいつ設定されているか Zend_Applicationで、iniの内容がOptionとして取得されていて、Zend_Application_Bootstrap_Boostrapのコンストラクタ内で、全部の値を取得しなおし、setOptionsで一括投入をしているようです。 とりあえず、具体的な処理の内容は、Zend_Application_Bootstrap_BootstrapAbstract内にあったので、ソースも一度追って見るといいかもしれません。
その他の回答 (3)
- hogehoge78
- ベストアンサー率80% (433/539)
現象確認しました。良く調べてなくてすみません。 内容把握したので、Zend_Layoutがなにやっているのか、説明します。 Zend_Layoutは、これのインスタンスを作成すると、同時に、Zend_Layout_Controller_Plugin_Layoutをプラグインに登録します。 その登録を行う際、 Zend_Controller_Front::registerPluginの第二引数に、動作順序を登録していました。 <?php $front->registerPlugin($plugin_layout, 99); //みたいな感じで。 ?> で例の通り、実際に99番目、に登録してました。100番目が、Zend_Controller_Plugin_ErrorHandlerです。 Zend_Layout_Plugin_Layoutは、ソレより前に出力した全部の出力を、Layout(/layouts/layout.phtml)の中に全部パックする挙動を取ります。 <?php public function postDispatch($request){ $response = $this->getResponse(); $content = $response->getBody(); $content = $this->layout()->render($content); $response->setBody($content); } //イメージです。Responseの中にある全部の内容を取得して、Layoutの中に収めて、もう一度Responseの中に戻す。 ?> というわけですので、今回文字コードを変更するプラグインを作る場合は、 <?php $front->regsiterPlugin(new Your_Plugin_Name(), 101); ?> というように、一番最後に登録するようにすれば、Layout含めすべてを変換出来ます。 といった感じでした。 ここから、学べることとして、 何か、例えば、Your_Mobileクラスというモバイルに特化した何かのライブラリを作ったとして、 ソレのインスタンスを生成するとともに、Your_Mobile_Plugin_Convertクラスとかプラグインも作り、ソレをregisterPluginとすると 統合的なライブラリとして動かしたりするのが楽になったりするかもしれませんね。(他にもZend_Controller_Action_Helperとかも。) 如何でしょうか。
- hogehoge78
- ベストアンサー率80% (433/539)
プラグインで、 $this->getResponse(); をした時に取得されるものというのは、 Zend_Controller_Response_Http というクラスになります。 このクラスは、header関数などで吐き出すHTTPヘッダと、HTTPのBODY部分(つまりHTMLやXML)を格納するオブジェクトです。 基本的には、Controller_Action内とか適当な場所でechoを行っても、最終的にこのResponseオブジェクトに出力が格納されてます。 Zend_Viewも、renderした後その場でechoしているのではなく、Responseオブジェクトに出力したテンプレートが格納されているイメージです。 ですので、Ext_View_Smartyは、参考サイトに記述されているまま使用して、プラグイン側で、私が書いたコードをそのまま使うと、解決すると思います。 ■とても大雑把な処理の流れ 1.Zend_Controller_Request_Httpにクライアント(お客さん/Webブラウザ)からの要求が格納される 2.Zend_Coontroller_Actionで、コンテンツの振り分けとかheaderの設定とか、出力処理を行い、それらがすべてZend_Controller_Response_Httpにキャッシュされる 3.postDispatchとか全処理が終了したら、Zend_Contorller_Response_HttpにキャッシュされたHTTPヘッダが出力され、 HTTPのBODYが出力される って感じです。 多分2番の工程にたどり着いたときに、最初にob_start関数が叩かれて、処理が終わったら、ob_get_cleanかなんかで、Response_Httpに格納する、とか処理をしてるのではないかなぁと思います。
お礼
たびたびのお返事ありがとうございます。 最初にそのままのソースで試したのですが結果は、メインコンテンツの部分 (IndexControllerのindexActionであればindex.tpl)だけ文字コードが 変換され出力されましたがlayout.tplで読込みしている他のtplに記載して あるものに関しては文字コードが変換できなかったので試行錯誤しておりました。 試しに、postDispatchでデバッグしてみましたがindex.tplの内容だけが 選択範囲となっているようです^^; Zend_Debug::dump($this->getResponse()->getBody()); 参考までにlayout.tplの中身は以下のようになっているのですが書き方が まずいのでしょうか? <div id="header"> <{include file="header.tpl"}> </div> <div id="content"> <{$this->layout()->content nofilter}> </div>
- hogehoge78
- ベストアンサー率80% (433/539)
今日はじめて使ってみたので、あまり当てにならないかもしれないですが、 結局この、Zend_Controller_Pluginは、RequestとResponseの切り分けとか操作を行う物のようです。 ■routeStartup/routeShutdown 受け取ったRequestに何か不正なものがあった場合に、Responseで、エラー画面にリダイレクトをするようにするのに使えそうです。 また、$request->setParam() あたりで、デフォルトのパラメータを指定しておけば、Actionコントローラでソレを拾うことが出来ますので、ソレをコントローラ側の切り分けの材料にすることが出来るかもしれませんね。 また、リクエストパスを書き換えちゃえば、RouterにRequestオブジェクトが渡された際に別のコントローラを動作させることが出来るかもしれません。 ■preDispatch これは、dcx147さんが仰るとおり、全コントローラ共通の認証などで使えます。 Zend_Controller_Actionに中間的な抽象クラスを作ってそこで処理してやる方法(CakePHPのAppController的なもの)でもいいですが、プラグイン化することで、別のアプリケーションを作成するときにも、簡単に移行出来るので便利かもしれません。 また、ここでブラウザの種類やIPなどで判別して、上述した$request->setParamに、('is_mobile', true)とか書きこんでやって、Actionコントローラ側で簡単に判別出来るようにする処理をいれてやるのもいいかもしれません。 ■postDispatch 文字コードの変換は、Viewのヘルパのほうが・・・とおっしゃっていましたが、 プログラムは、ブラウザからのRequestを受けて、処理をすべて行った後にResponseとして、HTTPヘッダとコンテンツ(主にHTML)を返します。ZendFrameworkもなんだかんだ処理を行って、最終的には、Zend_Controller_Response_Httpに全HTTPヘッダとコンテンツを入力して、最後に吐き出す、という処理をしています。 ですので、作成しているプログラムの文字コードとは違う文字コードをブラウザに出力するなら、ここで一括で文字コード変換、とか絵文字ライブラリで一括でキャリアごとの絵文字に書き換える、という事を行ったほうが、事は簡単になるかもしれません。 また、preDispatchと組み合わせて、preDispatchでRequestオブジェクト内にあるPostリクエストやGetリクエストなどを、Viewで指定してある文字コードからアプリケーションの文字コードに変換する処理をフックしてやるということと、 postDispatchで、Viewが最終的に構築したアプリケーションの文字コードのHTMLをまたViewで指定したい文字コードに変換をする、とかやると、修正が凄く簡単になる、のではないかなと妄想しました。 <?php public function postDispatch(Zend_Controller_Request_Abstract $request){ $content = $this->getResponse()->getBody(); $content = mb_convert_encoding($content, 'sjis-win', 'utf-8'); $this->getResponse()->setBody($content); } ?> と、これだけでも結構使えそうな気がしましたが、如何でしょうか。
お礼
お返事ありがとうございます。 このような具体的なご意見をいただけますと非常に為になります。 文字コード変換について1つ質問なのですがsmartyと連携させており 下記のURLのソースを利用しております。 http://www.gediminasm.org/article/smarty-3-extension-for-zend-framework Ext_View_Smartyのrunメソッドで下記のようにし(抜粋) $output = $this->_smarty->fetch($template); //echo $output; Zend_Registry::set('output', $output); //$this->_smarty->display($template); プラグインのpostDispatchメソッドで $output = Zend_Registry::get('output'); $output = mb_convert_encoding($output, 'SJIS-win', 'UTF-8'); echo $output; としているのですがメインとなるコンテンツしか表示されません。 layout.tplで全体の出力の制御をしておりヘッダーやフッターtplの読込みや メインコンテンツの出力を行っている状況です。 Ext_View_Smartyのrunメソッドでechoするとヘッダーやフッターtplの読込みも 問題なく表示されるのですがタイミングの問題とかでしょうか?
お礼
お返事ありがとうございます。 わざわざ試していただいたようで本当に感謝です! プラグインの実行する順番を101として試したところ下記の内容でOKでした。 $response = $this->getResponse(); $content = $response->getBody(); //$content = $this->layout()->render($content); $content = mb_convert_encoding($content, 'SJIS-win', 'UTF-8'); $response->setBody($content); 順番があるなんてマニュアルを見ても全くわかりませんでしたしアドバイスを いただいたおかげでコツを掴む事ができ、試しながら他の処理もプラグインで 作成できるようになりました! 1つ気になっている事がありこれで最後にしたいと思うのですが・・・ プラグインの登録など全てapplication.iniで行っているのですが探し方が わるいのかマニュアルでよくみる下記の書式がみあたりません。 $front = Zend_Controller_Front::getInstance(); インスタンスを取得して$front->registerPluginのようなインスタンスありきの 処理が必要な場合にはどうすればいいのでしょうか? インスタンスなしでメソッドを実行してもエラーになってしまいますよね? 当然、見えないところで内部的に処理を行っているのは間違いないと思うのですが 不思議です。