許多常用的API函數已經更改,例如HashTable API; 這個頁面致力於記錄儘量多的實際影響擴展和核心代碼的更改。 強烈建議在閱讀本指南以前閱讀phpng-int中有關PHPNG實現的通常信息。php
這不是一個涵蓋全部可能狀況的完整指南。 這是一個在大多數狀況下有用的彙總。 我但願它對大多數用戶級擴展來講是足夠的。 然而,若是你沒有在這裏找到一些信息,發現一個解決方案,由於它可能對其餘人有用 - 隨時完善您的方法。mysql
嘗試使用PHPNG編譯擴展。 查看編譯錯誤和警告。 他們能夠顯示出75%須要修改的地方。git
在調試模式下編譯和測試擴展(使用 -enable-debug
來配置PHP)。它將在運行時使用 assert()
函數捕獲一些錯誤。 您還將看到有關內存泄漏的信息。github
PHPNG不須要任何指向指向zval的指針的參與。大多數zval**
變量和參數必須更改成zval*
。 使用這些變量的相應Z_*_ PP()
宏應該更改成Z_*_P()
。sql
在許多地方PHPNG直接使用zval(消除了分配和釋放的需求)。 在這些狀況下,應將相應的zval *
變量轉換爲純zval
,使用此變量從Z_*_P()
到Z_*()
和相應的建立宏從ZVAL_*(var,...)
到ZVAL_*(&var,...)
。 必定要當心傳遞zval和&運算的地址。 PHPNG幾乎從 不須要
傳遞 zval *
的地址。 在某些地方應該刪除 &
運算。數組
有關zval分配的宏 ALLOC_ZVAL
, ALLOC_INIT_ZVAL
和 MAKE_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以下:app
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) {
IS_BOOL
再也不存在,但IS_TRUE
和IS_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_TRUE
在 Z_LVAL *()
的返回值裏是沒有定義的。
可使用相同的宏 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有一個新的 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
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_str
和smart_string
的API很是類似,實際上它們重複PHP5中使用的API。 因此採用代碼不是一個大問題。 最大的問題是自動爲每一個特定狀況選擇什麼,但它取決於最終結果的使用方式。
請注意,可能須要更改先前檢查的空 smart_str
:
- if (smart_str->c) { + if (smart_str->s) {
除了 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, ...);
數組實現或多或少相同,可是,若是之前的下劃線結構被實現爲指向 HashTable
的指針,如今咱們在這裏有一個指向 zend_array
的內部保持 HashTable
。 HashTable
能夠像以前同樣使用 Z_ARRVAL *()
宏讀取,但如今不可能將指針更改成HashTable。 它只能經過宏Z_ARR *()
獲取或設置指向整個zend_array的指針。
建立數組的最好方法是使用舊的 array_init()
函數,但也可使用 ZVAL_NEW_ARR()
建立新的未初始化數組,或者經過 ZVAL_ARR()
使用 zend_array
結構初始化數組。
一些數組多是不可變的(可使用 Z_IMMUTABLE()
宏來檢查)。 若是代碼須要修改它們,它們必須首先複製。 使用內部位置指針經過不可變數組迭代也是不可能的。 可使用帶有外部位置指針的舊迭代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...
咱們提供幾個專門的宏來遍歷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();
TODO: …
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
偏移量。
它用 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
處理程序中的內存。
類型 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));
'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);
關於記錄在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_value
,current_scope
,current_called_scope
,current_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變量以後。
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的指針。
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
的參數,在它被轉換爲操做碼以後
一些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