Welcome 微信登录

首页 / 网页编程 / PHP / php中有趣的流

php中有趣的流2013-09-09有趣的流

php常被提起的一个特性是流上下文. 这个可选的参数甚至在用户空间大多数流创建相关的函数中都可用, 它作为一个泛化的框架用于向给定包装器或流实现传入/传出额外的信息.

上下文

每个流的上下文包含两种内部消息类型. 首先最常用的是上下文选项. 这些值被安排在上下文中一个二维数组中, 通常用于改变流包装器的初始化行为. 还有一种则是上下文参数, 它对于包装器是未知的, 当前提供了一种方式用于在流包装层内部的事件通知.

php_stream_context *php_stream_context_alloc(void);  

通过这个API调用可以创建一个上下文, 它将分配一些存储空间并初始化用于保存上下文选项和参数的HashTable. 还会自动的注册为一个请求终止后将被清理的资源.

设置选项

设置上下文选项的内部API和用户空间的API是等同的:

int php_stream_context_set_option(php_stream_context *context,const char *wrappername, const char *optionname,zval *optionvalue);
下面是用户空间的原型:

bool stream_context_set_option(resource $context,string $wrapper, string $optionname,mixed $value);
它们的不同仅仅是用户空间和内部需要的数据类型不同.下面的例子就是使用这两个API调用, 通过内建包装器发起一个HTTP请求, 并通过一个上下文选项覆写了user_agent设置.

php_stream*php_varstream_get_homepage(const char *alt_user_agent TSRMLS_DC){php_stream_context*context;zvaltmpval;context = php_stream_context_alloc(TSRMLS_C);ZVAL_STRING(&tmpval, alt_user_agent, 0); php_stream_context_set_option(context, "http", "user_agent", &tmpval);return php_stream_open_wrapper_ex("http://www.php.net", "rb", REPORT_ERRORS | ENFORCE_SAFE_MODE, NULL, context);}
译者使用的php-5.4.10中php_stream_context_alloc()增加了线程安全控制, 因此相应的对例子进行了修改, 请读者测试时注意.

这里要注意的是tmpval并没有分配任何持久性的存储空间, 它的字符串值是通过复制设置的. php_stream_context_set_option()会自动的对传入的zval内容进行一次拷贝.

取回选项

用于取回上下文选项的API调用正好是对应的设置API的镜像:

int php_stream_context_get_option(php_stream_context *context,const char *wrappername, const char *optionname,zval ***optionvalue);
回顾前面, 上下文选项存储在一个嵌套的HashTable中, 当从一个HashTable中取回值时, 一般的方法是传递一个指向zval **的指针给zend_hash_find(). 当然, 由于php_stream_context_get_option()是zend_hash_find()的一个特殊代理, 它们的语义是相同的.

下面是内建的http包装器使用php_stream_context_get_option()设置user_agent的简化版示例:

zval **ua_zval;char *user_agent = "PHP/5.1.0";if (context &&php_stream_context_get_option(context, "http","user_agent", &ua_zval) == SUCCESS &&Z_TYPE_PP(ua_zval) == IS_STRING) {user_agent = Z_STRVAL_PP(ua_zval);}
这种情况下, 非字符串值将会被丢弃, 因为对用户代理字符串而言, 数值是没有意义的. 其他的上下文选项, 比如max_redirects, 则需要数字值, 由于在字符串的zval中存储数字值并不通用, 所以需要执行一个类型转换以使设置合法.

不幸的是这些变量是上下文拥有的, 因此它们不能直接转换; 而需要首先进行隔离再进行转换, 最终如果需要还要进行销毁:

long max_redirects = 20;zval **tmpzval;if (context &&php_stream_context_get_option(context, "http","max_redirects", &tmpzval) == SUCCESS) {if (Z_TYPE_PP(tmpzval) == IS_LONG) {max_redirects = Z_LVAL_PP(tmpzval);} else {zval copyval = **tmpzval;zval_copy_ctor(?val);convert_to_long(?val);max_redirects = Z_LVAL(copyval);zval_dtor(?val);}}
实际上, 在这个例子中, zval_dtor()并不是必须的. IS_LONG的变量并不需要zval容器之外的存储空间, 因此zval_dtor()实际上不会有真正的操作. 在这个例子中包含它是为了完整性考虑, 对于字符串, 数组, 对象, 资源以及未来可能的其他类型, 就需要这个调用了.

参数

虽然用户空间API中看起来参数和上下文选项是类似的, 但实际上在语言内部的php_stream_context结构体中它们被定义为不同的成员.

目前只支持一个上下文参数: 通知器. php_stream_context结构体中的这个元素可以指向下面的php_stream_notifier结构体:

typedef struct {php_stream_notification_func func;void (*dtor)(php_stream_notifier *notifier);void *ptr;int mask;size_t progress, progress_max;} php_stream_notifier;