一步一步开发PHP扩展(7):定义常量

php7扩展开发教程

我们在PHP程序中经常使用define()来定义常量,如下所示:

  1. <?php
  2.     define("__ARR__"array('2', 'site'=>"www.bo56.com"));
  3.     define("__SITE__""www.bo56.com", true);
  4.     define("say\__SITE__""bo56.com");
  5.     var_dump(__ARR__);
  6.     var_dump(__site__);
  7.     var_dump(say\__SITE__);
  8. ?>

那么在PHP扩展中我们该如何定义一个常量呢?

一般情况下,在扩展中只建议定义null,bool,long,double,string几种类型的常量。因为内核只提供了这几种类型的宏方法。常量定义的宏方法在Zend/zend_constants.h文件中。想定义一个常量,很简单,只要调用对应的宏方法即可。如:

  1. REGISTER_STRINGL_CONSTANT("__SITE__""www.bo56.com", 12, CONST_PERSISTENT);

宏方法的最后一个参数是一些标识符。

  • CONST_PERSISTENT 表示为持久的。常驻内存。
  • CONST_CS 表示为区分大小写。

注意我们上面定义常量时使用的是__SITE__,但是调用的时候使用的是__site__。还有一套可以指定命名空间的宏方法。宏方法中带NS。如:

  1. REGISTER_NS_STRINGL_CONSTANT("say""__SITE__""bo56.com", 8, CONST_CS|CONST_PERSISTENT);

第一个参数就是命名空间。

为了展示常量定义的一些细节。我们定义了一个__ARR__常量。

ZVAL_NEW_PERSISTENT_ARR(&c.value);我们想让__ARR__为持久的。所以使用ZVAL_NEW_PERSISTENT_ARR创建一个数组。

数组创建完后,我们需要初始化。初始化的代码就是

  1. zend_hash_init(Z_ARRVAL(c.value), 0, NULL,
  2.                         (dtor_func_t)say_entry_dtor_persistent, 1);

参数中的say_entry_dtor_persistent是一个析构函数,用于释放数组的元素。到这里,如果编译运行。当程序执行结束的时候,你会发现一个致命错误。错误信息如下:

  1. Fatal error: Internal zval's can't be arrays, objects or resources in Unknown on line 0

因为在程序执行完毕,内部zval释放的时候,会进行类型检测。如果发现是array object或者resources,则会报错。可以查看Zend/zend_variables.c文件中_zval_internal_dtor方法。

为了解决这个问题,我们需要手动释放我们创建的__ARR__相关的数组。

模块卸载时执行的方法,是优先Zend内部zval释放方法之前调用的。因此,我们只要在PHP_MSHUTDOWN_FUNCTION(say)方法中手动释放。不再让Zend去释放就可以解决了。

示例代码

下面我们将在say扩展的 PHP_MINIT_FUNCTION(say) 方法上增加相应的代码。源码如下:

  1. //增加两个方法
  2. //释放hash
  3. static void say_hash_destroy(HashTable *ht)
  4.     zend_string *key;
  5.     zval *element;
  6.     if (((ht)->u.flags & HASH_FLAG_INITIALIZED)) {
  7.         ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, element) {
  8.             if (key) {
  9.                 free(key);
  10.             }
  11.             switch (Z_TYPE_P(element)) {
  12.                 case IS_STRING:
  13.                     free(Z_PTR_P(element));
  14.                     break;
  15.                 case IS_ARRAY:
  16.                     say_hash_destroy(Z_ARRVAL_P(element));
  17.                     break;
  18.             }
  19.         } ZEND_HASH_FOREACH_END();
  20.         free(HT_GET_DATA_ADDR(ht));
  21.     }
  22.     free(ht);
  23. }
  24. //释放数组和字符串
  25. static void say_entry_dtor_persistent(zval *zvalue)
  26.     if (Z_TYPE_P(zvalue) == IS_ARRAY) {
  27.         say_hash_destroy(Z_ARRVAL_P(zvalue));
  28.     } else if (Z_TYPE_P(zvalue) == IS_STRING) {
  29.         zend_string_release(Z_STR_P(zvalue));
  30.     }
  31. }
  32. //PHP_MINIT_FUNCTION(say)方法的PHP扩展源码: 扩展初始化的调用此方法
  33. PHP_MINIT_FUNCTION(say)
  34. {
  35.     zend_constant c;
  36.     zend_string *key;
  37.     zval value;
  38.     ZVAL_NEW_PERSISTENT_ARR(&c.value);
  39.     zend_hash_init(Z_ARRVAL(c.value), 0, NULL,
  40.                         (dtor_func_t)say_entry_dtor_persistent, 1);
  41.     add_index_long(&c.value, 0, 2);
  42.     key = zend_string_init("site", 4, 1);
  43.     ZVAL_STR(&value, zend_string_init("www.bo56.com", 12, 1));
  44.     zend_hash_update(Z_ARRVAL(c.value), key, &value);
  45.     c.flags = CONST_CS|CONST_PERSISTENT;
  46.     c.name = zend_string_init("__ARR__", 7, 1);
  47.     c.module_number = module_number;
  48.     zend_register_constant(&c);
  49.     REGISTER_STRINGL_CONSTANT("__SITE__""www.bo56.com", 12, CONST_PERSISTENT);
  50.     REGISTER_NS_STRINGL_CONSTANT("say""__SITE__""bo56.com", 8, CONST_CS|CONST_PERSISTENT);
  51. }
  52. //扩展卸载的时候调用此方法
  53. PHP_MSHUTDOWN_FUNCTION(say)
  54. {
  55.     zval *val;
  56.     val = zend_get_constant_str("__ARR__", 7);
  57.     say_hash_destroy(Z_ARRVAL_P(val));
  58.     ZVAL_NULL(val);
  59.     return SUCCESS;
  60. }

下篇笔记分享PHP扩展中类的创建,敬请期待。

 

你想把广告放到这里吗?

发表评论

您必须 登录 才能发表留言!