コンテキストを通じてパスワードを設定する
コンテキストとは多少なじみがない方もいるのではないでしょうか。PHPのストリームでは、fopen()関数でストリームをオープンするときにパラメータを設定できます。モジュールを実装する側としてはコンテキストの方が実装は簡単ですが、利用者にとっては多少敷居が高くなってしまいます。ただし、実際の設定方法は以下のようにそれほど難しいものはありません。
$opt = array( 'pzip' => array('password' => 'abcdefg') // ……(1) ); $context = stream_context_create($opt); // ……(2) $fp = fopen($path,"rb",0,$context); // ……(3)
(1)のように、使用するスキーム名とキー値の2次元配列形式にして値を設定します。次に、(2)stream_context_create()関数でコンテキスト変数を作成します。(3)fopen()する際に、作成したコンテキスト変数を引数に設定します。
続いて、設定した値をモジュール側で取得するコードを紹介します。
zval **tmpzval; // ……(1) if(php_stream_context_get_option(context,"pzip","password",&tmpzval) == SUCCESS){ // ……(2) if(Z_TYPE_PP(tmpzval) == IS_STRING && Z_STRLEN_PP(tmpzval)){ // ……(3) password = estrdup(Z_STRVAL_PP(tmpzval)); // ……(4) } }
コンテキストに設定した文字列はPHP側から設定されてきますので、つまり、モジュール側ではzval形式の変数になります。従って、(1)のようにzval変数を用意します。(2)でコンテキストからスキーム名とキー値を指定して用意したzval変数を通じて値を取得します。(3)ではそのzval変数が文字列型であることを確認し、(4)でchar*型に変換して値を設定しています。このように、URLをパースする労力に比べると遙かに短いコードですませることができます。
ストリームラッパーとして登録する
続いて、作成したphp_stream_pzip_opner関数をストリームラッパーとして登録するための(1)構造体php_stream_wrapper_opsを用意します。この構造体は、php_stream_opsと非常に似ていますが、unlinkや、renameなどもあるように開いたストリームを扱うのではなくURL文字列によってリソース内のデータ操作を扱うためのものです。この構造体には、(2)先ほど作成したストリームをオープンするための関数を登録します。次に、その構造体をさらに(3)php_stream_wrapper構造体を用意し、(4)のように設定します。
static php_stream_wrapper_ops pzip_stream_wops = { // ……(1) php_stream_pzip_opener, // ……(2) NULL, /* close */ NULL, /* fstat */ NULL, /* stat */ NULL, /* opendir */ "pzip wrapper", NULL, /* unlink */ NULL, /* rename */ NULL, /* mkdir */ NULL, /* rmdir */ }; php_stream_wrapper php_stream_pzip_wrapper = { // ……(3) &pzip_stream_wops, // ……(4) NULL, 0 /* is_url */ };
これで必要な関数や変数がすべてそろいましたので、モジュールとしてPHP側に識別できるようにします。これまで、関数やクラスを登録する際には、(1)PHP_MINIT_FUNCTION関数内で指定していましたが、ストリームラッパーも同様に(2)php_register_url_stream_wrapper()で設定します。
PHP_MINIT_FUNCTION(pzip) // ……(1) { : (省略) php_register_url_stream_wrapper("pzip", &php_stream_pzip_wrapper TSRMLS_CC); // ……(2) return SUCCESS; }
PHPでstream_get_wrappers()やphpinfo()を実行し、利用できるストリームとして登録した"pzip"という文字列が含まれていれば、正常に登録できたことを確認できます。
最後に
今回は、PHPでのストリームの実装処理がどのようにできているかを実際にZipファイルの読み込みを通じて紹介しました。ストリームはPHPだけでも作成できる機能ですが、さまざまなリソースにアクセスするには通常はネイティブのライブラリが用意されており、それらを操作するクラスや関数を作成していたものに対して、多少の拡張で実装が可能です。
PHPでは非常に多くの関数が用意されています。また、その拡張もこれまで紹介してきたとおりできますが、そのアクセス方法は実装者に依存してしまったり、詳細なアクセス方法を提供するために結果的に難解になってしまう場合があります。そのような場合の利用方法として、ストリームラッパーなどを用意すると、新たな関数を覚える必要がなくデータの操作が可能です。また、場合によってはPHPのプログラムを変更することなく実装できるケースもあります。
このように、利用者のレベルや目的に応じたアクセス方法の提供ができます。