操纵

5个静态包装器操作中的4个用来处理不是基于I/O的流资源操作. 你已经看到过它们并了解它们的原型; 现在我们看看varstream包装器框架中它们的实现:

在你的wrapper_ops结构体中增加下面的函数, 它可以让unlink()通过varstream包装器, 拥有和unset()一样的行为:

  1. static int php_varstream_unlink(php_stream_wrapper *wrapper,
  2. char *filename, int options,
  3. php_stream_context *context
  4. TSRMLS_DC)
  5. {
  6. php_url *url;
  7. url = php_url_parse(filename);
  8. if (!url) {
  9. php_stream_wrapper_log_error(wrapper, options
  10. TSRMLS_CC, "Unexpected error parsing URL");
  11. return -1;
  12. }
  13. if (!url->host || (url->host[0] == 0) ||
  14. strcasecmp("var", url->scheme) != 0) {
  15. /* URL不合法 */
  16. php_stream_wrapper_log_error(wrapper, options
  17. TSRMLS_CC, "Invalid URL, must be in the form: "
  18. "var://variablename");
  19. php_url_free(url);
  20. return -1;
  21. }
  22. /* 从符号表删除变量 */
  23. //zend_hash_del(&EG(symbol_table), url->host, strlen(url->host) + 1);
  24. zend_delete_global_variable(url->host, strlen(url->host) + 1 TSRMLS_CC);
  25. php_url_free(url);
  26. return 0;
  27. }

这个函数的编码量和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. 下面是译者测试用的用户空间代码:

  1. <?php
  2. $fp = fopen('var://hello', 'r');
  3. fwrite($fp, 'world');
  4. var_dump($hello);
  5. unlink('var://hello');
  6. $a = $hello;

这个函数的代码量应该和php_varstream_opener差不多. 唯一的不同是这里是传递变量名给zend_hash_del()去删除变量.

rename, mkdir, rmdir

为了一致性, 下面给出rename, mkdir, rmdir函数的实现:

  1. static int php_varstream_rename(php_stream_wrapper *wrapper,
  2. char *url_from, char *url_to, int options,
  3. php_stream_context *context TSRMLS_DC)
  4. {
  5. php_url *from, *to;
  6. zval **var;
  7. /* 来源URL解析 */
  8. from = php_url_parse(url_from);
  9. if (!from) {
  10. php_stream_wrapper_log_error(wrapper, options
  11. TSRMLS_CC, "Unexpected error parsing source");
  12. return -1;
  13. }
  14. /* 查找变量 */
  15. if (zend_hash_find(&EG(symbol_table), from->host,
  16. strlen(from->host) + 1,
  17. (void**)&var) == FAILURE) {
  18. php_stream_wrapper_log_error(wrapper, options
  19. TSRMLS_CC, "$%s does not exist", from->host);
  20. php_url_free(from);
  21. return -1;
  22. }
  23. /* 目标URL解析 */
  24. to = php_url_parse(url_to);
  25. if (!to) {
  26. php_stream_wrapper_log_error(wrapper, options
  27. TSRMLS_CC, "Unexpected error parsing dest");
  28. php_url_free(from);
  29. return -1;
  30. }
  31. /* 变量的改名 */
  32. Z_SET_REFCOUNT_PP(var, Z_REFCOUNT_PP(var) + 1);
  33. zend_hash_update(&EG(symbol_table), to->host,
  34. strlen(to->host) + 1, (void*)var,
  35. sizeof(zval*), NULL);
  36. zend_hash_del(&EG(symbol_table), from->host,
  37. strlen(from->host) + 1);
  38. php_url_free(from);
  39. php_url_free(to);
  40. return 0;
  41. }
  42. static int php_varstream_mkdir(php_stream_wrapper *wrapper,
  43. char *url_from, int mode, int options,
  44. php_stream_context *context TSRMLS_DC)
  45. {
  46. php_url *url;
  47. /* URL解析 */
  48. url = php_url_parse(url_from);
  49. if (!url) {
  50. php_stream_wrapper_log_error(wrapper, options
  51. TSRMLS_CC, "Unexpected error parsing URL");
  52. return -1;
  53. }
  54. /* 检查变量是否存在 */
  55. if (zend_hash_exists(&EG(symbol_table), url->host,
  56. strlen(url->host) + 1)) {
  57. php_stream_wrapper_log_error(wrapper, options
  58. TSRMLS_CC, "$%s already exists", url->host);
  59. php_url_free(url);
  60. return -1;
  61. }
  62. /* EG(uninitialized_zval_ptr)通常是IS_NULL的zval *, 引用计数无限大 */
  63. zend_hash_add(&EG(symbol_table), url->host,
  64. strlen(url->host) + 1,
  65. (void*)&EG(uninitialized_zval_ptr),
  66. sizeof(zval*), NULL);
  67. php_url_free(url);
  68. return 0;
  69. }
  70. static int php_varstream_rmdir(php_stream_wrapper *wrapper,
  71. char *url, int options,
  72. php_stream_context *context TSRMLS_DC)
  73. {
  74. /* 行为等价于unlink() */
  75. wrapper->wops->unlink(wrapper, url, options,
  76. context TSRMLS_CC);
  77. }