• [Zend Framework][Zend_Auth][cookie]Zend_Auth_Storage_Interface を実装してcookieに対応

    cookieで認証情報を引き回したかったので作ってみました。まだまだ改良の余地がありますが、とりあえず公開してしまいます。 本来はZend_Http_Cookieをうまく使いたかったのですが、Zene_Http_ClientやZend_Http_CookieJarから使われるのが前提のようなつくりだったのでうまく使えませんでした。折を見て改良していこうと思います。 そのうちCodeReposのコミット権をもらったらそっちにアップしますが、それまではここにソースを貼り付けておきます。 ・2008/06/24 追記 クラス名にZend_というプリフィックスは使えないとご指摘をいただいたので修正しました。名前はここ風にブログの管理者名にしています。 ・08/07/04 追記 CodeReposにアップしました。 ・08/08/26 追記 指摘されたミスを修正しました。CodeReposのほうが最新ですのでそちらを参照ください。 ・08/10/05 追記 ライセンスを明記しました。Zend Frameworkと同じです。 バグの修正しました。 以上修正はCodeReposへ反映しました。 ↓以下のソースは最新ではありませんので注意! < ?php require\_once 'Zend/Auth/Storage/Interface.php'; class Wads\_Auth\_Storage\_Cookie implements Zend\_Auth\_Storage\_Interface { /\*\* \* Default cookie name \*/ const COOKIENAME\_DEFAULT = 'Wads\_Auth\_Cookie'; protected $\_name = ""; protected $\_value = ""; protected $\_expire = null; protected $\_path = "/"; protected $\_domain = ""; protected $\_secure = false; protected $\_httponly = false; public function \_\_construct($option = self::COOKIENAME\_DEFAULT) { if (is\_string($option)) { $this->setName($option); } else if (is\_array($option)) { $this->setCookieParams($option); } else if ($option instanceof Zend\_Config) { $this->setCookieParams($option); } else { $option = (string)$option; } } public function \_\_get($name) { $func = "get".
  • [Zend Framework][Smarty] Zend_Layout ③ Smartyに対応させてみる

    今回はZend_View_Interfaceを実装してSmartyに対応させたビューをZend_Layoutへ対応させてみようという試みです。 使用するSmartyのクラスはこれ(ViewSmarty)です。 まずはZend_Layoutにviewを登録する方法ですが、以下のどちらかの方法でViewSmartyのクラスを登録します(まだ他にもあるかもしれません)。 Zend_Layoutの$_viewプロパティに設定する Zend_Controller_Action_Helper_ViewRendererに登録する ・Zend_Layoutの$_viewプロパティに設定する``` require_once ‘Zend/Layout.php’; require_once ‘ViewSmarty.php’; Zend_Layout::startMvc(array( ‘view’ => new ViewSmarty(), )); もしくは require_once ‘Zend/Layout.php’; require_once ‘ViewSmarty.php’; Zend_Layout::startMvc(); $layout = Zend_Layout::getMvcInstance(); $layout->setView(new ViewSmarty()); ・Zend\_Controller\_Action\_Helper\_ViewRendererに登録する require_once ‘Zend/Controller/Action/Helper/ViewRenderer.php’; require_once ‘Zend/Layout.php’; require_once ‘ViewSmarty.php’; Zend_Controller_Action_HelperBroker::addHelper( new Zend_Controller_Action_Helper_ViewRenderer(new ViewSmarty()) ); Zend_Layout::startMvc(); この記述は [bootstrap.php](http://wadslab.net/2008/05/zend_controller-5/)に書くと良いでしょう。 これで実行すると、addScriptPath()がないというエラーが出ます。 addScriptPath()関数はZend\_View\_Abstract関数で実装されています。これは指定したパスを現在のビュースクリプトを走査するパスへ追加します。Smatryをざっと見たところ、複数のパスを設定することはできなさそうでした。ViewSmatyのほうで仕掛けを作ってやる必要がありそうです。 今回ところは、Zend\_View\_Interfaceにも規定されているsetScriptPath()関数のエイリアスとしておきます。 /** * Zend_Layout対応 * Smartyではスクリプトパス(template_dir)は複数指定で * きないので、setScriptPath()のエイリアスとしておく。 * 配列が渡された場合は最初の要素をパラメタとする。 * * @return string|array $path */ public function addScriptPath($path) { if(is_array($path)) { $path = array_shift($path); } return $this->setScriptPath($path); }
  • [Zend Framework][php] Zend_Layout ② クイックスタート

    前回の続き。 Zend_LayoutをZend FrameworkのMVCを利用して使用し、ビューを表示する例を書きます。 今回使用するファイルおよびディレクトリ構成は以下のようになっています。``` AppBase/ application/ default/ controllers/ IndexController.php views/ layouts/ layout.phtml another.phtml scripts/ index/ foo.phtml bar.phtml menu.phtml bootstrap.php lib/ Zend/ public/ .htaccess index.php 順々に見ていこうと思います。 フロントコントローラの役割を果たすindex.phpは以下のようにbootstrap.phpを呼び出すだけです。 ここの詳細はこちらをご覧ください。 AppBase/public/index.php < ?php require ‘../application/bootstrap.php’; bootstrap.phpにてZend\_Layoutのインスタンス化および設定を行います。 staticメソッドのstartMvc()を呼び出すと、Zend\_Layoutはインスタンスを生成し、クラスの内部に保持します。getMvcInstance()関数を使えばインスタンスを取得することができます。 startMvc()の引数にZend\_Configか配列を渡して、オプションの設定ができます。配列のキーに設定名、値に設定値を指定します。今回はレイアウトスクリプトの置き場所を指定するlayoutPathだけを設定しています。もし指定しない場合はZend\_Viewのスクリプトパスが使用されます。以下にbootstrap.phpの内容を書きます AppBase/application/bootstrap.php < ?php // Zend_Layout の設定 require_once ‘Zend/Layout.php’; Zend_Layout::startMvc(array( ‘layoutPath’ => ‘../application/default/views/layouts’ )); // Zend_Controllerなど、その他の設定は省略 このほかにもstartMvc()で指定できるオプション設定はいろいろありますが今回は割愛します。 続いてコントローラです。 AppBase/application/default/controllers/IndexController.php < ?php require_once ‘Zend/Controller/Action.php’; class IndexController extends Zend_Controller_Action { public function fooAction() { } public function barAction() { $this->\_helper->layout->setLayout('another'); // 別の方法 //Zend\_Layout::getMvcInstance()->setLayout('another'); } }
  • [Zend Framework] Zend_Layout ① 使いどころなど

    Zend Framework 1.5からZend_Layoutが追加されています。興味はあったのですが、リファレンスガイドやその他ちょっと調べてもいまいち良くわからなかったので敬遠していましたが、このたびAkra’s DevNotesのブログでとてもわかりやす解説を見つけて理解するにいたったのでここに書くことにしました。 Zend_Layoutは有名な「2ステップビューパターン」を実装したものと書いてあります。私はこの「2ステップビューパターン」というものを良く知りませんでした。Akra’s DevNotesには以下のように書いてあります。``` The view is now two-step. This means that we split our HTML between multiple files. The first step is to render the “inner” scripts, such as the sidebar and the action specific scripts. Then we render the “outer”, layout script which embeds the rendered “inner” scripts. 意訳すると、HTMLをいくつかのスクリプトに分けて、まずinnerスクリプトを描画したあとに、innerスクリプトが組み込んであるouter レイアウトスクリプトを描画するとあります。outerスクリプトがレイアウトにあたる部分で、innerスクリプトがコンテンツにあたる部分になります。 私もそうだったのですが、このようにinnerスクリプトとouterスクリプトに分けるようなことは皆さんもごく自然にやっているのではないかと思います。私は以下のようにやっていました。 … < ?php require '/path/to/view/script/share/menu.phtml'; ?> < ?php echo $this->list_type ?>の一覧表示 < ?
  • [Zend Framework] Zend Frameworkのbootstrap.php

    Zend Frameworkを使ってMVCフレームワークで開発をしようとする場合、Zend Frameworkのリファレンスガイドではドキュメントルート直下に置いたindex.phpをフロントコントローラとして使います。そして本命のディスパッチ処理はもちろん、ビューやルータの設定、また必要であればログ(Zend_Log)や権限(Zend_Acl)の設定もこのファイルに書いてしまうことが多いと思います。 Zend Framework COMMUNUTY Wikiには、まだ作成途中ですがOfficial ZF QuickStartという項目があって、リファレンスガイドのやり方とはちょっと違っていたのでメモっておきます。 ディレクトリ構成は以下のようになっています。``` AppRoot/ application/ controllers/ views/ scripts/ library/ public/ ドキュメントルートはAppRoot/publicで、ここにindex.phpが配置されます。bootstrap.phpはAppRoot/applicationの直下に置きます。AppRoot/application以下はZend Frameworkのリファレンスガイドにも例があるのでおなじみかと思います。AppRoot/library以下には、Zend Frameworkのライブラリ含めた外部ライブラリを置くところになっています。 AppRoot/public/index.php は以下のようになっています。 < ?php require ‘../application/bootstrap.php’; bootstrap.phpをインクルードするだけです。 AppRoot/application/bootstrap.phpにこれまでindex.phpに記述していた内容を書けばOKです。内容はほんの一例です。 < ?php error_reporting(E_ALL | E_STRICT); // インクルードパスの追加 set_include_path('../library’ . PATH_SEPARATOR . get_include_path()); // autoloaderの設定 require_once “Zend/Loader.php”; Zend_Loader::registerAutoload(); // front controllerの設定 $frontController = Zend_Controller_Front::getInstance(); $frontController->throwExceptions(true); $frontController->setControllerDirectory('../application/controllers’); $frontController->dispatch();
  • [zend framework][Yaml] Zend_Config_Yamlのようなものを考えてみる2

    前回に引き続きZend_Config_Yaml.phpのようなものを考えてみました。 Yamlのパーサは前回挙げたPHP拡張モジュール SyckとPHPライブラリのspycに対応しています。 Zend_Config_Iniでは継承を示すトークンに「:(コロン)」を使っていましたが、Yamlで「:」は特別な意味を持っているので[<]を使いました。 実装は以下のようになっています。 < ?php require\_once 'Zend/Config.php'; class Wads\_Config\_Yaml extends Zend\_Config { /** * String that symbol extended section */ protected $\_extendsSection = ';extends'; /** * constructor * * @param string $filename * @param mixed $section * @param boolean $allowModifications * @throws Zend\_Config\_Exception */ public function \_\_construct($filename, $section = null, $allowModifications = false) { if (empty($filename)) { /\*\* @see Zend\_Config\_Exception \*/ require\_once 'Zend/Config/Exception.php'; throw new Zend\_Config\_Exception('Filename is not set'); } $yamlArray = $this->\_parse\_yaml\_file($filename); $preProcessedArray = array(); foreach ($yamlArray as $key => $data) { $bits = explode('< ', $key); $numberOfBits = count($bits); $thisSection = trim($bits\[0\]); switch (count($bits)) { case 1: $preProcessedArray\[$thisSection\] = $data; break; case 2: $extendedSection = trim($bits\[1\]); if(!
  • [Zend Framework][yaml] Zend_Config_Yamlのようなものを考えてみる

    前回のエントリーに対するコメントで、YAMLはでないですかねというコメントがあってちょっと触発されてZend_Config_Yamlについて色々考えてみました。一応最初に言っておきますが、Zend_Config_Yamlのようなものができたわけではないのであまり期待せず読んでください。 Zend_ConfigではINIファイルとXMLファイルが対応していますが、それぞれ内容のパースは parse\_ini\_file() simplexml\_load\_file() というPHPに組み込みの関数で行っています。Zend_Yamlもないし、YAMLデータのパーサーもPHPに組み込まれていないのでZend_Config_Yamlというのがまだ実装されていないのでしょう。 YAML形式のデータのパーサーはPHPに組み込まれていませんが、拡張モジュールやライブラリとしてはいくつか実装されています。 PHP拡張モジュール Syck PHPライブラリのspyc YAMLの勉強がてらZend_Config_Yamlのようなものを作ってみようと思います(ただしYAMLの仕様まったく詳しくないのでおかしいところありましたらどんどん突込んでください)。 spycはphpファイルをダウンロードしてインクルードするだけで使えるのでまず最初に使ってみます。 YAML形式のデータのパースまでは以下のとおり簡単にできました。 require\_once "spyc/spyc.php5"; define("INPUT\_YAML\_FILE", './test.yaml'); // YAMLファイルをパースする $res = Spyc::YAMLLoad(INPUT\_YAML\_FILE); 結果は配列形式になります。たとえば、以下のようなデータがあれば Name: foo - a - b - c このようになります。 array(4) { \["Name"\]=> string(3) "foo" \[0\]=> string(1) "a" \[1\]=> string(1) "b" \[2\]=> string(1) "c" } ここで、Zend Frameworkのマニュアルには以下のように書いてあります。 例 5.1. Zend\_Config の使用例 通常は、Zend\_Config\_Ini あるいは Zend\_Config\_Xml のような アダプタクラスを使用することが想定されています。 しかし、 もし設定データが PHP の配列として存在するのなら、 単にそれ を Zend\_Config のコンストラクタに渡すだけで、 シンプルなオ ブジェクト指向のインターフェイスを使用することができます。 ということは、以下のようにするだけでYAML形式のデータをZend_Configで扱えるようになったことになります。
  • [Zend Framework][xml] Zend_Config_Xmlでxmlの要素名に-(ハイフン)が使えない

    Zend_Config_Xmlでは、コンフィグファイルを読み込むと、XMLの要素名をプロパティーとしてアクセスできるようになります。 test_config.xml < ?xml version="1.0" ?> 123457 というデータがあったら require_once 'Zend/Config/Xml.php'; $config = new Zend_Config_Xml('./test_config.xml'); echo $config->data->text; として読み出せるのですが、xmlの要素名に-(ハイフン)を使うと、PHP(というか大体のプログラム言語か?)ではプロパティー名に-を使えないので、上記のようにアクセスできなくなってしまいます。 < ?xml version\="1.0" ?> <testconfig\> <data\> <text\>123457</text\> <text-size\>12px</text-size\> </data\> </testconfig\> require_once 'Zend/Config/Xml.php'; $config = new Zend_Config_Xml('./test_config.xml'); // アクセスできない echo $config->data->text-size; // 0 PHP Noticeがでる // アクセスできるけど面倒くさい echo $config->data->{"text-size"}; // 12px $text_size = "text-size"; echo $config->data->$text\_size; // 12px ハイフン使わないでアンダーバー使えという話ですが、なんとなく気持ち悪いです。このためにZend_Config.phpやZend_Config_Xml.phpをいじるのも微妙です。 ちょっとした愚痴でした。
  • [Zend Framework][MySQL] Zend_Dbでトランザクション(MySQLi)

    Zend_Db_Adapter_Mysqliを使ってDBのトランザクションを制御する方法を書きます。とはいえ、インターフェースはZend_Db_Adapter_Abstractで定義されているので他のDBでも処理の書き方には変わりはないと思います。 MySQLの代表的なストレージエンジンで、InnoDBはトランザクションをサーポートしていますが、MyISAMはしていません。MyISAMでトランザクションを開始して、ロールバックを行った場合、特にException等が発生することはなくトランザクションの処理が無視(行の挿入や更新、削除等に対する取り消しがされません)されるだけですので注意してください。 トランザクションを開始するには、Zend_Db_AbstractのbeginTransaction()を、DBに対する処理を確定するにはcommit()関数を使います。また、何らかの処理が発生して、DBに対する処理をキャンセルしたい場合にはrollBack()を行います。``` // Zend_Db_Tableを使っている場合は、 // $db = $tbl->getAdapter() でDBアダプターを取得しておきます // トランザクションの開始 $db->beginTransaction(); try { $db->query(‘insert …'); $db->query(‘update …'); $db->query(‘insert …'); // Zend\_Db\_Table を使っている場合は、 // $tbl->insert($values); // $tbl->update($set, $where); // $tbl->insert($values); // コミット $db->commit(); } catch (Exception $e) { // 例外が発生したらロールバック $db->rollBack(); // 他のエラー処理 } コミットとロールバックでDBに値がどのように変化するか調べます。 以下のようなテーブルの定義のときに、 mysql> show create table test\G *************************** 1. row *************************** Table: test Create Table: CREATE TABLE `test` ( `id` int(11) NOT NULL auto_increment, `title` varchar(50) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
  • [Zend Framework] Zend_Controller_Action_Helper_ContextSwitchで独自のコンテクストを追加する方法

    Zend_Controller_Action_Helper_ContextSwitch(長い)で、独自のcontextを追加する方法を書きます。 デフォルトで組み込まれている機能(xmlとjson形式)の使い方は前のエントリーを参照ください。今回は自分の都合で、gif形式での方法を書きます。 新しいコンテキストを追加する方法は、addContext()を使います。 インターフェースは以下のようになっています。``` public function addContext($context, array $spec) $contextでコンテキスト名を指定し、$specでコンテキストの設定を配列で指定します。$contextがすでにあるコンテキストと重複した場合は、Zend\_Controller\_Action\_Exceptiongがスローされます。 $specには、配列のキーで設定名、値で設定値を指定します。設定名は、suffix、headers、callbacksがあります。ひとつづつ説明していきます。 **suffix** ViewRenderer に登録されているビュースクリプトのサフィックス(デフォルトでは .phtml)の前に追加するサフィックスです。ViewRendererを有効にしている場合、fooAction()のビュースクリプト名は、デフォルトでfoo.phtmlとなりますが、ContextSwitchアクションヘルパーでコンテキストスイッチを行うと、参照されるビュースクリプトはfoo.{suffix名}.phtmlとなります。 ‘suffix'=>'gif’ のように設定した場合は、foo.gif.phtmlというファイルがビュースクリプトとなります。 **headers** ヘッダの名前を配列のキー、値を配列の値とした値で、コンテキストスイッチを行ったときにレスポンスヘッダに追加したいヘッダを指定します。Content-Typeは必須だとおもうので、たとえば ‘headers'=>array(‘Content-Type'=>'image/gif’) という設定をします。個人的にはもう少し設定しますが今回は割愛します。 **callbacks** キーに'init'や'post'を含む配列で、それぞれコンテキストの初期化や後処理の際に使用するコールバック関数を指定します。たとえば、 ‘callbacks’ => array( ‘init’ => ‘initGifContext’, ‘post’ => ‘postGifContext’ ) のように設定してある場合、initGifContext()がinitContext()コール時に、コントローラのpostDispatch()時にそれぞれ実行されます。 デフォルトで組み込まれているjsonでは、 ‘callbacks’ => array( ‘init’ => ‘initJsonContext’, ‘post’ => ‘postJsonContext’ ) 1. $callbackが文字列なら、同名のクラスメソッドを探す 2. $callbackが文字列なら、PHP関数を探す 3. $callbackが配列なら、call\_user\_func()に引数として渡す 4. ここまでで関数が見つからなければZend\_Controller\_Action\_Exceptionをスローする これまでの説明を踏まえて、設定の例を以下に示します。``` $spec = array( 'suffix' => 'gif', 'headers' => array( 'Content-Type' => 'image/gif', ), 'callbacks' => array( 'init' => 'initGifContext', 'post' => 'postGifContext' ) ); $this->addContext('gif', $spec); ```あとは、コールバック関数のinitGifContext()とpostGifContext()の定義です。 jsonのものを参考に、それぞれ定義してみました。 **・initGifContext() - ビューレンダラーの解除**``` public function initGifContext() { $viewRenderer = Zend\_Controller\_Action\_HelperBroker::getStaticHelper('viewRenderer'); $view = $viewRenderer->view; if ($view instanceof Zend\_View\_Interface) { $viewRenderer->setNoRender(true); } } ```**・postGifContext() - gifファイルの出力**``` public function postGifContext() { $viewRenderer = Zend\_Controller\_Action\_HelperBroker::getStaticHelper('viewRenderer'); $view = $viewRenderer->view; if ($view instanceof Zend\_View\_Interface) { $vars = $view->getVars(); if (@readfile($vars\['file'\]) === FALSE) { throw new Exception('failed readfile()'); } } } ```アクションメソッドにて、ビューファイルにfileという名前で設定(assign())したgifファイルを出力する仕様になっています。 今回は新しいコンテクストの作り方として、Zend\_Controller\_Action\_Helper\_ContextSwitchを継承したクラスに書くことにします。 **MyGifContext.