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)そのトークンをパスワードとして設定します。
