操纵
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);
}