资讯专栏INFORMATION COLUMN

[译]将PHP扩展从PHP5升级到NG(PHP7)

Chaz / 3252人阅读

摘要:在这些情况下,应将相应的变量转换为纯,使用此变量从到和相应的创建宏从,到,。因此使用标志的宏被移除,和。宏可以用于在析构函数中达到实际的指针值。另外,如果使用添加元素,析构函数也负责指针本身的解除分配。应使用最佳的宏,而不是旧的和功能。

许多经常使用的API函数已经更改,例如HashTable API; 这个页面致力于记录尽可能多的实际影响扩展和核心代码的更改。 强烈建议在阅读本指南之前阅读phpng-int中有关PHPNG实现的一般信息。

这不是一个涵盖所有可能情况的完整指南。 这是一个在大多数情况下有用的汇总。 我希望它对大多数用户级扩展来说是足够的。 然而,如果你没有在这里找到一些信息,发现一个解决方案,因为它可能对其他人有用 - 随时完善您的方法。

一般建议

尝试使用PHPNG编译扩展。 查看编译错误和警告。 他们可以显示出75%需要修改的地方。

在调试模式下编译和测试扩展(使用 -enable-debug 来配置PHP)。它将在运行时使用 assert() 函数捕获一些错误。 您还将看到有关内存泄漏的信息。

zval

PHPNG不需要任何指向指向zval的指针的参与。大多数zval**变量和参数必须更改为zval*。 使用这些变量的相应Z_*_ PP()宏应该更改为Z_*_P()

在许多地方PHPNG直接使用zval(消除了分配和释放的需求)。 在这些情况下,应将相应的zval *变量转换为纯zval,使用此变量从Z_*_P()Z_*()和相应的创建宏从ZVAL_*(var,...)ZVAL_*(&var,...)。 一定要小心传递zval和&运算的地址。 PHPNG几乎从 不需要 传递 zval * 的地址。 在某些地方应该删除 & 运算。

有关zval分配的宏 ALLOC_ZVALALLOC_INIT_ZVALMAKE_STD_ZVAL 被移除。 在大多数情况下,它们的用法表明zval *需要更改为纯zval。 宏INIT_PZVAL也被删除,它的用法在大多数情况下应该被删除。

-  zval *zv;
-  ALLOC_INIT_ZVAL();
-  ZVAL_LONG(zv, 0);
+  zval zv;
+  ZVAL_LONG(&zv, 0);

zval结构已完全更改。 现在它的定义是:

struct _zval_struct {
    zend_value        value;            /* value */
    union {
        struct {
            ZEND_ENDIAN_LOHI_4(
                zend_uchar    type,         /* active type */
                zend_uchar    type_flags,
                zend_uchar    const_flags,
                zend_uchar    reserved)     /* various IS_VAR flags */
        } v;
        zend_uint type_info;
    } u1;
    union {
        zend_uint     var_flags;
        zend_uint     next;                 /* hash collision chain */
        zend_uint     str_offset;           /* string offset */
        zend_uint     cache_slot;           /* literal cache slot */
    } u2;
};

zend_value如下:

typedef union _zend_value {
    long              lval;             /* long value */
    double            dval;             /* double value */
    zend_refcounted  *counted;
    zend_string      *str;
    zend_array       *arr;
    zend_object      *obj;
    zend_resource    *res;
    zend_reference   *ref;
    zend_ast_ref     *ast;
    zval             *zv;
    void             *ptr;
    zend_class_entry *ce;
    zend_function    *func;
} zend_value;

主要的区别是,现在我们处理标量和复杂类型不同。 PHP不在堆中分配标量值,而是直接在VM堆栈上,在HashTables和对象内部。 它们 不再 是引用计数和垃圾收集的主体。 标量值没有引用计数器,不再支持 Z_ADDREF *()Z_DELREF *()Z_REFCOUNT *()Z_SET_REFCOUNT *() 宏。 在大多数情况下,你应该判断zval是否支持这些宏,然后再调用它们。 否则你会得到一个assert()或崩溃。

- Z_ADDREF_P(zv)
+ if (Z_REFCOUNTED_P(zv)) {Z_ADDREF_P(zv);}
# or equivalently
+ Z_TRY_ADDREF_P(zv);

应使用 ZVAL_COPY_VALUE() 宏复制zval值。

如果需要,可以使用 ZVAL_COPY() 宏复制和增加引用计数器。

可以使用 ZVAL_DUP() 宏来完成 zval(zval_copy_ctor) 的复制。

如果将zval *转换为zval并且提前使用NULL来指示未定义的值,那么现在可以改用IS_UNDEF类型。 它可以使用 ZVAL_UNDEF(&zv) 设置并可以使用if(Z_ISUNDEF(zv))进行检查。

如果要使用cast-semantics而不修改原始zval来获取zval的long/double/string值,现在可以使用 zval_get_long(zv)zval_get_double(zv)zval_get_string(zv) API简化代码:

- zval tmp;
- ZVAL_COPY_VALUE(&tmp, zv);
- zval_copy_ctor(&tmp);
- convert_to_string(&tmp);
- // ...
- zval_dtor(&tmp);
+ zend_string *str = zval_get_string(zv);
+ // ...
+ zend_string_release(str);

查看 zend_types.h 代码获取更多详细信息: https://github.com/php/php-sr...

参考

PHPNG中的 zval 不再有 is_ref 标志。 引用是使用多带带的复数引用计数类型 IS_REFERENCE 实现的。 仍然可以使用 Z_ISREF *() 宏来检查给定的 zval 是否被引用。 实际上,它只是检查给定的zval的类型是否等于IS_REFERENCE。 因此使用is_ref标志的宏被移除:Z_SET_ISREF *()Z_UNSET_ISREF *()Z_SET_ISREF_TO *() 。 它们的用法应该以下列方式改变:

- Z_SET_ISREF_P(zv);
+ ZVAL_MAKE_REF(zv);

- Z_UNSET_ISREF_P(zv);
+ if (Z_ISREF_P(zv)) {ZVAL_UNREF(zv);}

以前的引用可以直接检查引用的类型。 现在我们必须通过 Z_REFVAL *() 宏来间接检查它。

- if (Z_ISREF_P(zv) && Z_TYPE_P(zv) == IS_ARRAY) {}
+ if (Z_ISREF_P(zv) && Z_TYPE_P(Z_REFVAL_P(zv)) == IS_ARRAY) {}

或使用 ZVAL_DEREF() 宏执行手动取消引用:

- if (Z_ISREF_P(zv)) {...}
- if (Z_TYPE_P(zv) == IS_ARRAY) {
+ if (Z_ISREF_P(zv)) {...}
+ ZVAL_DEREF(zv);
+ if (Z_TYPE_P(zv) == IS_ARRAY) {
Booleans

IS_BOOL不再存在,但IS_TRUEIS_FALSE是依然是它的类型:

- if ((Z_TYPE_PP(item) == IS_BOOL || Z_TYPE_PP(item) == IS_LONG) && Z_LVAL_PP(item)) {
+ if (Z_TYPE_P(item) == IS_TRUE || (Z_TYPE_P(item) == IS_LONG && Z_LVAL_P(item))) {

将删除 Z_BVAL *() 宏。 注意, IS_FALSE/IS_TRUEZ_LVAL *() 的返回值里是没有定义的。

Strings

可以使用相同的宏 Z_STRVAL *()Z_STRLEN *() 来访问字符串的值/长度。 但是现在字符串表示的下划线数据结构是 zend_string (在多带带的部分中描述)。 zend_string可以通过 Z_STR *() 宏从zval中检索。 它也可以通过 Z_STRHASH *() 获取字符串的哈希值。

如果代码需要检查给定的字符串是否是可转为int,现在应该使用zend_string(不是char *):

- if (IS_INTERNED(Z_STRVAL_P(zv))) {
+ if (IS_INTERNED(Z_STR_P(zv))) {

创建字符串zvals有点改变。 以前的宏,如 ZVAL_STRING() 有一个额外的参数,告诉是否应该复制给定的字符。 现在这些宏总是必须创建 zend_string 结构,所以这个参数变得没用了。 但是,如果它的实际值为0,则可以释放原始字符串,以避免内存泄漏。

- ZVAL_STRING(zv, str, 1);
+ ZVAL_STRING(zv, str);

- ZVAL_STRINGL(zv, str, len, 1);
+ ZVAL_STRINGL(zv, str, len);

- ZVAL_STRING(zv, str, 0);
+ ZVAL_STRING(zv, str);
+ efree(str);

- ZVAL_STRINGL(zv, str, len, 0);
+ ZVAL_STRINGL(zv, str, len);
+ efree(str);

类似的宏,如 RETURN_STRING()RETVAL_STRINGS() 等等和一些内部API函数也是如此。

- add_assoc_string(zv, key, str, 1);
+ add_assoc_string(zv, key, str);

- add_assoc_string(zv, key, str, 0);
+ add_assoc_string(zv, key, str);
+ efree(str);

可以直接使用 zend_string API并直接从zend_string创建zval来避免双重新分配。

- char * str = estrdup("Hello");
- RETURN_STRING(str);
+ zend_string *str = zend_string_init("Hello", sizeof("Hello")-1, 0);
+ RETURN_STR(str);

Z_STRVAL *() 现在应该用作只读对象。 它不可能分配任何东西。 它可以修改多带带的字符,但在做之前,你必须确保这个字符串没有被引用到其他地方(它不是interned,它的reference-counter是1)。 此外,在字符串修改后,可能需要重置计算的哈希值。

  SEPARATE_ZVAL(zv);
  Z_STRVAL_P(zv)[0] = Z_STRVAL_P(zv)[0] + ("A" - "a");
+ zend_string_forget_hash_val((Z_STR_P(zv))
zend_string API

Zend有一个新的 zend_string API,除了zend_string是在zval中的字符串表示的下划线结构,这些结构也被用于以前使用 char *int 的大部分代码库。

可以使用 zend_string_init(char * val,size_t len,int persistent) 函数创建zend_strings(不是IS_STRING zvals)。 实际字符可以作为 str→val 和字符串长度作为 str→len 访问。 字符串的哈希值应通过 zend_string_hash_val 函数访问。 如果需要,它将重新计算哈希值。

字符串应该使用 zend_string_release() 函数释放,这不需要空闲内存,因为相同的字符串可能从几个地方引用。

如果你打算在某个地方保持 zend_string 指针,你应该增加它的reference-counter或使用 zend_string_copy() 函数,它会为你做。 在许多地方,代码复制字符只是为了保持值(不修改),可以使用这个函数。

- ptr->str = estrndup(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
+ ptr->str = zend_string_copy(Z_STR_P(zv));
  ...
- efree(str);
+ zend_string_release(str);

如果复制的字符串要更改,您可以使用 zend string_dup() :

- char *str = estrndup(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
+ zend_string *str = zend_string_dup(Z_STR_P(zv));
  ...
- efree(str);
+ zend_string_release(str);

具有旧宏的代码也是支持的,因此无需切换到新宏。

在某些情况下,在实际字符串数据已知之前分配字符串缓冲区是有意义的。 您可以使用 zend_string_alloc()zend_string_realloc() 函数来完成。

- char *ret = emalloc(16+1);
- md5(something, ret); 
- RETURN_STRINGL(ret, 16, 0);
+ zend_string *ret = zend_string_alloc(16, 0);
+ md5(something, ret->val);
+ RETURN_STR(ret);

不是所有的扩展代码都必须将 char * 转换为 zend_string 。 由扩展维护者决定哪种类型在每种特定情况下更合适。

查看 zend_string.h 代码了解更多详细信息:https://github.com/php/php-sr...

smart_str 和 smart_string

为了一致的命名约定,旧的smart_str API被重命名为smart_string。 它可以像以前一样使用,除了新的名称。

- smart_str str = {0};
- smart_str_appendl(str, " ", sizeof(" ") - 1);
- smart_str_0(str);
- RETURN_STRINGL(implstr.c, implstr.len, 0);
+ smart_string str = {0};
+ smart_string_appendl(str, " ", sizeof(" ") - 1);
+ smart_string_0(str);
+ RETVAL_STRINGL(str.c, str.len);
+ smart_string_free(&str);

此外,引入了一个新的 zend_str API,它直接与 zend_string 一起工作:

- smart_str str = {0};
- smart_str_appendl(str, " ", sizeof(" ") - 1);
- smart_str_0(str);
- RETURN_STRINGL(implstr.c, implstr.len, 0);
+ smart_str str = {0};
+ smart_str_appendl(&str, " ", sizeof(" ") - 1);
+ smart_str_0(&str);
+ if (str.s) {
+   RETURN_STR(str.s);
+ } else {
+   RETURN_EMPTY_STRING();
+ }

smart_str 定义如下:

typedef struct {
    zend_string *s;
    size_t a;
} smart_str;

smart_strsmart_string的API非常相似,实际上它们重复PHP5中使用的API。 所以采用代码不是一个大问题。 最大的问题是自动为每个特定情况选择什么,但它取决于最终结果的使用方式。

请注意,可能需要更改先前检查的空 smart_str

- if (smart_str->c) {
+ if (smart_str->s) {
strprintf

除了 sprintf()vsprintf() 函数,我们引入了类似的函数,产生zend_string ,而不是 char * 。 它取决于您决定何时应该更改为新的变体。

PHPAPI zend_string *vstrpprintf(size_t max_len, const char *format, va_list ap);
PHPAPI zend_string *strpprintf(size_t max_len, const char *format, ...);
Arrays

数组实现或多或少相同,但是,如果以前的下划线结构被实现为指向 HashTable 的指针,现在我们在这里有一个指向 zend_array 的内部保持 HashTableHashTable 可以像之前一样使用 Z_ARRVAL *() 宏读取,但现在不可能将指针更改为HashTable。 它只能通过宏Z_ARR *()获取或设置指向整个zend_array的指针。

创建数组的最好方法是使用旧的 array_init() 函数,但也可以使用 ZVAL_NEW_ARR() 创建新的未初始化数组,或者通过 ZVAL_ARR() 使用 zend_array 结构初始化数组。

一些数组可能是不可变的(可以使用 Z_IMMUTABLE() 宏来检查)。 如果代码需要修改它们,它们必须首先复制。 使用内部位置指针通过不可变数组迭代也是不可能的。 可以使用带有外部位置指针的旧迭代API或使用在多带带部分中描述的新的HashTable迭代API来遍历这些数组。

HashTable API

HashTable API 明显的改变,它可能会导致扩展兼容中的一些麻烦。

首先,现在HashTables总是使用zval。 即使我们存储一个任意指针,它被打包到zval与特殊类型IS_PTR。 无论如何,这简化了zval的工作:

- zend_hash_update(ht, Z_STRVAL_P(key), Z_STRLEN_P(key)+1, (void*)&zv, sizeof(zval**), NULL) == SUCCESS) {
+ if (zend_hash_update(EG(function_table), Z_STR_P(key), zv)) != NULL) {

大多数API函数直接返回请求的值(而不是通过引用参数使用附加参数并返回SUCCESS / FAILURE):

- if (zend_hash_find(ht, Z_STRVAL_P(key), Z_STRLEN_P(key)+1, (void**)&zv_ptr) == SUCCESS) {
+ if ((zv = zend_hash_find(ht, Z_STR_P(key))) != NULL) {

键表示为zend_string。 大多数函数有两种形式。 一个以zend_string作为键,另一个以char *作为键,长度对。

重要说明:当键值字符串的长度不包括尾随零(0)。 在某些地方,必须删除或添加+1 / -1

- if (zend_hash_find(ht, "value", sizeof("value"), (void**)&zv_ptr) == SUCCESS) {
+ if ((zv = zend_hash_str_find(ht, "value", sizeof("value")-1)) != NULL) {

这也适用于zend_hash之外的其他hashtable相关的API。 例如:

- add_assoc_bool_ex(&zv, "valid", sizeof("valid"), 0);
+ add_assoc_bool_ex(&zv, "valid", sizeof("valid") - 1, 0);

API提供了一组多带带的函数来处理任意指针。 这些函数与 _ptr 后缀具有相同的名称。

- if (zend_hash_find(EG(class_table), Z_STRVAL_P(key), Z_STRLEN_P(key)+1, (void**)&ce_ptr) == SUCCESS) {
+ if ((ce_ptr = zend_hash_find_ptr(EG(class_table), Z_STR_P(key))) != NULL) {

- zend_hash_update(EG(class_table), Z_STRVAL_P(key), Z_STRLEN_P(key)+1, (void*)&ce, sizeof(zend_class_entry*), NULL) == SUCCESS) {
+ if (zend_hash_update_ptr(EG(class_table), Z_STR_P(key), ce)) != NULL) {

API提供了一组多带带的函数来存储任意大小的内存块。 这些函数与 _mem 后缀具有相同的名称,并且它们实现为相应 _ptr 函数的内联封装。 这不意味着如果使用_mem或_ptr变量存储某些内容。 它总是可以使用 zend_hash_find_ptr() 找回来。

- zend_hash_update(EG(function_table), Z_STRVAL_P(key), Z_STRLEN_P(key)+1, (void*)func, sizeof(zend_function), NULL) == SUCCESS) {
+ if (zend_hash_update_mem(EG(function_table), Z_STR_P(key), func, sizeof(zend_function))) != NULL) {

增加了新的元素插入的新的优化功能。 它们旨在用于代码仅添加新元素(不能与现有键重叠)的情况。 例如,当您将一个HashTable的一些元素复制到一个新的。 所有这些函数都有 _new 后缀。

zval* zend_hash_add_new(HashTable *ht, zend_string *key, zval *zv);
zval* zend_hash_str_add_new(HashTable *ht, char *key, int len, zval *zv);
zval* zend_hash_index_add_new(HashTable *ht, pzval *zv);
zval* zend_hash_next_index_insert_new(HashTable *ht, pzval *zv);
void* zend_hash_add_new_ptr(HashTable *ht, zend_string *key, void *pData);
...

HashTable析构函数现在总是接收zval *(即使我们使用zend_hash_add_ptr或zend_hash_add_mem来添加元素)。 Z_PTR_P() 宏可以用于在析构函数中达到实际的指针值。 另外,如果使用 zend_hash_add_mem 添加元素,析构函数也负责指针本身的解除分配。

- void my_ht_destructor(void *ptr)
+ void my_ht_destructor(zval *zv)
  {
-    my_ht_el_t *p = (my_ht_el_t*) ptr;
+    my_ht_el_t *p = (my_ht_el_t*) Z_PTR_P(zv);
     ...
+    efree(p); // this efree() is not always necessary
  }
);

所有 zend_hash_apply_*() 函数的回调,以及 zend_hash_copy()zend_hash_merge() 的回调应该改变为接收 zval * 而不是 void * && ,与析构函数相同。 这些函数中的一些还接收指向 zend_hash_key 结构的指针。 它的定义以下面的方式改变。 对于字符串键,h包含hash函数的值,key是实际的字符串。 对于整数键,h包含数字键值,键为 NULL

typedef struct _zend_hash_key {
    ulong        h;
    zend_string *key;
} zend_hash_key;

在某些情况下,将 zend_hash_apply_*() 函数的用法更改为使用新的HashTable迭代API是有意义的。 这可能导致更小和更有效的代码。

可参考zend_hash.h :https://github.com/php/php-sr...

HashTable Iteration API

我们提供几个专门的宏来遍历HashTables的元素(和键)。 宏的第一个参数是哈希表,其他是在每个迭代步骤上分配的变量。

ZEND_HASH_FOREACH_VAL(ht, val)
ZEND_HASH_FOREACH_KEY(ht, h, key)
ZEND_HASH_FOREACH_PTR(ht, ptr)
ZEND_HASH_FOREACH_NUM_KEY(ht, h)
ZEND_HASH_FOREACH_STR_KEY(ht, key)
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val)
ZEND_HASH_FOREACH_KEY_VAL(ht, h, key, val)

应使用最佳的宏,而不是旧的reset, current, 和move功能。

- HashPosition pos;
  ulong num_key;
- char *key;
- uint key_len;
+ zend_string *key;
- zval **pzv;
+ zval *zv;
-
- zend_hash_internal_pointer_reset_ex(&ht, &pos);
- while (zend_hash_get_current_data_ex(&ht, (void**)&ppzval, &pos) == SUCCESS) {
-   if (zend_hash_get_current_key_ex(&ht, &key, &key_len, &num_key, 0, &pos) == HASH_KEY_IS_STRING){
-   }
+ ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, key, val) {
+   if (key) { //HASH_KEY_IS_STRING
+   }
    ........
-   zend_hash_move_forward_ex(&ht, &pos);
- }
+ } ZEND_HASH_FOREACH_END();
Objects

TODO: …

Custom Objects

TODO: …

zend_object struct定义为:

struct _zend_object {
    zend_refcounted   gc;
    zend_uint         handle; // TODO: may be removed ???
    zend_class_entry *ce;
    const zend_object_handlers *handlers;
    HashTable        *properties;
    HashTable        *guards; /* protects from __get/__set ... recursion */
    zval              properties_table[1];
};

我们内联了properties_table以获得更好的访问性能,但这也带来了一个问题,我们习惯于这样定义一个自定义对象:

struct custom_object {
   zend_object std;
   void  *custom_data;
}
 
 
zend_object_value custom_object_new(zend_class_entry *ce TSRMLS_DC) {
 
   zend_object_value retval;
   struct custom_object *intern;
 
   intern = emalloc(sizeof(struct custom_object));
   zend_object_std_init(&intern->std, ce TSRMLS_CC);
   object_properties_init(&intern->std, ce);
   retval.handle = zend_objects_store_put(intern,
        (zend_objects_store_dtor_t)zend_objects_destroy_object,
        (zend_objects_free_object_storage_t) custom_free_storage, 
        NULL TSRMLC_CC);
   intern->handle = retval.handle;
   retval.handlers = &custom_object_handlers;
   return retval;
}
 
struct custom_object* obj = (struct custom_object *)zend_objects_get_address(getThis());

但现在,zend_object是变量长度现在(内联的properties_table)。 因此上述代码应改为:

struct custom_object {
   void  *custom_data;
   zend_object std;
}
 
zend_object * custom_object_new(zend_class_entry *ce TSRMLS_DC) {
     # Allocate sizeof(custom) + sizeof(properties table requirements)
     struct custom_object *intern = ecalloc(1, 
         sizeof(struct custom_object) + 
         zend_object_properties_size(ce));
     # Allocating:
     # struct custom_object {
     #    void *custom_data;
     #    zend_object std;
     # }
     # zval[ce->default_properties_count-1]
     zend_object_std_init(&intern->std, ce TSRMLS_CC);
     ...
     custom_object_handlers.offset = XtOffsetOf(struct custom_obj, std);
     custom_object_handlers.free_obj = custom_free_storage;
 
     intern->std.handlers = custom_object_handlers;
 
     return &intern->std;
}
 
# Fetching the custom object:
 
static inline struct custom_object * php_custom_object_fetch_object(zend_object *obj) {
      return (struct custom_object *)((char *)obj - XtOffsetOf(struct custom_object, std));
}
 
#define Z_CUSTOM_OBJ_P(zv) php_custom_object_fetch_object(Z_OBJ_P(zv));
 
struct custom_object* obj = Z_CUSTOM_OBJ_P(getThis());
zend_object_handlers

一个新的项目偏移被添加到zend_object_handlers,你应该总是将它定义为在你的自定义对象结构中的zend_object偏移量。

它用 zend_objects_store_* 来查找分配的内存的正确起始地址。

// An example in spl_array
memcpy(&spl_handler_ArrayObject, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
spl_handler_ArrayObject.offset = XtOffsetOf(spl_array_object, std);

对象的内存现在将由 zend_objects_store_* 释放,因此您不应释放自定义对象free_obj处理程序中的内存。

Resources

类型 IS_RESOURCE 的zvals不再保留资源句柄。 无法使用 Z_LVAL_*() 检索资源句柄。 相反,应该使用 Z_RES_*() 宏直接检索资源记录。 资源记录由 zend_resource 结构表示。 它包含:

tyep - 资源类型,

ptr - 指向实际数据的指针,

handle - 数字资源索引(用于兼容性)以及引用计数器的服务字段。

实际上,这个zend_resurce结构是间接引用的zend_rsrc_list_entry的替代。 所有出现的zend_rsrc_list_entry应替换为zend_resource

zend_list_find() 函数被删除,因为资源被直接访问。

- long handle = Z_LVAL_P(zv);
- int  type;
- void *ptr = zend_list_find(handle, &type);
+ long handle = Z_RES_P(zv)->handle;
+ int  type = Z_RES_P(zv)->type;
+ void *ptr = = Z_RES_P(zv)->ptr;

删除 Z_RESVAL_*() 宏可以改用 Z_RES*():

- long handle = Z_RESVAL_P(zv);
+ long handle = Z_RES_P(zv)->handle;

ZEND_REGISTER_RESOURCE / ZEND_FETCH_RESOURCE()被删除

- ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, &link_arg, link_id, LE_LINK, le_link, le_plink);

//if you are sure that link_arg is a IS_RESOURCE type, then use :
+if ((ib_link = (ibase_db_link *)zend_fetch_resource2(Z_RES_P(link_arg), LE_LINK, le_link, le_plink)) == NULL) {
+    RETURN_FALSE;
+}

//otherwise, if you know nothing about link_arg"s type, use
+if ((ib_link = (ibase_db_link *)zend_fetch_resource2_ex(link_arg, LE_LINK, le_link, le_plink)) == NULL) {
+    RETURN_FALSE;
+}

- REGISTER_RESOURCE(return_value, result, le_result);
+ RETURN_RES(zend_register_resource(result, le_result);

zend_list_addref()zend_list_delref()函数被删除。 资源使用与所有zval相同的引用计数机制。

- zend_list_addref(Z_LVAL_P(zv));
+ Z_ADDREF_P(zv);

同样的:

- zend_list_addref(Z_LVAL_P(zv));
+ Z_RES_P(zv)->gc.refcount++;

zend_list_delete()将指针指向zend_resource结构,而不是资源句柄:

- zend_list_delete(Z_LVAL_P(zv));
+ zend_list_delete(Z_RES_P(zv));

在大多数用户扩展函数(如mysql_close())中,应该使用zend_list_close()而不是zend_list_delete()。 这将关闭实际连接并释放扩展特定的数据结构,但不释放zend_reference结构。 可能仍然从zval(s)引用。 这也不会递减资源引用计数器。

- zend_list_delete(Z_LVAL_P(zv));
+ zend_list_close(Z_RES_P(zv));
Parameters Parsing API changes

"l"说明符现在期望一个zend_long参数,而不是一个long参数。

- long lval;
+ zend_long lval;
  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &lval) == FAILURE) {

"s"说明符的长度参数现在需要一个size_t变量,而不是一个int变量。

  char *str;
- int len;
+ size_t len;
  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len) == FAILURE) {

除了需要字符串的"s"说明符,PHPNG引入了"S"说明符,它也期望字符串,但将参数放在zend_string变量中。 在某些情况下,直接使用zend_string是首选。 (例如,当接收到的字符串用作HashTable API中的键时。

- char *str;
- int len;
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len) == FAILURE) {
+ zend_string *str;
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "S", &str) == FAILURE) {

PHPNG不再使用zval **,所以它不再需要"Z"说明符了。 它必须替换为"z"

- zval **pzv;
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &pzv) == FAILURE) {
+ zval *zv;
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zv) == FAILURE) {

"+""*"说明符现在只返回zval数组(而不是之前的zval **数组)

- zval ***argv = NULL;
+ zval *argv = NULL;
  int argn;
  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &argv, &argn) == FAILURE) {

通过引用传递的参数应该分配到引用的值。 有可能分离这样的参数,得到引用值在第一位。

- zval **ret;
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &ret) == FAILURE) {
+ zval *ret;
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/", &ret) == FAILURE) {
    return;
  }
- ZVAL_LONG(*ret, 0);
+ ZVAL_LONG(ret, 0);
Call Frame Changes (zend_execute_data)

关于记录在zend_execute_data结构链中的每个函数调用的信息。 EG(current_execute_data) 指向当前执行函数的调用帧(以前的zend_execute_data结构仅为用户级PHP函数创建)。 我将尝试逐个字段解释旧的和新的调用框架结构之间的区别。

zend_execute_data.opline - 当前执行的用户函数的指令指针。 对于内部函数,其值未定义。 (以前为内部函数,其值为NULL)

zend_execute_data.function_state - 此字段已删除。 应该使用zend_execute_data.call。

zend_execute_data.call - 以前它是一个指向当前call_slot的指针。 目前它是一个指向当前调用函数的zend_execute_data的指针。 此字段最初为NULL,然后由ZEND_INIT_FCALL(或类似)操作码更改,然后由ZEND_FO_FCALL恢复。 语法嵌套函数调用,像foo($ a,bar($ c)),通过zend_execute_data.prev_nested_call构造一个这样的结构链

zend_execute_data.op_array - 此字段由zend_execute_data.func替换,因为现在它可能不仅表示用户函数,而且表示内部函数。

zend_execute_data.func - 当前执行的函数

zend_execute_data.object - $ this当前执行的函数(以前它是一个zval ,现在它是一个zend_object

zend_execute_data.symbol_table - 当前符号表或NULL

zend_execute_data.prev_execute_data - 回溯调用链的链接

original_return_valuecurrent_scopecurrent_called_scopecurrent_this - 这些字段保留旧值以在调用后恢复它们。 现在他们被删除。

zend_execute_data.scope - 当前执行函数的作用域(这是一个新字段)。

zend_execute_data.called_scope - called_scope当前执行的函数(这是一个新字段)。

zend_execute_data.run_time_cache - 当前执行函数的运行时缓存。 这是一个新字段,实际上它是op_array.run_time_cache的副本。

zend_execute_data.num_args - 传递给函数的参数数量(这是一个新字段)

zend_execute_data.return_value - 指向zval *的指针,其中当前执行的op_array应存储结果。 如果调用不关心返回值,它可以为NULL。 (这是一个新字段)。

参数存储在zval槽中的函数直接在zend_execute_data结构之后。 它们可以使用 ZEND_CALL_ARG(execute_data,arg_num) 宏访问。 对于用户PHP函数,第一个参数与第一个编译的变量 - CV0等重叠。如果调用者传递了被调用者接收的更多参数,所有额外的参数都被复制到被调用者CV和TMP变量之后。

Executor Globals - EG() Changes

EG(symbol_table) - 被改为一个zend_array(以前它是一个HashTable)。 到达下划线HashTable不是一个大问题

- symbols = zend_hash_num_elements(&EG(symbol_table));
+ symbols = zend_hash_num_elements(&EG(symbol_table).ht);

删除EG(uninitialized_zval_ptr)EG(error_zval_ptr)。 使用&EG(uninitialized_zval)&EG(error_zval)

EG(current_execute_data) - 这个字段的含义改变了一点。 以前它是一个指向最后执行的PHP函数的框架的指针。 现在它是一个指向最后执行的调用框架(如果它的用户或内部函数,不介意)。 可以获得最后一个op_array遍历调用链列表的zend_execute_data结构。

  zend_execute_data *ex = EG(current_execute_data);
+ while (ex && (!ex->func || !ZEND_USER_CODE(ex->func->type))) {
+    ex = ex->prev_execute_data;
+ }
  if (ex) {

EG(opline_ptr) - 。 请改用execute_data→opline

EG(return_value_ptr_ptr) - 已删除。 请改用execute_data→return_value

EG(active_symbol_table) - 被删除。 请使用execute_data→symbol_table

EG(active_op_array) - 被删除。 请使用execute_data→func

EG(called_scope) - 被删除。 请改用execute_data→called_scope

EG(This) - 变成了zval,以前它是一个指向zval的指针。 用户代码不应该修改它。

EG(in_execution))。 如果EG(current_excute_data)`不为NULL,我们正在执行某事。

EG(异常)EG(prev_exception) - 被转换为指向zend_object的指针,以前它们是指向zval的指针。

Opcodes changes

ZEND_DO_FCALL_BY_NAME - 已删除, ZEND_INIT_FCALL_BY_NAME已添加。

ZEND_BIND_GLOBAL - 被添加到处理“全局$ var”

ZEND_STRLEN - 已添加以替换strlen函数

ZEND_TYPE_CHECK - 已添加以替换is_array / is_int / is_ *(如果可能)

ZEND_DEFINED - 被添加来替换zif_defined如果可能(如果只有一个参数,它的常量字符串,它不在命名空间样式)

ZEND_SEND_VAR_EX - 是为了做比 ZEND_SEND_VAR更多的检查,如果条件无法在编译时间内解决

ZEND_SEND_VAL_EX - 已添加,以进行比 ZEND_SEND_VAL更多的检查,如果条件无法在编译时间内解决

ZEND_INIT_USER_CALL - 被添加以替换call_user_func(_array)如果可能的话,如果在编译时无法找到该函数,否则它可以转换为 ZEND_INIT_FCALL

ZEND_SEND_ARRAY - 被添加发送第二个参数,call_user_func_array的数组在被转换为操作码

ZEND_SEND_USER - 被添加以发送call_user_func的参数,在它被转换为操作码之后

temp_variable PCRE

一些pcre API使用或返回zend_string现在。 F.e. php_pcre_replace返回一个zend_string,并将zend_string作为第一个参数。 仔细检查他们的声明以及编译器警告,这很可能是错误的参数类型。

phpng-upgrading.txt · Last modified: 2016/01/21 17:18 by nikic

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/22149.html

相关文章

  • PHP 7 新特征

    摘要:本次发布标志着新的重要的系列的开始。经过社区投票,新项目命名为。结果如下结果如下四新特性标量类型声明有两种模式强制默认和严格模式。已废弃的和函数已被移除。在中,如果发生这种情况,会引发错误,并且返回。 最好的语言发布了新的版本,一个划时代的大版本:PHP7。 PHP7修复了大量BUG,新增了功能和语法糖。这些改动涉及到了核心包、GD库、PDO、ZIP、ZLIB等熟悉和不熟悉的核心功能与...

    Channe 评论0 收藏0
  • 安装php7,与php5共存

    摘要:安装源码在下载并解压。为不与冲突,文件夹都用,安装过程中报错的安装响应的依赖。启动中途如遇到日志文件路径不存在就手动创建并给予写的权限。遇到这个错误时,要添加个组再重新启动。的配置这是访问文件是变成下载文件,因为并未配置响应处理。 起步 之前在服务器搭建了lamp环境,想换用性能更强的nginx作为服务器软件,又想将php5升级为php7.安装nginx无需赘述:sudo apt-ge...

    TalkingData 评论0 收藏0
  • PHP7新特性的介绍

    摘要:截止到目前为止,官方已经发布了的版本,距离发布第一个正式版本不会很远了现在来说的重大特性肯定已经是定型了,不会再有什么变动了。 截止到目前为止,PHP官方已经发布了php7的RC7版本,距离发布第一个正式版本不会很远了!现在来说php7的重大特性肯定已经是定型了,不会再有什么变动了。后续一些版本的迭代主要也就是修修bug,优化之类的。下面就来说话我们一直期待的php7会有那些主要的变化...

    klivitamJ 评论0 收藏0
  • 项目切换PHP7.1.15

    摘要:也可以接入项目打包测试流程做代码检测。扩展替换以后废弃了和扩展,项目中使用的使用的类使用的是已经废弃的扩展使用扩展做兼容替换。测试方案和大部分公司差不多,项目组划分了线下开发环境预发布环境和生产环境三个环境。 项目由PHP5.5切换至PHP7.1.15 背景 从2015年鸟哥的技术分享,我们知道PHP7是对底层实现得一次完全重构,函数调用机制和内存管理等很多方便做了优化,使PHP性能有...

    tuniutech 评论0 收藏0
  • 如何把扩展PHP5升级PHP7

    摘要:所以,分配内存的宏都被删掉了。为的轮询设计了一组宏,使用起来非常方便。 我在公司的生产环境已经升级了PHP7,大部分活跃的扩展都可以兼容PHP7或者有了PHP7的分支,但是处理protocolbuffers数据的扩展一直没有人来升级,我只能摸着石头过河,自己做一把升级了。目前已经编译通过,处在处理单测失败的case阶段,欢迎大家一起讨论。对PHP扩展升级可以先看一下官方的升级说明,ht...

    AWang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<