5个静态包装器操作中的4个用来处理不是基于I/O的流资源操作. 你已经看到过它们并了解它们的原型; 现在我们看看varstream包装器框架中它们的实现:
unlink
在你的wrapper_ops结构体中增加下面的函数, 它可以让unlink()通过varstream包装器, 拥有和unset()一样的行为:
static int php_varstream_unlink(php_stream_wrapper *wrapper, char *filename, int options, php_stream_context *context TSRMLS_DC) { php_url *url; url = php_url_parse(filename); if (!url) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unexpected error parsing URL"); return -1; } if (!url->host || (url->host[0] == 0) || strcasecmp("var", url->scheme) != 0) { /* URL不合法 */ php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Invalid URL, must be in the form: " "var:/variablename"); php_url_free(url); return -1; } /* 从符号表删除变量 */ /zend_hash_del(&EG(symbol_table), url->host, strlen(url->host) + 1); zend_delete_global_variable(url->host, strlen(url->host) + 1 TSRMLS_CC); php_url_free(url); return 0; }
这个函数的编码量和php_varstream_opener差不多. 唯一的不同在于这里你需要传递变量名给zend_hash_del()去删除变量.
译注: 译者的php-5.4.10环境中, 使用unlink()删除变量后, 在用户空间再次读取该变量名的值会导致core dump. 因此上面代码中译者进行了修正, 删除变量时使用了zend_delete_global_variable(), 请读者参考阅读zend_delete_global_variable()函数源代码, 考虑为什么直接用zend_hash_del()删除, 会导致core dump. 下面是译者测试用的用户空间代码:
<?php $fp = fopen('var:/hello', 'r'); fwrite($fp, 'world'); var_dump($hello); unlink('var:/hello'); $a = $hello;
这个函数的代码量应该和php_varstream_opener差不多. 唯一的不同是这里是传递变量名给zend_hash_del()去删除变量.
rename, mkdir, rmdir
为了一致性, 下面给出rename, mkdir, rmdir函数的实现:
static int php_varstream_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC) { php_url *from, *to; zval **var; /* 来源URL解析 */ from = php_url_parse(url_from); if (!from) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unexpected error parsing source"); return -1; } /* 查找变量 */ if (zend_hash_find(&EG(symbol_table), from->host, strlen(from->host) + 1, (void**)&var) == FAILURE) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "$%s does not exist", from->host); php_url_free(from); return -1; } /* 目标URL解析 */ to = php_url_parse(url_to); if (!to) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unexpected error parsing dest"); php_url_free(from); return -1; } /* 变量的改名 */ Z_SET_REFCOUNT_PP(var, Z_REFCOUNT_PP(var) + 1); zend_hash_update(&EG(symbol_table), to->host, strlen(to->host) + 1, (void*)var, sizeof(zval*), NULL); zend_hash_del(&EG(symbol_table), from->host, strlen(from->host) + 1); php_url_free(from); php_url_free(to); return 0; } static int php_varstream_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, int options, php_stream_context *context TSRMLS_DC) { php_url *url; /* URL解析 */ url = php_url_parse(url_from); if (!url) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unexpected error parsing URL"); return -1; } /* 检查变量是否存在 */ if (zend_hash_exists(&EG(symbol_table), url->host, strlen(url->host) + 1)) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "$%s already exists", url->host); php_url_free(url); return -1; } /* EG(uninitialized_zval_ptr)通常是IS_NULL的zval *, 引用计数无限大 */ zend_hash_add(&EG(symbol_table), url->host, strlen(url->host) + 1, (void*)&EG(uninitialized_zval_ptr), sizeof(zval*), NULL); php_url_free(url); return 0; } static int php_varstream_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) { /* 行为等价于unlink() */ wrapper->wops->unlink(wrapper, url, options, context TSRMLS_CC); }