摘要:告訴引擎要取的參數的信息,用來確保線程安全,返回值檢測是還是。數組遍歷假設我們需要一個取代以下功能的擴展的遍歷數組和差很多,提供了一些專門的宏來遍歷元素或。是一個關于線程安全的動作,用于避免各線程的作用域被其他的侵入。
起步
到這已經能聲明簡單函數,返回靜態或者動態值了。定義INI選項,聲明內部數值或全局數值。本章節將介紹如何接收從調用腳本(php文件)傳入參數的數值,以及 PHP內核 和 Zend引擎 如何操作內部變量。
接收參數與用戶控件的代碼不同,內部函數的參數實際上并不是在函數頭部聲明的,函數聲明都形如: PHP_FUNCTION(func_name) 的形式,參數聲明不在其中。參數的傳入是通過參數列表的地址傳入的,并且是傳入每一個函數,不論是否存在參數。
通過定義函數hello_str()來看一下,它將接收一個參數然后把它與問候的文本一起輸出。
PHP_FUNCTION(hello_greetme) { char *name = NULL; size_t name_len; zend_string *strg; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { RETURN_NULL(); } strg = strpprintf(0, "你好: %s", name); RETURN_STR(strg); }
大多數 zend_parse_parameters() 塊看起來都差不多。 ZEND_NUM_ARGS() 告訴Zend引擎要取的參數的信息, TSRMLS_CC 用來確保線程安全,返回值檢測是SUCCESS還是FAILURE。通常情況下返回是SUCCESS的。除非傳入的參數太少或太多或者參數不能被轉為適當的類型,Zend會自動輸出一條錯誤信息并將控制權還給調用腳本。
指定 "s" 表明此函數期望只傳入一個參數,并且該參數被轉化為string數據類型,地址傳入char * 變量。
此外,還有一個int變量通過地址傳遞到 zend_parse_parameters() 。這使Zend引擎提供字符串的字節長度,如此二進制安全的函數不再依賴strlen(name)來確定字符串的長度。因為實際上使用strlen(name)甚至得不到正確的結果,因為name可能在字符串結束之前包含了NULL字符。
在php7中,提供另一種獲取參數的方式FAST_ZPP,是為了提高參數解析的性能。
#ifdef FAST_ZPP ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_STR(type) Z_PARAM_OPTIONAL Z_PARAM_ZVAL_EX(value, 0, 1) ZEND_PARSE_PARAMETERS_END(); #endif參數類型表
類型 | 代碼 | 變量類型 |
---|---|---|
Boolean | b | zend_bool |
Long | l | long |
Double | d | double |
String | s | char*, int |
Resource | r | zval * |
Array | a | zval * |
Object | o | zval * |
zval | z | zval * |
最后四個類型都是zvals *.這是因為在php的實際使用中,zval數據類型存儲所有的用戶空間變量。三種“復雜”數據類型:資源、數組、對象。當它們的數據類型代碼被用于zend_parse_parameters()時,Zend引擎會進行類型檢查,但是因為在C中沒有與它們對應的數據類型,所以不會執行類型轉換。
Zval一般而言,zval和php用戶空間變量是很傷腦筋的,概念很難懂。到了PHP7,它的結構在Zend/zend_types.h中有定義:
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) /* call info for EX(This) */ } v; uint32_t type_info; } u1; union { uint32_t next; /* hash collision chain */ uint32_t cache_slot; /* literal cache slot */ uint32_t lineno; /* line number (for ast nodes) */ uint32_t num_args; /* arguments number for EX(This) */ uint32_t fe_pos; /* foreach position */ uint32_t fe_iter_idx; /* foreach iterator index */ uint32_t access_flags; /* class constant access flags */ uint32_t property_guard; /* single property guard */ } u2; };
可以看到,變量是通過_zval_struct結構體存儲的,而變量的值是zend_value類型的:
typedef union _zend_value { zend_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; struct { uint32_t w1; uint32_t w2; } ww; } zend_value;
雖然結構體看起來很大,但細細看,其實都是聯合體,value的擴充,u1是type_info,u2是其他各種輔助字段。
zval 類型變量存儲的數據是有數據類型的,php7中總體有以下類型,Zend/zend_types.h中有定義:
/* regular data types */ #define IS_UNDEF 0 #define IS_NULL 1 #define IS_FALSE 2 #define IS_TRUE 3 #define IS_LONG 4 #define IS_DOUBLE 5 #define IS_STRING 6 #define IS_ARRAY 7 #define IS_OBJECT 8 #define IS_RESOURCE 9 #define IS_REFERENCE 10 /* constant expressions */ #define IS_CONSTANT 11 #define IS_CONSTANT_AST 12 /* fake types */ #define _IS_BOOL 13 #define IS_CALLABLE 14 #define IS_ITERABLE 19 #define IS_VOID 18 /* internal types */ #define IS_INDIRECT 15 #define IS_PTR 17 #define _IS_ERROR 20測試
書寫一個類似gettype()來取得變量的類型的hello_typeof():
PHP_FUNCTION(hello_typeof) { zval *userval = NULL; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &userval) == FAILURE) { RETURN_NULL(); } switch (Z_TYPE_P(userval)) { case IS_NULL: RETVAL_STRING("NULL"); break; case IS_TRUE: RETVAL_STRING("true"); break; case IS_FALSE: RETVAL_STRING("false"); break; case IS_LONG: RETVAL_STRING("integer"); break; case IS_DOUBLE: RETVAL_STRING("double"); break; case IS_STRING: RETVAL_STRING("string"); break; case IS_ARRAY: RETVAL_STRING("array"); break; case IS_OBJECT: RETVAL_STRING("object"); break; case IS_RESOURCE: RETVAL_STRING("resource"); break; default: RETVAL_STRING("unknown type"); } }
這里使用RETVAL_STRING()與之前的RETURN_STRING()差別并不大,它們都是宏。只不過RETURN_STRING中包含了RETVAL_STRING的宏代替,詳細在 Zend/zend_API.h 中有定義:
#define RETVAL_STRING(s) ZVAL_STRING(return_value, s) #define RETVAL_STRINGL(s, l) ZVAL_STRINGL(return_value, s, l) #define RETURN_STRING(s) { RETVAL_STRING(s); return; } #define RETURN_STRINGL(s, l) { RETVAL_STRINGL(s, l); return; }創建zval
前面用到的zval是由Zend引擎分配空間,也通過同樣的途徑釋放。然而有時候需要創建自己的zval,可以參考如下代碼:
{ zval temp; ZVAL_LONG(&temp, 1234); }數組
數組作為運載其他變量的變量。內部實現上使用了眾所周知的 HashTable .要創建將被返回PPHP的數組,最簡單的方法:
PHP語法 | C語法(arr是zval*) | 意義 |
---|---|---|
$arr = array(); | array_init(arr); | 初始化一個新數組 |
$arr[] = NULL; | add_next_index_null(arr); | 向數字索引的數組增加指定類型的值 |
$arr[] = 42; | add_next_index_long(arr, 42); | |
$arr[] = true; | add_next_index_bool(arr, 1); | |
$arr[] = 3.14; | add_next_index_double(arr, 3.14); | |
$arr[] = "foo"; | add_next_index_string(arr, "foo", 1); | |
$arr[] = $myvar; | add_next_index_zval(arr, myvar); | |
$arr[0] = NULL; | add_index_null(arr, 0); | 向數組中指定的數字索引增加指定類型的值 |
$arr[1] = 42; | add_index_long(arr, 1, 42); | |
$arr[2] = true; | add_index_bool(arr, 2, 1); | |
$arr[3] = 3.14; | add_index_double(arr, 3, 3.14); | |
$arr[4] = "foo"; | add_index_string(arr, 4, "foo", 1); | |
$arr[5] = $myvar; | add_index_zval(arr, 5, myvar); | |
$arr["abc"] = NULL; | add_assoc_null(arr, "abc"); | |
$arr["def"] = 711; | add_assoc_long(arr, "def", 711); | 向關聯索引的數組增加指定類型的值 |
$arr["ghi"] = true; | add_assoc_bool(arr, "ghi", 1); | |
$arr["jkl"] = 1.44; | add_assoc_double(arr, "jkl", 1.44); | |
$arr["mno"] = "baz"; | add_assoc_string(arr, "mno", "baz", 1); | |
$arr["pqr"] = $myvar; | add_assoc_zval(arr, "pqr", myvar); |
做一個測試:
PHP_FUNCTION(hello_get_arr) { array_init(return_value); add_next_index_null(return_value); add_next_index_long(return_value, 42); add_next_index_bool(return_value, 1); add_next_index_double(return_value, 3.14); add_next_index_string(return_value, "foo"); add_assoc_string(return_value, "mno", "baz"); add_assoc_bool(return_value, "ghi", 1); }
add_*_string()函數參數從四個改為了三個。
數組遍歷假設我們需要一個取代以下功能的擴展:
php7的遍歷數組和php5差很多,7提供了一些專門的宏來遍歷元素(或keys)。宏的第一個參數是HashTable,其他的變量被分配到每一步迭代:
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)因此它的對應函數實現如下:
PHP_FUNCTION(hello_array_strings) { ulong num_key; zend_string *key; zval *val, *arr; HashTable *arr_hash; int array_count; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) { RETURN_NULL(); } arr_hash = Z_ARRVAL_P(arr); array_count = zend_hash_num_elements(arr_hash); php_printf("The array passed contains %d elements ", array_count); ZEND_HASH_FOREACH_KEY_VAL(arr_hash, num_key, key, val) { //if (key) { //HASH_KEY_IS_STRING //} PHPWRITE(Z_STRVAL_P(val), Z_STRLEN_P(val)); php_printf(" "); }ZEND_HASH_FOREACH_END(); }因為這是新的遍歷方法,而我看的還是php5的處理方式,調試出上面的代碼花了不少功夫,總的來說,用宏的方式遍歷大大減少了編碼體積。哈希表是php中很重要的一個內容,有時間再好好研究一下。
遍歷數組的其他方式遍歷 HashTable 還有其他方法。Zend引擎針對這個任務展露了三個非常類似的函數:zend_hash_apply(), zend_hash_apply_with_argument(), zend_hash_apply_with_arguments。第一個形式僅僅遍歷HashTable,第二種形式允許傳入單個void*參數,第三種形式通過var arg列表允許數量不限的參數。hello_array_walk()展示個他們各自的行為。
static int php_hello_array_walk(zval *ele TSRMLS_DC) { zval temp = *ele; // 臨時zval,避免convert_to_string 污染原元素 zval_copy_ctor(&temp); // 分配新 zval 空間并復制 ele 的值 convert_to_string(&temp); // 字符串類型轉換 //簡單的打印 PHPWRITE(Z_STRVAL(temp), Z_STRLEN(temp)); php_printf(" "); zval_dtor(&temp); //釋放臨時的 temp return ZEND_HASH_APPLY_KEEP; } static int php_hello_array_walk_arg(zval *ele, char *greeting TSRMLS_DC) { php_printf("%s", greeting); php_hello_array_walk(ele TSRMLS_CC); return ZEND_HASH_APPLY_KEEP; } static int php_hello_array_walk_args(zval *ele, int num_args, va_list args, zend_hash_key *hash_key) { char *prefix = va_arg(args, char*); char *suffix = va_arg(args, char*); TSRMLS_FETCH(); php_printf("%s", prefix); // 打印鍵值對結果 php_printf("key is : [ "); if (hash_key->key) { PHPWRITE(ZSTR_VAL(hash_key->key), ZSTR_LEN(hash_key->key)); } else { php_printf("%ld", hash_key->h); } php_printf(" ]"); php_hello_array_walk(ele TSRMLS_CC); php_printf("%s ", suffix); return ZEND_HASH_APPLY_KEEP; }用戶調用的函數:
PHP_FUNCTION(hello_array_walk) { zval *arr; HashTable *arr_hash; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) { RETURN_NULL(); } arr_hash = Z_ARRVAL_P(arr); //第一種遍歷 簡單遍歷各個元素 zend_hash_apply(arr_hash, (apply_func_t)php_hello_array_walk TSRMLS_CC); //第二種遍歷 帶一個參數的簡單遍歷各個元素 zend_hash_apply_with_argument(arr_hash, (apply_func_arg_t)php_hello_array_walk_arg, "Hello " TSRMLS_CC); //第三種遍歷 帶多參數的遍歷key->value zend_hash_apply_with_arguments(arr_hash, (apply_func_args_t)php_hello_array_walk_args, 2, "Hello ", "Welcome to my extension!"); RETURN_TRUE; }為了復用,在輸出值時調用php_hello_array_walk(ele TSRMLS_CC)。傳入hello_array_walk()的數組被遍歷了三次,一次不帶參數,一次帶單個參數,一次帶兩給參數。三個遍歷的函數返回了ZEND_HASH_APPLY_KEEP。這告訴zend_hash_apply()函數離開HashTable中的(當前)元素,繼續處理下一個。
這兒也可以返回其他值:ZEND_HASH_APPLY_REMOVE刪
除當前元素并繼續應用到下一個;ZEND_HASH_APPLY_STOP在當前元素中止數組的遍歷并退出zend_hash_apply()函數。TSRMLS_FETCH() 是一個關于線程安全的動作,用于避免各線程的作用域被其他的侵入。因為zend_hash_apply()的多線程版本用了vararg列表,tsrm_ls標記沒有傳入walk()函數。
"888", "key2"=>"aaa"]; hello_array_walk($arr);
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/22065.html
摘要:在這些情況下,應將相應的變量轉換為純,使用此變量從到和相應的創建宏從,到,。因此使用標志的宏被移除,和。宏可以用于在析構函數中達到實際的指針值。另外,如果使用添加元素,析構函數也負責指針本身的解除分配。應使用最佳的宏,而不是舊的和功能。 許多經常使用的API函數已經更改,例如HashTable API; 這個頁面致力于記錄盡可能多的實際影響擴展和核心代碼的更改。 強烈建議在閱讀本指南之...
摘要:但在密集計算方面比等靜態編譯語言差幾十倍甚至上百倍。一使用棧內存在引擎和擴展中,經常要創建一個的變量,底層就是一個指針。代碼中創建的變量也進行了優化,直接在棧內存上預分配。應用層與底層在錯誤拋出的方式全部統一為異常。 原文:http://rango.swoole.com/archives/440最近PHP官方終于發布了傳說中的PHP7,雖然只是alpha版。PHP7號稱是新一代的PHP...
摘要:此版本被認為是在年發布后最重要的變化。標量類型聲明有兩種選擇強制強制性是默認模式,不需要指定。嚴格嚴格模式有明確的暗示。 PHP7是什么鬼? PHP7是PHP編程語言的一個主要版本,并號稱是開發Web應用程序的一次革命,可開發和交付移動企業和云應用。此版本被認為是PHP在2004年發布PHP5后最重要的變化。 新功能 PHP7有加入幾十個功能,最顯著的是下面提到 - 改進的性能 - P...
摘要:我們為了處理這些挑戰,提出了一個新的引用測試框架當然,也是開源的,并且在整個過程中節省了上百萬美元。另一方面,被證實有一些嚴重的缺點部署困難而且慢。在緩存刷新期間,當可用于別的進程的已緩存的文件字節碼在此時損壞,就會導致崩潰。 How Badoo saved one million dollars switching to PHP7 我們成功的把我們的應用遷移到了php7上面(數百臺機...
閱讀 2078·2021-10-08 10:21
閱讀 2471·2021-09-29 09:34
閱讀 3494·2021-09-22 15:51
閱讀 4926·2021-09-22 15:46
閱讀 2314·2021-08-09 13:42
閱讀 3434·2019-08-30 15:52
閱讀 2723·2019-08-29 17:13
閱讀 1555·2019-08-29 11:30