URL記述からのPHPストリームの作成
続いて、URL風の記述を用いてのストリーム作成の方法を紹介します。おおよその流れはメソッドからのストリームの作成と同様ですが、これまではストリームをオープンするところからでした。しかし、URL記述からですので多少異なり、以下のような流れになります。
-
ストリームの状態を管理する構造体を用意する
(メソッドから取得するストリームで用意したものを再利用可能)。 -
ストリームの読み込み(read)、書き込み(write)、クローズ(close)などを行うための関数を用意する
(メソッドから取得するストリームで作成したものを再利用可能)。 -
2で作成した関数をハンドラとして登録する構造体を用意する
(メソッドからの取得するストリームで作成したものを再利用可能)。 - 3で作成した構造体を用いてファイルをオープンする関数を作成する。
- 4で作成した関数をハンドラとして登録する構造体を用意する。
- モジュールとして新たなストリームプロトコルとして利用できるように、5で作成した構造体をモジュールの初期化処理で登録する。
1から3までは今まで作成したものをほぼそのまま再利用可能で、その流れも変わりません。4はメソッドからの取得の場合には、PZipFile::getStream()として実装していた部分ですが、今回はURLスキームをパースして実際のリソースをオープンする処理を新たに記述する必要があります。5、6はPHP全体に対してそのURLスキームを用いたときに、指定した関数が動作するようにするための処理になります。
ストリームをオープンする
4でのストリームをオープンする処理の実装を行っていきます。実際のコードを記述するには、ほぼ同様の処理を行っているPHPソース内のext/zip/zip_stream.cのphp_stream_zip_opener関数を参考にし、新たに関数を以下のように作成します。ただし、今回は暗号付きZipファイルの解凍を行いますので、パスワードを設定する必要があります。そのパスワードを設定する方法には2つの方法があります。
- URL文字列にパスワードを設定する方法
- コンテキストを通じてパスワードを設定する方法
php_stream *php_stream_pzip_opener(php_stream_wrapper *wrapper, char *path, // ……(1) char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) // ……(2) { : // (省略) }
(1)のpath変数は、URLの文字列が設定される引数で、(2)context変数はストリームのコンテキストが設定される引数です。
URL文字列をパースする
PHPから以下のような文字列としてURLが設定される場合を想定します。
$fp = fopen("pzip:///home/guest/file.zip?password=abcedefg#file1.txt","r");
URL文字列をパースしてパスワードを設定する方法の場合、あまりPHPモジュール固有の知識は必要なく実装できますが、ここでは、PHPのモジュール開発時に利用できる関数を使用してパースを行います。
php_url *url = NULL; // ……(1) char *tmppath; if(strncasecmp("pzip:///",path,8) == 0){ // ……(2) int count , new_len = 0; tmppath = php_str_to_str_ex( // ……(3) path,strlen(path),"pzip:///", strlen("pzip:///"), "pzip://localhost/",strlen("pzip://localhost/"), &new_len,0,&count); url = php_url_parse(tmppath); // ……(4) efree(tmppath); } : // (省略) if(url->query){ // ……(5) char *saveptr; char *token; token = php_strtok_r(url->query,"?",&saveptr); // ……(6) while(1){ if(token == NULL){ break; } else{ if(strncasecmp("password=",token,9) == 0){ // ……(7) token += 9; password = estrdup(token); // ……(8) } token = php_strtok_r(NULL,"?",&saveptr); } } } : // (省略)
(1)php_urlの構造体を用意します。この構造体はPHPの関数であるparse_url()関数を実行した時の結果とほぼ同様の構造です。(2)でURLの文字列が"pzip:///"で開始されているかをチェックします。残念ながら(4)で行うphp_url_parse()関数はscheme://hostのように必ずhostがあることを想定しており、処理が失敗してしまいます。そこで、(3)php_str_to_str_ex()では、"pzip:///"という文字列を"pzip://localhost/"という文字列に置換します。このように少々分かりにくい関数名ではありますが、PHPでのstr_replace相当の関数も用意されています。これらの関数はマニュアルがそろっている訳ではないので、根気よくほかのモジュールで使われている関数を探す必要があります。
これでURLとしてパースできる文字列が完成しましたので、(4)php_url_parse()で文字列をパースしてphp_url構造体として結果を取得します。クエリー文字列は(5)url->queryとして取得できますので、それを(6)php_strtok_r()を用いて"?"で区切られるトークンとして処理をしていきます。(7)では文字列が"password="で始まっている場合には、(8)そのトークンをパスワードとして設定します。