ストリームへのアクセス
PHP ユーザー空間でのすべてのファイル I/O 処理は、PHP 4.3 で導入された PHP ストリーム ラッパー層を通じて内部的に処理されます。拡張コードはオプションで stdio または posix ファイルを使用できます。ローカル ファイル システムまたはバークレー ドメイン ソケットを使用するか、ユーザー空間ストリーム I/O として同じ API を呼び出すことができます
ストリームの概要
通常、直接ファイル記述子は、ただし、これにより、特定のプロトコルを実装するすべての作業が拡張機能の開発者に課せられ、拡張機能のコードを透過的にすることができます。HTTP などのさまざまな組み込みストリーム ラッパーを使用します。 FTP、およびその SSL 対応物、および gzip および bzip2 圧縮ラッパーを含めることにより、コードは SSH2、WebDav、さらには Gopher などの他のプロトコルにもアクセスできます。ストリームに基づいて内部的に動作する基本的な API については、後の第 16 章「興味深いストリーム」で説明します。フィルターの適用、コンテキスト オプションやパラメーターの使用などについて説明します。 概要: ストリームを開く
これは統合された API ですが、実際には、必要なストリームのタイプに応じて、ストリームを開くための 4 つの異なるパスがあります。ユーザー空間の観点から見ると、4 つの異なるカテゴリは次のとおりです (関数リストは例のみを示しており、そうではありません)。完全なリスト):<?php
/* fopen包装
* 操作文件/URI方式指定远程文件类资源 */
$fp = fopen($url, $mode);
$data = file_get_contents($url);
file_put_contents($url, $data);
$lines = file($url);
/* 传输
* 基于套接字的顺序I/O */
$fp = fsockopen($host, $port);
$fp = stream_socket_client($uri);
$fp = stream_socket_server($uri, $options);
/* 目录流 */
$dir = opendir($url);
$files = scandir($url);
$obj = dir($url);
/* "特殊"的流 */
$fp = tmpfile();
$fp = popen($cmd);
proc_open($cmd, $pipes);
?>
どのタイプのストリームを開いたとしても、それらはすべて共通構造 php_stream に保存されます
fopen ラッパー
まず fopen() を実装することから始めます。これで、拡張機能スケルトンの作成に慣れてきたはずです。そうでない場合は、第 5 章「最初の拡張機能」に戻ってください。「復習のために、実装した fopen() 関数を以下に示します。 PHP_FUNCTION(sample5_fopen)
{
php_stream *stream;
char *path, *mode;
int path_len, mode_len;
int options = ENFORCE_SAFE_MODE | REPORT_ERRORS;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
&path, &path_len, &mode, &mode_len) == FAILURE) {
return;
}
stream = php_stream_open_wrapper(path, mode, options, NULL);
if (!stream) {
RETURN_FALSE;
}
php_stream_to_zval(stream, return_value);
}
php_stream_open_wrapper() 」最下層を完全にバイパスするように設計されています。パスは読み書きされるファイル名または URL を指定し、読み書きの動作は
オプションのタグ付き値のコレクションです。ここでは、以下に紹介する一連の固定値に設定されます:
USE_PATH
php.ini ファイルの include_path を上記の組み込み関数 fopen() の相対パスに適用します。 3 番目のパラメーターが TRUE
として指定されている場合、このオプションが設定されます。
STREAM_USE_URL このオプションを設定した後は、
php:// に対してのみリモート URL が開きます。 、file://、zlib://、bzip2:// これらの URL ラッパーは、リモート URL とはみなしません
。
ENFORCE_SAFE_MODE この定数はこのように名前が付けられていますが、実際にはこのオプションを設定します
は、セーフ モード (php.ini ファイルの
safe_mode ディレクティブ) の必須チェックです。この
オプションが設定されていない場合、safe_mode チェックが行われます。 (INI 設定でのセーフモードの設定に関係なく
)
REPORT_ERRORSこのオプションが設定されている場合、指定されたリソースを開くときにエラーが発生した場合
エラー レポートが生成されます
STREAM_MUST_SEEKソケットなどの一部のストリームでは、シークすることはできません (ランダム
アクセスは特定の環境下でのみ可能です)。状況
seek。呼び出し元のスコープがこのオプションを指定し、ラッパー
がシーク可能であることが保証されていないことを検出した場合、ストリームを開くことを拒否します。 if 呼び出し元のスコープで、ストリームを stdio または
posix ファイル記述子に変換できることが必要な場合、I/O 操作が確実に失敗するように、このオプションを open_wrapper 関数
に渡す必要があります。それが発生する前に
STREAM_ONLY_GET_HEADERS フラグはストリームからメタデータを要求するだけで済みます。実際、これは http_response_headers グローバル変数
を取得するために
http ラッパーで使用されます。 実際にはなしリモート ファイル Content.
STREAM_DISABLE_OPEN_BASEDIR を取得しますsafe_mode チェックと同様に、このオプションが設定されていない場合はチェックされます
INI 設定 open_basedir、このオプションを指定すると、このデフォルトのチェックをバイパスできます
STREAM_OPEN_PERSISTENT は、内部的に割り当てられたすべてのスペースが永続的に割り当てられ、関連するリソースが永続リストに登録されることをストリーム パッケージング レイヤーに伝えます
IGNORE_PATH。それから検索してくださいほとんどの URL
ラッパーは、このオプションを無視します。 IGNORE_URL提供这个选项时, 流包装层只打开本地文件. 所 有的is_url包装器都将被忽略. 最后的NULL参数是char **类型, 它最初是用来设置匹配路径, 如果path指向普通文件URL, 则去掉file://部分, 保留直接的文件路径用于传统的文件名操作. 这个参数仅仅是以前引擎内部处理使用的. 此外, 还有php_stream_open_wrapper()的一个扩展版本: 最后一个参数context允许附加的控制, 并可以得到包装器内的通知. 你将在第16章看到这个参数的细节. 传输层包装 尽管传输流和fopen包装流是相同的组件组成的, 但它的注册策略和其他的流不同. 从某种程度上来说, 这是因为用户空间对它们的访问方式的不同造成的, 它们需要实现基于套接字的其他因子. 从扩展开发者角度来看, 打开传输流的过程是相同的. 下面是对fsockopen()的实现: 这个函数的基础构造和前面的fopen示例是一样的. 不同在于host和端口号使用不同的参数指定, 接着为了给出一个传输流URL就必须将它们合并到一起. 在产生了一个有意义的"路径:后, 将它传递给php_stream_xport_create()函数, 方式和fopen()使用的php_stream_open_wrapper()API一样. php_stream_xport_create()的原型如下: 每个参数的含义如下: xport基于URI的传输描述符. 对于基于inet的套接字流, 它可以是 tcp://127.0.0.1:80, udp://10.0.0.1:53, ssl://169.254.13.24:445等. 此 外, UNIX域传输协议unix:///path/to/socket, udg:///path/to/dgramsocket等都是合法的. xport_len指定了xport的长 度, 因此xport是二进制安全的. options这个值是由前面php_stream_open_wrapper()中介绍的选项通过按位 或组成的值. flags由STREAM_XPORT_CLIENT或STREAM_XPORT_SERVER之一 与下面另外一张表中将列出的STREAM_XPORT_*常量通过按位或 组合得到的值. persistent_id 要求されたトランスポート ストリームをリクエスト間で永続化する必要がある場合、呼び出し元のスコープは、接続を説明する キー名を指定して、非永続的な接続を作成します。 のみ の文字列値は、最初に永続性プールから既存のトランスポート ストリームを検索しようとするか、見つからない場合は新しい永続性ストリームを作成します。 timeout 試行する時間。この値を NULL として渡すと、 は php.ini で指定されたデフォルトのタイムアウト値を使用します。選択したソケットの作成、接続、バインド、またはリッスン中にエラーが発生した場合、ここで渡される char * 参照値 は、最初はエラーの理由を説明する文字列 に設定されます。 は NULL を指す必要があります。返すときに値が設定されている場合、呼び出し元のスコープはこの文字列に関連するメモリを解放する責任があります。 errstr を通じて返されたエラー メッセージに対応する数値エラー コード。 php_stream_xport_create() の flags パラメーターで使用される定数の STREAM_XPORT_* ファミリーは次のように定義されます: ローカル エンドはトランスポート層とリモート リソースを渡します。これにより接続が確立されます。タグは通常、STREAM_XPORT_CONNECT または STREAM_XPORT_CONNECT_ASYNC と組み合わせて使用されます。通常、ローカル側はトランスポート層を介して接続を受け入れます。 が使用されますSTREAM_XPORT_BIND および STREAM_XPORT_LISTEN は、リモート リソース接続の確立がトランスポート ストリーム作成の一部であることを示すために使用されます 。 ポイントは省略しても合法です。このフラグはクライアントのトランスポート ストリームを作成するときに使用しますが、そのためには php_stream_xport_connect() を手動で呼び出す必要があります。 STREAM_XPORT_CONNECT_ASYNC尝试连接到远程资源, 但不阻塞. STREAM_XPORT_BIND将传输流绑定到本地资源. 用在服务端传输流时, 这将使得accept连接的传输流准备端口, 路径或 特定的端点标识符等信息. STREAM_XPORT_LISTEN在已绑定的传输流端点上监听到来的连接. 这通 常用于基于流的传输协议, 比如: tcp://, ssl://, unix://. 目录访问 fopen包装器支持目录访问, 比如file://和ftp://, 还有第三种流打开函数也可以用于目录访问, 下面是对opendir()的实现: 同样的, 也可以为某个特定目录打开一个流, 比如本地文件系统的目录名或支持目录访问的URL格式资源. 这里我们又看到了options参数, 它和原来的含义一样, 第三个参数NULL原型是php_stream_context类型. 在目录流打开后, 和文件以及传输流一样, 返回给用户空间. 特殊流 还有一些特殊类型的流不能归类到fopen/transport/directory中. 它们中每一个都有自己独有的API: 创建一个可seek的缓冲区流用于读写. 在关闭时, 这个流使用的所有临时资源, 包括所有的缓冲区(无论是在内存还是磁盘), 都将被释放. 使用这一组API中的后一个函数, 允许临时文件被以特定的格式命名放到指定路径. 这些内部API调用被用户空间的tmpfile()函数隐藏. 这3个API方法接受已经打开的FILE *资源或文件描述符ID, 使用流API的某种操作包装. fd格式的接口不会搜索匹配你前面看到过的fopen函数打开的资源, 但是它会注册持久化的资源, 后续的fopen可以使用到这个持久化资源. 访问流 在你打开一个流之后, 就可以在它上面执行I/O操作了. 使用那种协议包装API创建了流并不重要, 它们都使用相同的访问API. 读 流的读写可以使用下面的API函数组合完成, 它们多数都是遵循POSIX I/O中对应的API规范的: 从数据流中接收一个字符. 如果流上再没有数据, 则返回EOF. 从指定流中读取指定字节的数据. buf必须预分配至少count字节的内存空间. 这个函数将返回从数据流实际读到缓冲区中的数据字节数. php_stream_read()不同于其他的流读取函数. 如果使用的流不是普通文件流, 哪怕数据流中有超过请求字节数的数据, 并且当前也可以返回, 它也只会调用过一次底层流实现的read函数. 这是为了兼容基于包(比如UDP)的协议的这种做法. 这两个函数从stream中读取最多maxlen个字符, 直到碰到换行符或流结束. buf可以是一个指向预分配的至少maxlen字节的内存空间的指针, 也可以是NULL, 当它是NULL时, 则会自动的创建一个动态大小的缓冲区, 用从流中实际读出的数据填充, 成功后函数返回指向缓冲区的指针, 失败则返回NULL. 如果returned_len传递了非NULL值, 则在返回时它将被设置为实际从流中读取的字节数. 和php_stream_get_line()类似, 这个函数将读取最多maxlen, 或到达EOF/行结束第一次出现的位置. 但是它也有和php_stream_get_line()的不同指出, 这个函数允许指定任意的停止读取标记. 读取目录项 从php流中读取目录项和上面从普通文件中读取普通数据相同. 这些数据放到了固定大小的dirents块中. 内部的php_stream_dirent结构体如下, 它与POSIX定义的dirent结构体一致: 实际上你可以直接使用php_stream_read()函数读取数据到这个结构体中: 由于从目录流中读取是很常见的操作, php流包装层暴露了一个API, 它将记录大小的检查和类型转换处理封装到了一次调用中: 如果成功读取到目录项, 则传入的entry指针将被返回, 否则返回NULL标识错误. 使用这个为目录流特殊构建的函数而不是直接从目录流读取非常重要, 这样做未来流API改变时就不至于和你的代码冲突. 写 和读类似, 向流中写数据只需要传递一个缓冲区和缓冲区长度给流. write_string的版本实际上是一个提供便利的宏, 它允许写一个NULL终止的字符串,而不用显式的提供长度. 返回的是实际写到流中的字节数. 要特别小心的是尝试写大数据的时候可能导致流阻塞, 比如套接字流, 而如果流被标记为非阻塞, 则实际写入的数据量可能会小于传递给函数的期望大小. 还有一种选择是, 使用php_stream_putc()和php_stream_puts()写入一个字符或一个字符串到流中. 要注意, php_stream_puts()不同于php_stream_write_string(), 虽然它们的原型看起来是一样的, 但是php_stream_puts()会在写出buf中的数据后自动的追加一个换行符. 功能和格式上都类似于fprintf(), 这个API调用允许在写的同时构造字符串而不用去创建临时缓冲区构造数据. 这里我们能够看到的一个明显的不同是它需要TSRMLS_CC宏来保证线程安全. 随机访问, 查看文件偏移量以及缓存的flush 基于文件的流, 以及另外几种流是可以随机访问的. 也就是说, 在流的一个位置读取了一些数据之后, 文件指针可以向前或向后移动, 以非线性顺序读取其他部分. 如果你的流应用代码预测到底层的流支持随机访问, 在打开的时候就应该传递STREAM_MUST_SEEK选项. 对于那些原本就可随机访问的流来说, 这通常不会有什么影响, 因为流本身就是可随机访问的. 而对于那些原本不可随机访问的流, 比如网络I/O或线性访问文件比如FIFO管道, 这个暗示可以让调用程序有机会在流的数据被消耗掉之前, 优雅的失败. 在可随机访问的流资源上工作时, 下面的函数可用来将文件指针移动到任意位置: offset是相对于whence表示的流位置的偏移字节数, whence的可选值及含义如下: SEEK_SEToffset相对于文件开始位置. php_stream_rewind()API调用实际上是一个宏, 展开后是php_stream_seek(stream, 0, SEEK_SET), 表示移动到文件开始 位置偏移0字节处. 当使用SEEK_SET时, 如果offset传递负值被认为是错误 的, 将会导致未定义行为. 指定的位置超过流的末尾也是未定义的, 不过结果 通常是一个错误或文件被扩大以满足指定的偏移量. SEEK_CURoffset相对于文件的当前偏移量. 调用php_stream_seek(steram, offset, SEEK_CUR)一般来说等价于php_stream_seek(stream, php_stream_tell() + offset, SEEK_SET); SEEK_ENDoffset是相对于当前的EOF位置的. 负值的offset表示在EOF之前的位置, 正 值和SEEK_SET中描述的是相同的语义, 可能在某些流实现上可以工作. 在目录流上随机访问时, 只有php_stream_rewinddir()函数可用. 使用php_stream_seek()函数将导致未定义行为. 所有的随机访问一族函数返回0标识成功或者-1标识失败. 如你之前所见, php_stream_tell()将返回当前的文件偏移量. 调用flush()函数将强制将流过滤器此类内部缓冲区中的数据输出到最终的资源中. 在流被关闭时, flush()函数将自动调用, 并且大多数无过滤流资源虽然不进行任何内部缓冲, 但也需要flush. 显式的调用这个函数很少见, 并且通常也是不需要的. 调用php_stream_stat()可以获取到流实例的其他信息, 它的行为类似于fstat()函数. 实际上, php_stream_statbuf结构体现在仅包含一个元素: struct statbuf sb; 因此, php_stream_stat()调用可以如下面例子一样, 直接用传统的fstat()操作替代, 它只是将posix的stat操作翻译成流兼容的: 关闭 所有流的关闭都是通过php_stream_free()函数处理的, 它的原型如下: 这个函数中的options参数允许的值是PHP_STREAM_FREE_xxx一族常量的按位或的结果, 这一族常量定义如下(下面省略PHP_STREAM_FREE_前缀): CALL_DTOR流实现的析构器应该被调用. 这里提供了一个时机对特定的流 进行显式释放. RELEASE_STREAM释放为php_stream结构体分配的内存 PRESERVE_HANDLE指示流的析构器不要关闭它的底层描述符句柄 RSRC_DTOR流包装层内部管理资源列表的垃圾回收 PERSISTENT作用在持久化流上时, 它的行为将是永久的而不局限于当前请 求. CLOSECALL_DTOR和RELEASE_STREAM的联合. 这是关闭 非持久化流的一般选项. CLOSE_CASTEDCLOSE和PRESERVE_HANDLE的联合. CLOSE_PERSISTENTCLOSE和PERSISTENT的联合. 这是永久关闭持久化流的一 般选项. 实际上, 你并不需要直接调用php_stream_free()函数. 而是在关闭流时使用下面两个宏的某个替代: 通过zval交换流 因为流通常映射到zval上, 反之亦然, 因此提供了一组宏用来简化操作, 并统一编码(格式): 要注意, 这里并没有调用ZEND_REGISTER_RESOURCE(). 这是因为当流打开的时候, 已经自动的注册为资源了, 这样就可以利用到引擎内建的垃圾回收和shutdown系统的优点. 使用这个宏而不是尝试手动的将流注册为新的资源ID是非常重要的; 这样做的最终结果是导致流被关闭两次以及引擎崩溃. 从传入的zval *中取回php_stream *有一个类似的宏. 可以看出, 这个宏只是对资源获取函数(第9章"资源数据类型")的一个简单封装. 请回顾ZEND_FETCH_RESOURCE2()宏, 第一个宏php_stream_from_zval()就是对它的包装, 如果资源类型不匹配, 它将抛出一个警告并尝试从函数实现中返回. 如果你只是想从传入的zval *中获取一个php_stream *, 而不希望有自动的错误处理, 就需要使用php_stream_from_zval_no_verify()并且需要手动的检查结果值.
静态资源操作 一个基于流的原子操作并不需要实际的实例. 下面这些API仅仅使用URL执行这样的操作: 和前面的php_stream_stat()类似, 这个函数提供了一个对POSIX的stat()函数协议依赖的包装. 要注意, 并不是所有的协议都支持URL记法, 并且即便支持也可能不能报告出statbuf结构体中的所有成员值. 一定要检查php_stream_stat_path()失败时的返回值, 0标识成功, 要知道, 不支持的元素返回时其值将是默认的0. 这个php_stream_url_stat()的扩展版本允许传递另外两个参数. 第一个是flags, 它的值可以是下面的PHP_STERAM_URL_STAT_*(下面省略PHP_STREAM_URL_STAT_前缀)一族常量的按位或的结果. 还有一个是context参数, 它在其他的一些流函数中也有出现, 我们将在第16章去详细学习. LINK原始的php_stream_stat_path()对于符号链接或目录将会进行解析直到碰到 协议定义的结束资源. 传递PHP_STREAM_URL_STAT_LINK标记将导致 php_stream_stat_path()返回请求资源的信息而不会进行符号链接的解析. (译注: 我们可以这样理解, 没有这个标记, 底层使用stat(), 如果有这个标记, 底层使用lstat(), 关于stat()和lstat()的区别, 请查看*nix手册) QUIET默认情况下, 如果在执行URL的stat操作过程中碰到错误, 包括文件未找到错 误, 都将通过php的错误处理机制触发. 传递QUIET标记可以使得 php_stream_stat_path()返回而不报告错误. 创建和删除目录也会如你期望的工作. 这里的options参数和前面的php_stream_open_wrapper()函数的同名参数含义一致. 对于php_stream_mkdir(), 还有一个参数mode用于指定一个八进制的值表明读写执行权限. 小结 本章中你接触了一些基于流的I/O的内部表象. 下一章将演示做呢样实现自己的协议包装, 甚至是定义自己的流类型. 以上就是[翻译][php扩展开发和嵌入式]第14章-php中流的访问的内容,更多相关内容请关注PHP中文网(www.php.cn)!php_stream *php_stream_open_wrapper_ex(char *path,
char *mode, int options, char **opened_path,
php_stream_context *context);
PHP_FUNCTION(sample5_fsockopen)
{
php_stream *stream;
char *host, *transport, *errstr = NULL;
int host_len, transport_len, implicit_tcp = 1, errcode = 0;
long port = 0;
int options = ENFORCE_SAFE_MODE;
int flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l",
&host, &host_len, &port) == FAILURE) {
return;
}
if (port) {
int implicit_tcp = 1;
if (strstr(host, "://")) {
/* A protocol was specified,
* no need to fall back on tcp:// */
implicit_tcp = 0;
}
transport_len = spprintf(&transport, 0, "%s%s:%d",
implicit_tcp ? "tcp://" : "", host, port);
} else {
/* When port isn't specified
* we can safely assume that a protocol was
* (e.g. unix:// or udg://) */
transport = host;
transport_len = host_len;
}
stream = php_stream_xport_create(transport, transport_len,
options, flags,
NULL, NULL, NULL, &errstr, &errcode);
if (transport != host) {
efree(transport);
}
if (errstr) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%d] %s",
errcode, errstr);
efree(errstr);
}
if (!stream) {
RETURN_FALSE;
}
php_stream_to_zval(stream, return_value);
}
php_stream *php_stream_xport_create(char *xport, int xport_len,
int options, int flags,
const char *persistent_id,
struct timeval *timeout,
php_stream_context *context,
char **errstr, int *errcode);
PHP_FUNCTION(sample5_opendir)
{
php_stream *stream;
char *path;
int path_len, options = ENFORCE_SAFE_MODE | REPORT_ERRORS;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
&path, &path_len) == FAILURE) {
return;
}
stream = php_stream_opendir(path, options, NULL);
if (!stream) {
RETURN_FALSE;
}
php_stream_to_zval(stream, return_value);
}
php_stream *php_stream_fopen_tmpfile(void);
php_stream *php_stream_fopen_temporary_file(const char *dir,
const char *pfx, char **opened_path);
php_stream *php_stream_fopen_from_fd(int fd,
const char *mode, const char *persistent_id);
php_stream *php_stream_fopen_from_file(FILE *file,
const char *mode);
php_stream *php_stream_fopen_from_pipe(FILE *file,
const char *mode);
int php_stream_getc(php_stream *stream);
size_t php_stream_read(php_stream *stream, char *buf, size_t count);
char *php_stream_get_line(php_stream *stream, char *buf,
size_t maxlen, size_t *returned_len);
char *php_stream_gets(php_stream *stream, char *buf,
size_t maxlen);
char *php_stream_get_record(php_stream *stream,
size_t maxlen, size_t *returned_len,
char *delim, size_t delim_len
TSRMLS_DC);
typedef struct _php_stream_dirent {
char d_name[MAXPATHLEN];
} php_stream_dirent;
{
struct dirent entry;
if (php_stream_read(stream, (char*)&entry, sizeof(entry))
== sizeof(entry)) {
/* 成功从目录流中读取到一项 */
php_printf("File: %s\n", entry.d_name);
}
}
php_stream_dirent *php_stream_readdir(php_stream *dirstream,
php_stream_dirent *entry);
size_t php_stream_write(php_stream *stream, char *buf,
size_t count);
size_t php_stream_write_string(php_stream *stream, char *stf);
int php_stream_putc(php_stream *stream, int c);
int php_stream_puts(php_string *stream, char *buf);
size_t php_stream_printf(php_stream *stream TSRMLS_DC,
const char *format, ...);
int php_stream_seek(php_stream *stream, off_t offset, int whence);
int php_stream_rewind(php_stream *stream);
int php_stream_rewinddir(php_stream *dirstream);
off_t php_stream_tell(php_stream *stream);
int php_stream_flush(php_stream *stream);
int php_stream_stat(php_stream *stream, php_stream_statbuf *ssb);
int php_sample4_fd_is_fifo(int fd)
{
struct statbuf sb;
fstat(fd, &sb);
return S_ISFIFO(sb.st_mode);
}
int php_sample4_stream_is_fifo(php_stream *stream)
{
php_stream_statbuf ssb;
php_stream_stat(stream, &ssb);
return S_ISFIFO(ssb.sb.st_mode);
}
int php_stream_free(php_stream *stream, int options);
#define php_stream_close(stream) \
php_stream_free((stream), PHP_STREAM_FREE_CLOSE)
#define php_stream_pclose(stream) \
php_stream_free((stream), PHP_STREAM_FREE_CLOSE_PERSISTENT)
#define php_stream_to_zval(stream, pzval) \
ZVAL_RESOURCE((pzval), (stream)->rsrc_id);
#define php_stream_from_zval(stream, ppzval) \
ZEND_FETCH_RESOURCE2((stream), php_stream*, (ppzval), \
-1, "stream", php_file_le_stream(), php_file_le_pstream())
#define php_stream_from_zval_no_verify(stream, ppzval) \
(stream) = (php_stream*)zend_fetch_resource((ppzval) \
TSRMLS_CC, -1, "stream", NULL, 2, \
php_file_le_stream(), php_file_le_pstream())
int php_stream_stat_path(char *path, php_stream_statbuf *ssb);
int php_stream_stat_path_ex(char *path, int flags,
php_stream_statbuf *ssb, php_stream_context *context);
int php_stream_mkdir(char *path, int mode, int options,
php_stream_context *context);
int php_stream_rmdir(char *path, int options,
php_stream_context *context);