アーカイブ

‘Zend Framework’ カテゴリーのアーカイブ

Zend_ApplicationでZend_Session_SaveHandler_DbTableを使うとエラーになってしまう

2009 年 8 月 4 日 admin コメントはありません

Zend_Applicationが便利そうなので使ってみたら一つハマりましたので報告(Zend Frameword 1.9.0にて)。

Zend_Application_Resource_Sessionを使って設定ファイルからZend_Session_SaveHandler_DbTableの設定を行なおうとすると

Zend_Db_Table_Exception: No adapter found for Zend_Session_SaveHandler_DbTable in /library/Zend/Db/Table/Abstract.php on line 754

というエラーメッセージを吐いて止まってしまいます。

対処法としては”全体のブートストラップを行なう前にDBリソースだけ先にブートストラップを行う”という方法をとってみましたが、あまり美しい方法では無いので次のヴァージョンアップで直っていることを願います。

$application->getBootstrap()->bootstrap('db');
$application->bootstrap()
            ->run();
カテゴリー: Zend Framework タグ:

Zend_Sessionでセッションデータの検証

2008 年 12 月 10 日 admin コメント 2 件

Zend_Session_Validateなる存在をみなさんご存知ですか?Zend Frameworkのマニュアルには載っていないのですがZend_Session_Validateを使用する事でZend_Session::start()時にセッションデータの検証を行う事が出来るのです。Zend_Sessionのソースを覗いている時に偶然発見してしまいました。たまにはソースも覗いてみるべきですね!

Zend_Session_Validatorの処理としてはZend_Session::registerValidator()で検証すべきデータをセッションに格納し、Zend_Session::start()の時にセッションデータの検証を行います。で、その検証に失敗するとZend_Session_Exceptionが返されますのでtry~catchで受け取り適切な処理を記述する形になります。

最新版Zned Framework(1.7.1)で組み込まれているZend_Session_ValidateはZend_Session_Validator_HttpUserAgentだけですので、とりあえずこれを実装してみます。


try {
	Zend_Session::start();
} catch (Zend_Session_Exception $e) {
	throw $e;
}
Zend_Session::registerValidator(new Zend_Session_Validator_HttpUserAgent());

これで前回セッションを使用した時とUserAgentの変更があればセッションデータを破棄することができます。携帯サイト等でこのコードを実装すると多少問題が出ることもありそうですが、PCサイトであれば多少なりとも安全になりますのでオススメです。

あと注意点として”Zend_Session::start()の後にZend_Session::registerValidator()を行う事”を挙げておきます。処理を理解すると当然なんですが、何となく逆にしてしまいがちなので。

2008/12/15 追記:ZJ吉田さんがコメントされた内容

早速バグとして報告をあげておきました。

がどうしても気になったのでメールで確認してみたところ、『マニュアルにZend_Session_Validatorの記述が無い』というバグでした。バグはプログラムの方だと思い込んでいたのでマニュアルのバグとは気付きませんでした。ZJ吉田さんのおかげでZend Frameworkに貢献でき大変感謝です!!

2009/02/23 追記

サンプルソースに間違いがありましたので訂正いたしました。
Zend_Session::start()に失敗した場合、Zend_Session::destroy()を行なってsessionを破棄しようとしていましたが、startに失敗しているのに破棄できる訳がなくWarningメッセージ(Trying to destroy uninitialized session)をはいておりました。

カテゴリー: Zend Framework タグ:

Zend Framework(Zend_Validate)におけるZend_Translateを用いたエラーメッセージの日本語化

2008 年 12 月 9 日 admin コメント 6 件

Zend Frameworkではデータ検証用クラスとしてZend_Validateが用意されています。日ごろ使いそうな検証はほぼ揃っているのでありがたく使わせてもらっているのですが、残念ながらこのZend_Validateには日本語のエラーメッセージが実装されていません。なのでエラーメッセージの日本語化を自分で行う必要があります。 Zend Frameworkではメッセージの多言語化を行うためにZend_Translateというクラスが用意されているので、今回はこのZend_Translateを用いてZend_Validateの日本語化を行ってみます。

まずZend_Translateを用いてメッセージの他言語化を行う場合、メッセージの翻訳データを用意する必要があります。そしてZend_Translateではその翻訳データの種類に合わせて多くのアダプターが用意されています。私は今までプログラムの翻訳作業等やったことがないので、とりあえず一番使われていそうなGettextアダプタを使用してみることにします。gettextについての解説は下記サイトが大変参考になりました。

GNU gettextユーティリティ

gettextで翻訳作業を行うために、まずPOファイルと言うものが必要になります。gettext関数を用いて書かれたプログラムであればxgettextなどを用いて自動で翻訳のもとになるメッセージを抽出できるようなのですが、今回はクラスで定義されたプロパティからメッセージを抽出する必要があるので、自作でZend_Validateからメッセージを抽出するプログラムを作ってみました。POファイルはテキストファイルなので手作業で作る事も出来るのですが、今後Zend_Translateのクラスが増えたり、メッセージが増えたりする事があるかもしれませんのでこのようにプログラムを組んでおくと楽なんじゃなかろうかと思います。

xgettext.pl

#!/usr/bin/perl
use File::Basename;

my $start;
my $basename;
my %texts;

while (<>) {
    chomp;
    if (/\$_messageTemplates = array\($/) {
        $start = 1;
        next;
    } elsif ($start) {
        if (/\);$/) {
            $start = 0;
            next;
        }
        s/^.*=> ('|")([^\1]*)\1,?$/$2/;
        $basename = basename(${ARGV});
        push @{$texts{$_}}, $basename . ':' . $.;
    }
    if (eof) {
        close(ARGV);
    }
}

print "msgid \"\"\n";
print "msgstr \"\"\n";
print "\"Content-Type: text/plain; charset=utf-8\\n\"\n";
print "\"Content-Transfer-Encoding: 8bit\\n\"\n\n";

foreach $key (sort keys %texts) {
    foreach my $file_name (@{$texts{$key}}) {
        print "#: ${file_name}\n";
    }
    print "msgid \"${key}\"\n";
    print "msgstr \"\"\n\n";
}

PHPのフレームワークの解説なのにperlで実装してしまいました。私の中でコマンドラインのプログラムを書く際はシェルスクリプトかperlで書くという掟があるのです。許して下さい…。

./xgettext.pl lib/Zend/Validate/* > validate.po

とコマンドラインでやってもらえるとPOファイルが作成できます。
ちなみに現在のZend Frameworkの最新版(1.7.1)でのPOファイルはこんな感じです。

msgid ""
msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"

#: EmailAddress.php:58
msgid "'%hostname%' does not appear to have a valid MX record for the email address '%value%'"
msgstr ""

#: EmailAddress.php:57
msgid "'%hostname%' is not a valid hostname for email address '%value%'"
msgstr ""

#: EmailAddress.php:61
msgid "'%localPart%' is not a valid local part for email address '%value%'"
msgstr ""

#: EmailAddress.php:59
msgid "'%localPart%' not matched against dot-atom format"
msgstr ""

#: EmailAddress.php:60
msgid "'%localPart%' not matched against quoted-string format"
msgstr ""

#: Hostname.php:74
msgid "'%value%' appears to be a DNS hostname but cannot extract TLD part"
msgstr ""

#: Hostname.php:71
msgid "'%value%' appears to be a DNS hostname but cannot match TLD against known list"
msgstr ""

#: Hostname.php:73
msgid "'%value%' appears to be a DNS hostname but cannot match against hostname schema for TLD '%tld%'"
msgstr ""

#: Hostname.php:72
msgid "'%value%' appears to be a DNS hostname but contains a dash (-) in an invalid position"
msgstr ""

#: Hostname.php:77
msgid "'%value%' appears to be a local network name but local network names are not allowed"
msgstr ""

#: Hostname.php:70
msgid "'%value%' appears to be an IP address, but IP addresses are not allowed"
msgstr ""

#: Digits.php:61
msgid "'%value%' contains not only digit characters"
msgstr ""

#: Float.php:45
msgid "'%value%' does not appear to be a float"
msgstr ""

#: Ip.php:41
msgid "'%value%' does not appear to be a valid IP address"
msgstr ""

#: Date.php:60
msgid "'%value%' does not appear to be a valid date"
msgstr ""

#: Hostname.php:76
msgid "'%value%' does not appear to be a valid local network name"
msgstr ""

#: Int.php:41
msgid "'%value%' does not appear to be an integer"
msgstr ""

#: Date.php:61
msgid "'%value%' does not fit given date format"
msgstr ""

#: Regex.php:45
msgid "'%value%' does not match against pattern '%pattern%'"
msgstr ""

#: Hostname.php:75
msgid "'%value%' does not match the expected structure for a DNS hostname"
msgstr ""

#: Alnum.php:68
msgid "'%value%' has not only alphabetic and digit characters"
msgstr ""

#: Alpha.php:68
msgid "'%value%' has not only alphabetic characters"
msgstr ""

#: Hex.php:49
msgid "'%value%' has not only hexadecimal digit characters"
msgstr ""

#: Alnum.php:69
#: Alpha.php:69
#: Digits.php:62
msgid "'%value%' is an empty string"
msgstr ""

#: StringLength.php:47
msgid "'%value%' is greater than %max% characters long"
msgstr ""

#: StringLength.php:46
msgid "'%value%' is less than %min% characters long"
msgstr ""

#: EmailAddress.php:56
msgid "'%value%' is not a valid email address in the basic format local-part@hostname"
msgstr ""

#: Between.php:54
msgid "'%value%' is not between '%min%' and '%max%', inclusively"
msgstr ""

#: GreaterThan.php:45
msgid "'%value%' is not greater than '%min%'"
msgstr ""

#: LessThan.php:45
msgid "'%value%' is not less than '%max%'"
msgstr ""

#: Date.php:59
msgid "'%value%' is not of the format YYYY-MM-DD"
msgstr ""

#: Between.php:55
msgid "'%value%' is not strictly between '%min%' and '%max%'"
msgstr ""

#: Ccnum.php:61
msgid "'%value%' must contain between 13 and 19 digits"
msgstr ""

#: InArray.php:45
msgid "'%value%' was not found in the haystack"
msgstr ""

#: Ccnum.php:62
msgid "Luhn algorithm (mod-10 checksum) failed on '%value%'"
msgstr ""

#: Identical.php:47
msgid "No token was provided to match against"
msgstr ""

#: Identical.php:46
msgid "Tokens do not match"
msgstr ""

#: NotEmpty.php:45
msgid "Value is empty, but a non-empty value is required"
msgstr ""

この作成したPOファイルのmsgidに対する日本語訳を下記のようにmsgstrに記述していきます。

#: StringLength.php:47
msgid "'%value%' is greater than %max% characters long"
msgstr "%max%文字以下で入力して下さい"

全ての日本語訳が完成したらmsgfmt等を用いてMOファイルを作成します。

msgfmt -o validate.mo validate.po

msgfmtに関しても詳しい事は上記『GNU gettextユーティリティ』にて解説してありますので参考にしてみて下さい。

以上で翻訳データの準備は完了です。後はこの翻訳データを用いてZend Frameworkでのエラーメッセージの日本語化を行います。

まず翻訳データを格納するディレクトリの構造を決める必要があります。
Zend Frameworkでは次の5つの構造を推奨しています。

  • 単一構造のソース
  • 言語ごとに分けた構造
  • アプリケーションごとに分けた構造
  • Gettext 形式の構造
  • ファイル構造のソース

今回アダプターとしてGettextを選びましたので『Gettext 形式の構造』を選ぼうかと思ったのですが”LC_MESSAGES”の存在意義が見いだせなかったので『 言語ごとに分けた構造』にしてみました。

/languages
  /ja
    validate.mo

あとはindex.phpなどでZend_translateオブジェクトを作りZend_Validate_AbstractにDefautlTranslatorとして登録するだけです。

$translate = new Zend_Translate('gettext', '/var/www/html/languages', null, array('scan' => Zend_Translate::LOCALE_DIRECTORY));
Zend_Validate_Abstract::setDefaultTranslator($translate);

注意事項としてZend_Translateの第二引数として渡しているパスは翻訳データを格納しているディレクトリまでの絶対パスで指定して下さい。

以上でZend_Validateのエラーメッセージの日本語化は終了です。
記述した内容でおかしなところ等あればコメント残して頂けると喜びます!!

カテゴリー: Zend Framework タグ:

XREA DOMXPathのquery()で真っ白に

2008 年 5 月 14 日 admin コメントはありません

比較的新しいバージョンのプログラムを使えるのと、サーバー代が安いのでXREAのサーバーをよく使わせてもらっているのですが、そのXREAで今回一つハマってしまったことがありましたので報告です。

Zend FrameworkのZend_Service_Yahooを使用してちょっとしたマッシュアップサイトを作ったのでXREA(s341.xrea.com)に上げて動かそうとしたら、画面が真っ白になって反応なし・・・。当然display_errorsはon。原因を調べてみるとZend_Service_Yahooの_checkErrors()で使用されているDOMXPathのquery()を実行するところでプログラムが止まっていることが分かりました。これ以上は自分の力で原因を追うことが出来ないのでネットで検索。すると次のサイトで同じような状況が報告されていました。

XUGJ「OpenID モジュール」

$doc = new DOMDocument;$doc->loadXML($xml);

$xpath = new DOMXPath($doc);

$result = $xpath->query('/xrds:XRDS');//ここでプロセス落ちる

不思議な物ショップ 「OpenID」

・原因
xrea、CORESERVER.JPのレンタルサーバーで使用されているlibxml2のバージョンに問題があり、xmlファイルの解析に失敗する。
libxml Version 2.6.19の不具合が原因のようです。

さっそくphpinfo()で使用しているサーバーのlibxml2のバージョンを確認してみると

libXML Version 2.6.19

やはりlibxml2のバージョンが原因のようです。

libxml2を使用するプログラムはXREA以外のサーバーで動かす必要がありそうですね。ちなみにZend Framework内をxpath->queryで検索したところ次に列挙されたクラスがヒットしましたので使用の際は注意しておいてください。

  • Zend_Search_Lucene_Document_Html
  • Zend_Service_Amazon(Zend_Service_Amazon_xxx色々)
  • Zend_Service_Flickr(Zend_Service_Flickr_xxx色々)
  • Zend_Service_Simpy(Zend_Service_Simpy_xxx色々)
  • Zend_Service_Technorati(Zend_Service_Technorati_xxx色々)
  • Zend_Service_Yahoo(Zend_Service_Yahoo_xxx色々)
カテゴリー: Linux, Zend Framework タグ:

Zend Framework日本語訳マニュアル

2008 年 3 月 13 日 admin コメントはありません

m-takagiの実験室さんで、Zend Frameworkの日本語訳マニュアルを本家よりはやく見ることが出来ます。有り難い限りです!!

カテゴリー: Zend Framework, その他 タグ:

SAKURA internetでZend Framework(Zend_Db)とMySQLを使う

2008 年 3 月 9 日 admin コメントはありません

去年一杯でPHP4のサポートが終わったことにより、共有ホスティング界でもPHP5化が急速に進んでいるように感じられます。その流れにまかせて私も仕事で使用するメインフレームワークを変更することにしてみました。今まではEthnaをメインにしていたのですが、開発が止まってしまってるみたいですし、クラス間の依存度も高いので、個人的に結構扱いづらいです。そこで趣味で使っていたZend Frameworkをメインにしてみることにしました。このZend Frameworkは他のPHPフレームワークに比べるとオブジェクト指向へのこだわりが強く感じられ拡張が非常にやりやすいように感じます。CakePHPも多少使ってみたのですがZend Frameworkの方に惹かれる私はJava向きなのかもしれないですね。

そのZend FrameworkをSAKURA internetで使用するとZend_DbとMySQLで問題がありましたので解決法を提供します。

Zend_Dbを使用する場合、使用するデータベース用のZend_Db_Adapterを用意する必要があるのですがZend Frameworkに最初から用意されているZend_Db_Adapterは下記に挙げられている物になります。

  • IBM DB2 および Informix Dynamic Server (IDS) (pdo_ibm PHP 拡張モジュールを使用)
  • MySQL (pdo_mysql PHP 拡張モジュールを使用)
  • Microsoft SQL Server (pdo_mssql PHP 拡張モジュールを使用)
  • Oracle (pdo_oci PHP 拡張モジュールを使用)
  • PostgreSQL (pdo_pgsql PHP 拡張モジュールを使用)
  • SQLite (pdo_sqlite PHP 拡張モジュールを使用)
  • MySQL (mysqli を使用します)
  • Oracle (oci8 を使用します)
  • IBM DB2 (ibm_db2 を使用します)
  • Firebird/Interbase (php_interbase を使用します)

そこでMySQLを使用する場合、ドライバとしてpdo_mysqlもしくはmysqliを組み込む必要があるのですが、SAKURA internet組み込まれているMySQLドライバはmysqlのみです。そこで考えられる解決策は下記の3つ。

  1. 使用するデータベースを変更する
  2. pdo_mysqlドライバをインストールする
  3. mysqlドライバ用のZend_Db_Adapterを用意する

1.「使用するデータベースを変更する」はSAKURA internetでMySQL以外のデータベースの選択肢としてはSQLiteのみ。漏洩しても問題ないデータを扱うなら良いのですが共有サーバーでSQLiteはちょっと怖い気がするので無し。

2. 「pdo_mysqlドライバをインストールする」はPHP5を入れ直す必要がありますよね??その辺がまだ良くわかっていない・・・。とにかく面倒な気がするので無し。

そのような訳で私は3.「mysqlドライバ用のZend_Db_Adapterを用意する」を選択。しかしわざわざ自分でmysqlドライバ用のZend_Db_Adapterを作るのも面倒くさいのでネットで探してみたら、やはりありました。

Tutorial : Using Zend Framework Without PDO

このサイトにPDOを使わないZend_Db_Adapterの使い方とライブラリ(現在の最新Php-0.3.0.tar.gz)が置いてあるのでこれを使います。しかしこのサイトの情報が古いので最新のZend Framework(1.5.0 RC1)ではここに書かれているようにしても動きませんので修正が必要です。

まずリンク先のライブラリをダウンロード。

その中にZend_Db_Adapter_Php_Dbphpというクラスがあるのですが、このクラスに下記のようにZend_Db_Profilerをrequierする記述を追加

require_once 'Zend/Db/Profiler.php';

次にZend_Db_Adapter_Php_Mysqlに下記のgetQuoteIdentifierSymbol()を追加

public function getQuoteIdentifierSymbol()
{
	return "`";
}

後はそのダウンロードしてきたライブラリの中にあるPhpフォルダをZend FrameworkのZend_Db_Adapterフォルダの中に突っ込むだけです。

リンク先に「Zend_Dbを用意されている物と置き換えないといけないですよ〜。」的なことが書いてあるのですが現在のZend Frameworkはその必要はありませんので無視です。

後Zend_DB::factory()を使う場合はアダプター名が「PHP_MYSQL」 になるのでご注意を!!

とここまで色々書いて参りましたがPHP5対応させるならpdo_mysqlは入れるだろう、普通・・・。お願いしますよ、SAKURAさん。

カテゴリー: Zend Framework タグ:

Zend_Validate_Alpha 日本語対応

2007 年 8 月 9 日 admin コメント 2 件

Zend_Validate_Alphaを使ってみたら、どうも日本語の判定がおかしい。日本語が完全に無視されているようだ。コードを覗いてみると

public function isValid($value)
{
    $valueString = (string) $value;

    $this->_setValue($valueString);

    if ('' === $valueString) {
      $this->_error(self::STRING_EMPTY);
      return false;
    }

    if (null === self::$_filter) {
      /**
       * @see Zend_Filter_Alpha
       */
      require_once 'Zend/Filter/Alpha.php';
      self::$_filter = new Zend_Filter_Alpha();
    }

    self::$_filter->allowWhiteSpace = $this->allowWhiteSpace;

    if ($valueString !== self::$_filter->filter($valueString)) {
      $this->_error(self::NOT_ALPHA);
      return false;
    }

    return true;
}

どうやらZend_Filter_Alphaでフィルタリングして、入力した値との差を比べることでアルファベットの有無を調べているようだ。ということはZend_Filter_Alphaに問題がありそう。

public function filter($value)
{
    $whiteSpace = $this->allowWhiteSpace ? '\s' : '';
    if (!self::$_unicodeEnabled) {
      // POSIX named classes are not supported, use alternative a-zA-Z match
      $pattern = '/[^a-zA-Z' . $whiteSpace . ']/';
    } else {
      $pattern = '/[^\p{L}' . $whiteSpace . ']/u';
    }

    return preg_replace($pattern, '', (string) $value);
}

あっ!? preg_replaceだ!どうりで日本語無視される訳だ。しかし”$pattern = ‘/[^a-zA-Z]/’”なら問題なさそうの気がするんだけど、何故にユニコード使用の時は”$pattern = ‘/[^\p{L}]/u’”??よく理由はわからんが次のように書き換えてみる。

public function filter($value)
{
    $whiteSpace = $this->allowWhiteSpace ? '\s' : '';
    return preg_replace('/[^a-zA-Z' . $whiteSpace . ']/', '', (string) $value);
}

これでとりあえず日本語判定も問題なく行えるようになったが、 わざわざ’/[^\p{L}]/u’を行う理由がやっぱり分からん。何か意味あるのかなぁ・・・。

カテゴリー: Zend Framework タグ: