您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)PHP源碼之implode函數(shù)源碼怎么用,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
// 方法1 implode ( string $glue , array $pieces ) : string // 方法2 implode ( array $pieces ) : string
$pieces = [ 123, ',是一個(gè)', 'number!', ]; $str1 = implode($pieces); $str2 = implode('', $pieces); var_dump($str1, $str2); /* string(20) "123,是一個(gè)number!" string(20) "123,是一個(gè)number!" */
PHP_FUNCTION(implode)
可以找到,該函數(shù)定義于 \ext\standard\string.c
文件中的 1288 行if (arg2 == NULL) { if (Z_TYPE_P(arg1) != IS_ARRAY) { php_error_docref(NULL, E_WARNING, "Argument must be an array"); return; } glue = ZSTR_EMPTY_ALLOC(); tmp_glue = NULL; pieces = arg1; } else { if (Z_TYPE_P(arg1) == IS_ARRAY) { glue = zval_get_tmp_string(arg2, &tmp_glue); pieces = arg1; } else if (Z_TYPE_P(arg2) == IS_ARRAY) { glue = zval_get_tmp_string(arg1, &tmp_glue); pieces = arg2; } else { php_error_docref(NULL, E_WARNING, "Invalid arguments passed"); return; } }
arg2 == NULL
,主要是對參數(shù)的一些處理php_implode(glue, pieces, return_value);
PHP源碼中 return_value
,找到了答案。PHP_FUNCTION(implode)
來聲明的。而 PHP_FUNCTION 的定義是:#define PHP_FUNCTION ZEND_FUNCTION // 對應(yīng)的 ZEND_FUNCTION 定義如下 #define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name)) // 對應(yīng)的 ZEND_NAMED_FUNCTION 定義如下 #define ZEND_NAMED_FUNCTION(name) void ZEND_FASTCALL name(INTERNAL_FUNCTION_PARAMETERS) // 對應(yīng)的 ZEND_FN 定義如下 #define ZEND_FN(name) zif_##name // 對應(yīng)的 ZEND_FASTCALL 定義如下 # define ZEND_FASTCALL __attribute__((fastcall))
void zif_implode(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC)
ALLOCA_FLAG(use_heap)
進(jìn)行標(biāo)識,如果申請內(nèi)存,則申請的是堆內(nèi)存numelems = zend_hash_num_elements(Z_ARRVAL_P(pieces));
獲取 pieces 參數(shù)的單元數(shù)量,如果是空數(shù)組,則直接返回空字符串ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zend_array), tmp) { // ... } ZEND_HASH_FOREACH_END();
在循環(huán)內(nèi),對數(shù)組單元分為三類:
struct { zend_string *str; zend_long lval; } *strings, *ptr;
// tmp 是循環(huán)中的單元值 ptr->str = Z_STR_P(tmp); len += ZSTR_LEN(ptr->str); ptr->lval = 0; ptr++;
len += ZSTR_LEN(ptr->str);
Z_TYPE_P(tmp) == IS_LONG
。一旦知道當(dāng)前的數(shù)據(jù)類型是 zend_long,則將其賦值給 ptr 的 lval 結(jié)構(gòu)體成員。然后 ptr 指針后移一個(gè)單位長度。len += ZSTR_LEN(val);
的方式獲取其字符長度。對于 zend_long,有什么好的方法呢?while (val) { val /= 10; len++; }
if (val <= 0) { len++; }
str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(glue), len, 0);
,用于存放單元字符串總長度加上連接字符的總長度,即 (n-1)glue + len
。因?yàn)?n 個(gè)數(shù)組單元,只需要 n-1 個(gè) glue 字符串。然后,將這段內(nèi)存的尾地址,賦值給 cptr,為什么要指向尾部呢?看下一部分,你就會(huì)明白了。ptr--;
,然后針對 ptr->str 的判斷 if (EXPECTED(ptr->str))
,看了一下此處的 EXPECTED 的作用,可以參考這里??梢院唵蔚膶⑵淅斫庖环N匯編層面的優(yōu)化,當(dāng)實(shí)際執(zhí)行的情況更偏向于當(dāng)前條件下的分支而非 else 的分支時(shí),就用 EXPECTED 宏將其包裝起來:EXPECTED(ptr->str)
。我敢說,當(dāng)你調(diào)用 implode 傳遞的數(shù)組中都是數(shù)字而非字符串,那么這里的 EXPECTED 作用就會(huì)失效。cptr -= ZSTR_LEN(ptr->str); memcpy(cptr, ZSTR_VAL(ptr->str), ZSTR_LEN(ptr->str));
ptr->str
(某數(shù)組單元的值)通過 c 標(biāo)準(zhǔn)庫函數(shù) memcpy 拷貝到 cptr 內(nèi)存空間中。ptr == strings
滿足時(shí),意味著 ptr 不再有可被復(fù)制的字符串/數(shù)字。因?yàn)?strings 是 ptr 所在區(qū)域的首地址。ptr->str
一樣處理 glue 即可。至少源碼中是這么做的。*cptr = 0
,它的作用相當(dāng)于賦值空字符串。free_alloca(strings, use_heap); RETURN_NEW_STR(str);
free_alloca(strings, use_heap);
。RETURN_NEW_STR
會(huì)將最終的返回值寫入 return_value 中debug/implode
,可自行下載運(yùn)行,看看效果。static void su_php_implode(const zend_string *glue, zval *pieces, zval *return_value) { // 源碼內(nèi)容省略 }
PHP_FUNCTION(su_test) { zval tmp; zend_string *str, *glue, *tmp_glue; zval *arg1, *arg2 = NULL, *pieces; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_ZVAL(arg1) Z_PARAM_OPTIONAL Z_PARAM_ZVAL(arg2) ZEND_PARSE_PARAMETERS_END(); glue = zval_get_tmp_string(arg1, &tmp_glue); pieces = arg2; su_php_implode(glue, pieces, return_value); }
// t1.php $res = su_test('-', [ 2019, '01', '01', ]); var_dump($res);
string(10) "2019-01-01"
,這意味著,你已經(jīng)成功編寫了一個(gè)擴(kuò)展函數(shù)。別急,這只是邁出了第一步,別忘記我們的目標(biāo):通過調(diào)試來學(xué)習(xí) implode 源碼。gdb php b zval_get_tmp_string r t1.php
b
即 break,表示打一個(gè)斷點(diǎn)r
即 run,表示運(yùn)行腳本s
即 step,表示一步一步調(diào)試,遇到方法調(diào)用,會(huì)進(jìn)入方法內(nèi)部單步調(diào)試n
即 next,表示一行一行調(diào)試。遇到方法,則調(diào)試直接略過直接執(zhí)行返回,調(diào)試不會(huì)進(jìn)入其內(nèi)部。p
即 print,表示打印當(dāng)前作用域中的一個(gè)變量r t1.php
,則會(huì)定位到第一個(gè)斷點(diǎn)對應(yīng)的行,顯示如下:Breakpoint 1, zif_su_test (execute_data=0x7ffff1a1d0c0, return_value=0x7ffff1a1d090) at /home/www/clang/php-7.3.3/ext/su_dd/su_dd.c:179 179 glue = zval_get_tmp_string(arg1, &tmp_glue);
n
,顯示如下:184 su_php_implode(glue, pieces, return_value);
glue
,pieces
,return_value
pieces
的值。先使用命令:p pieces
,此時(shí)在終端會(huì)顯示類似于如下內(nèi)容:$1 = (zval *) 0x7ffff1a1d120
pieces
是一個(gè) zval 類型的指針,0x7ffff1a1d120
是其地址,當(dāng)然,你運(yùn)行的時(shí)候?qū)?yīng)的也是一個(gè)地址,只不過跟我的這個(gè)會(huì)不太一樣。p
去打印存儲(chǔ)于改地址的變量內(nèi)容:p *$1
,$1
可以認(rèn)為是一個(gè)臨時(shí)變量名,*
是取值運(yùn)算符。運(yùn)行完后,此時(shí)顯示如下:(gdb) p *$1 $2 = {value = {lval = 140737247576960, dval = 6.9533439118030153e-310, counted = 0x7ffff1a60380, str = 0x7ffff1a60380, arr = 0x7ffff1a60380, obj = 0x7ffff1a60380, res = 0x7ffff1a60380, ref = 0x7ffff1a60380, ast = 0x7ffff1a60380, zv = 0x7ffff1a60380, ptr = 0x7ffff1a60380, ce = 0x7ffff1a60380, func = 0x7ffff1a60380, ww = {w1 = 4054188928, w2 = 32767}}, u1 = {v = {type = 7 '\a', type_flags = 1 '\001', u = { call_info = 0, extra = 0}}, type_info = 263}, u2 = {next = 0, cache_slot = 0, opline_num = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, constant_flags = 0, extra = 0}}
struct _zval_struct { zend_value value; /* value */ union { struct { ZEND_ENDIAN_LOHI_3( zend_uchar type, /* active type */ zend_uchar type_flags, union { uint16_t call_info; /* call info for EX(This) */ uint16_t extra; /* not further specified */ } u) } v; uint32_t type_info; } u1; union { uint32_t next; /* hash collision chain */ uint32_t cache_slot; /* cache slot (for RECV_INIT) */ uint32_t opline_num; /* opline number (for FAST_CALL) */ 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 */ uint32_t constant_flags; /* constant flags */ uint32_t extra; /* not further specified */ } u2; };
value
,打印一下其中的內(nèi)容。打印結(jié)構(gòu)體成員可以使用 .
運(yùn)算符,例如:p $2.value
,運(yùn)行這個(gè)命令,顯示如下:(gdb) p $2.value $3 = {lval = 140737247576960, dval = 6.9533439118030153e-310, counted = 0x7ffff1a60380, str = 0x7ffff1a60380, arr = 0x7ffff1a60380, obj = 0x7ffff1a60380, res = 0x7ffff1a60380, ref = 0x7ffff1a60380, ast = 0x7ffff1a60380, zv = 0x7ffff1a60380, ptr = 0x7ffff1a60380, ce = 0x7ffff1a60380, func = 0x7ffff1a60380, ww = {w1 = 4054188928, w2 = 32767}}
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;
*arr
成員,它外表看起來就是一個(gè)指針,因此打印其內(nèi)容,需要使用 *
運(yùn)算符(gdb) p *$3.arr $4 = {gc = {refcount = 2, u = {type_info = 23}}, u = {v = {flags = 28 '\034', _unused = 0 '\000', nIteratorsCount = 0 '\000', _unused2 = 0 '\000'}, flags = 28}, nTableMask = 4294967294, arData = 0x7ffff1a67648, nNumUsed = 3, nNumOfElements = 3, nTableSize = 8, nInternalPointer = 0, nNextFreeElement = 3, pDestructor = 0x555555b6e200 <zval_ptr_dtor>}
*arr
的類型是 zend_array:struct _zend_array { zend_refcounted_h gc; union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar _unused, zend_uchar nIteratorsCount, zend_uchar _unused2) } v; uint32_t flags; } u; uint32_t nTableMask; Bucket *arData; uint32_t nNumUsed; uint32_t nNumOfElements; uint32_t nTableSize; uint32_t nInternalPointer; zend_long nNextFreeElement; dtor_func_t pDestructor; };
*arData
,使用:p *$4.arDaa
:(gdb) p *$4.arData $5 = {val = {value = {lval = 2019, dval = 9.9751853895347677e-321, counted = 0x7e3, str = 0x7e3, arr = 0x7e3, obj = 0x7e3, res = 0x7e3, ref = 0x7e3, ast = 0x7e3, zv = 0x7e3, ptr = 0x7e3, ce = 0x7e3, func = 0x7e3, ww = {w1 = 2019, w2 = 0}}, u1 = {v = {type = 4 '\004', type_flags = 0 '\000', u = {call_info = 0, extra = 0}}, type_info = 4}, u2 = {next = 0, cache_slot = 0, opline_num = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, constant_flags = 0, extra = 0}}, h = 0, key = 0x0}
lval = 2019
。$res = implode('-', [ 2019, '01', '01', [1,2] ]); var_dump($res);
PHP Notice: Array to string conversion in /path/to/t2.php on line 3 PHP Notice: Array to string conversion in /path/to/t2.php on line 3 string(16) "2019-01-01-Array"
PHPAPI void php_implode(const zend_string *glue, zval *pieces, zval *return_value) { zval *tmp; int numelems; zend_string *str; char *cptr; size_t len = 0; struct { zend_string *str; zend_long lval; } *strings, *ptr; ALLOCA_FLAG(use_heap) numelems = zend_hash_num_elements(Z_ARRVAL_P(pieces)); if (numelems == 0) { RETURN_EMPTY_STRING(); } else if (numelems == 1) { /* loop to search the first not undefined element... */ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pieces), tmp) { RETURN_STR(zval_get_string(tmp)); } ZEND_HASH_FOREACH_END(); } ptr = strings = do_alloca((sizeof(*strings)) * numelems, use_heap); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pieces), tmp) { if (EXPECTED(Z_TYPE_P(tmp) == IS_STRING)) { ptr->str = Z_STR_P(tmp); len += ZSTR_LEN(ptr->str); ptr->lval = 0; ptr++; } else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_LONG)) { zend_long val = Z_LVAL_P(tmp); ptr->str = NULL; ptr->lval = val; ptr++; if (val <= 0) { len++; } while (val) { val /= 10; len++; } } else { ptr->str = zval_get_string_func(tmp); len += ZSTR_LEN(ptr->str); ptr->lval = 1; ptr++; } } ZEND_HASH_FOREACH_END(); /* numelems can not be 0, we checked above */ str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(glue), len, 0); cptr = ZSTR_VAL(str) + ZSTR_LEN(str); *cptr = 0; while (1) { ptr--; if (EXPECTED(ptr->str)) { cptr -= ZSTR_LEN(ptr->str); memcpy(cptr, ZSTR_VAL(ptr->str), ZSTR_LEN(ptr->str)); if (ptr->lval) { zend_string_release_ex(ptr->str, 0); } } else { char *oldPtr = cptr; char oldVal = *cptr; cptr = zend_print_long_to_buf(cptr, ptr->lval); *oldPtr = oldVal; } if (ptr == strings) { break; } cptr -= ZSTR_LEN(glue); memcpy(cptr, ZSTR_VAL(glue), ZSTR_LEN(glue)); } free_alloca(strings, use_heap); RETURN_NEW_STR(str); }
static void su_php_implode(const zend_string *glue, zval *pieces, zval *return_value)
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pieces), tmp) { if (EXPECTED(Z_TYPE_P(tmp) == IS_STRING)) { // ... } else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_LONG)) { // ... } else { // ... } } ZEND_HASH_FOREACH_END();
else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_ARRAY))
,其具體內(nèi)容如下:ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pieces), tmp) { if (EXPECTED(Z_TYPE_P(tmp) == IS_STRING)) { // ... } else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_LONG)) { // ... } else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_ARRAY)) { // 如果值是數(shù)組,則調(diào)用 php_implode,將其使用 glue 連接成字符串 cptr = ZSTR_VAL(ptr->str); zend_string* str2 = origin_php_implode(glue, tmp, tmp_val); ptr->str = str2; // 此時(shí),要拿到拼接后的字符串長度 len += ZSTR_LEN(str2); ptr++; } else { // ... } } ZEND_HASH_FOREACH_END();
origin_php_implode
:static zend_string* origin_php_implode(const zend_string *glue, zval *pieces, zval *return_value) { zval *tmp; int numelems; zend_string *str; char *cptr; size_t len = 0; struct { zend_string *str; zend_long lval; } *strings, *ptr; ALLOCA_FLAG(use_heap) numelems = zend_hash_num_elements(Z_ARRVAL_P(pieces)); if (numelems == 0) { RETURN_EMPTY_STRING(); } else if (numelems == 1) { /* loop to search the first not undefined element... */ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pieces), tmp) { RETURN_STR(zval_get_string(tmp)); } ZEND_HASH_FOREACH_END(); } ptr = strings = do_alloca((sizeof(*strings)) * numelems, use_heap); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pieces), tmp) { if (EXPECTED(Z_TYPE_P(tmp) == IS_STRING)) { ptr->str = Z_STR_P(tmp); len += ZSTR_LEN(ptr->str); ptr->lval = 0; ptr++; } else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_LONG)) { zend_long val = Z_LVAL_P(tmp); ptr->str = NULL; ptr->lval = val; ptr++; if (val <= 0) { len++; } while (val) { val /= 10; len++; } } else { ptr->str = zval_get_string_func(tmp); len += ZSTR_LEN(ptr->str); ptr->lval = 1; ptr++; } } ZEND_HASH_FOREACH_END(); /* numelems can not be 0, we checked above */ str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(glue), len, 0); cptr = ZSTR_VAL(str) + ZSTR_LEN(str); *cptr = 0; while (1) { ptr--; if (EXPECTED(ptr->str)) { cptr -= ZSTR_LEN(ptr->str); memcpy(cptr, ZSTR_VAL(ptr->str), ZSTR_LEN(ptr->str)); if (ptr->lval) { zend_string_release_ex(ptr->str, 0); } } else { char *oldPtr = cptr; char oldVal = *cptr; cptr = zend_print_long_to_buf(cptr, ptr->lval); *oldPtr = oldVal; } if (ptr == strings) { break; } cptr -= ZSTR_LEN(glue); memcpy(cptr, ZSTR_VAL(glue), ZSTR_LEN(glue)); } free_alloca(strings, use_heap); // RETURN_NEW_STR(str); return str; }
PHP_FUNCTION(su_test)
,功能實(shí)現(xiàn)的差不多了。我們?nèi)ゾ幾g看看:./configure sudo make sudo make install
$res = su_test('-', [ 2019, '01', '01', ['1', '2',], ]); var_dump($res);
string(14) "2019-01-01-1-2"
關(guān)于PHP源碼之implode函數(shù)源碼怎么用就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。