リソースとしてのストリーム

すべてのストリームは、作成されるとリソースとして登録されます。これにより、 たとえ致命的なエラーが発生したとしても適切な後処理が行われることが保障されます。 PHP のすべてのファイルシステム関数は、ストリームリソースに対して操作することができます。 つまり、あなたの作成した拡張モジュールは、 通常の PHP ファイルポインタをパラメータとして受け取って 結果をストリームで返すことができるということです。 ストリーム API により、この処理が楽にできるようになっています。

例 44-2. ストリームをパラメータとして受け取る方法

PHP_FUNCTION(example_write_hello)
{
    zval *zstream;
    php_stream *stream;
    
    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstream))
        return;
    
    php_stream_from_zval(stream, &zstream);

    /* これでストリームを使うことができます。しかし、ストリームの
       "所有者" はこの関数ではなく、呼び出し元のスクリプトです。
       つまり、この関数内でストリームを閉じてはいけないということです。
       そんなことをすると PHP がクラッシュしてしまいます! */

    php_stream_write(stream, "hello\n");
        
    RETURN_TRUE();
}

例 44-3. 関数からストリームを返す方法

PHP_FUNCTION(example_open_php_home_page)
{
    php_stream *stream;
    
    stream = php_stream_open_wrapper("http://www.php.net", "rb", REPORT_ERRORS, NULL);
    
    php_stream_to_zval(stream, return_value);

    /* これ以降、ストリームの "所有者" は呼び出し元スクリプトとなります。
       もしここでストリームを閉じると、PHP がクラッシュしてしまいます! */
}

ストリームの後始末が自動的に行われることから、 わざわざ後始末を気にしたりしないずさんなプログラマでいても大丈夫と思われるかもしれません。 確かにそれでもうまくいくでしょうが、いくつかの理由からこれはお勧めできません。 ストリームのオープン中はシステムリソースがロックされるので、 使用済みのファイルをオープンしたままにしておくと、 他のプロセスがファイルにアクセスできなくなります。 大量のファイルを扱うスクリプトでは、 使用済みのリソースを溜め込み続けるとメモリやファイル記述子の番号がいっぱいになってしまいます。 その結果ウェブサーバがリクエストを受け付けられないようになります。 どうです? あまりいい話ではないでしょう? ストリーム API には、すっきりとしたコードが書けるような細工が組み込まれています。 ストリームを閉じるべき場所で閉じていない場合は、 ウェブサーバのエラーログに有用なデバッグ情報が出力されます。

注意: 拡張モジュールの開発中は、常にデバッグビルド版の PHP を使用するようにしましょう (configure の際に --enable-debug を指定します)。 そうすることで、メモリリークやストリームのリークに関する重要な警告を受け取れるようになります。

時には、リクエストを持続させるためにストリームをオープンし続けることが有用なこともあるでしょう。 例えばログを記録したり結果をファイルにトレースする場合などです。 このようなストリームについて、確実に後始末を行うコードを書くことはさほど難しくありません。 しかしその数行のコードがどうしても必要なのかというと、そうではないでしょう。 このような場合にコードを書く手間を省くため、ストリームに対して 「このストリームの後始末は自動処理にまかせる」という印をつけることができます。 こうすると、ストリームの後始末が自動的に行われた際に、 ストリーム API は何の警告も発しなくなります。この印をつけるには php_stream_auto_cleanup() を使用します。