explode和implode函數(shù)主要用作字符串-數(shù)組的操作,比如獲取一段參數(shù)后根據(jù)某個字符分割字符串,或者將一個數(shù)組的結(jié)果使用一個字符合并成一個字符串輸出。在PHP中經(jīng)常會用到這兩個函數(shù),因此有必要了解一下其原理。
explodearray explode ( string $delimiter, string $string, [ , $limit ] )
返回由字符串組成的數(shù)組,每個元素都是string的一個子串,被字符串$delimiter作為邊界點分割出來。
$limit如果設(shè)置了$limit,且為正數(shù),則返回的數(shù)組最多包含$limit個元素,最后的那個元素將包含$string的剩余部分。
如果$limit是負數(shù),則返回除了最后的-$limit個元素外的所有元素。
如果$limit是0,則會被當做1。
$delimiter如果$delimiter為空,則函數(shù)返回FALSE。如果delimiter不在string中,且$limit為負數(shù),則返回空數(shù)組。
核心源碼// 如果delimiter為空字符串,返回FALSE if (delim_len == 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, 'Empty delimiter'); RETURN_FALSE; } // 初始化返回的數(shù)組 array_init(return_html' target='_blank'>value); if (str_len == 0) { if (limit >= 0) { // 如果字符串為空且limit大于等于0,則返回一個包含空字符串的數(shù)組,注意此處sizeof('') == 1 add_next_index_stringl(return_value, '', sizeof('') - 1, 1); } return; } // 初始化zstr和zdelim的字符串變量 ZVAL_STRINGL(&zstr, str, str_len, 0); ZVAL_STRINGL(&zdelim, delim, delim_len, 0); if (limit > 1) { // limit大于1,limit默認是LONG_MAX php_explode(&zdelim, &zstr, return_value, limit); } else if (limit < 0) { // limit 為負數(shù) php_explode_negative_limit(&zdelim, &zstr, return_value, limit); } else { // limit為0,被當作1處理,返回整個字符串,add_index_stringl函數(shù)將str追加到數(shù)組return_value中 add_index_stringl(return_value, 0, str, str_len, 1); }
處理完特殊情況和初始化變量后,就調(diào)用php_explode/php_explode_negative_limit函數(shù)進行下一步處理。下面是php_explode函數(shù)的源碼
php_explodeendp = Z_STRVAL_P(str) + Z_STRLEN_P(str); // p1指向字符串的開始 p1 = Z_STRVAL_P(str); // p2指向第一個分隔符的位置 ,找出分隔符位置主要用的是php_memnstr函數(shù) p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp); if (p2 == NULL) { // p2為NULL表示找不到分隔符,直接返回整個字符串 add_next_index_stringl(return_value, p1, Z_STRLEN_P(str), 1); } else { do { // 將p1添加到return_value數(shù)組中 ,移動到下一個分隔符的位置 add_next_index_stringl(return_value, p1, p2 - p1, 1); p1 = p2 + Z_STRLEN_P(delim); } while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL && --limit > 1); // 將最后一個值追加到return_value if (p1 <= endp) add_next_index_stringl(return_value, p1, endp-p1, 1); }
實現(xiàn)時調(diào)用了add_next_index_stringl將得到的每個字符串添加到數(shù)組return_value中。add_next_index_string是此功能的核心函數(shù)。
ZEND_API int add_next_index_stringl(zval *arg, const char *str, uint length, int duplicate) { zval *tmp; MAKE_STD_ZVAL(tmp); ZVAL_STRINGL(tmp, str, length, duplicate); return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp, sizeof(zval *), NULL);}
add_next_index_stringl函數(shù)調(diào)用zend_hash_next_index_insert函數(shù)將str插入到數(shù)組中。再看看php_explode_negative_limit函數(shù)的源碼
php_explode_negative_limit// 如果delimiter不在string中,且limit為負數(shù),什么都不做,返回空的array,p2為NULL表示delimiter不在string中 if (p2 == NULL) { /* 如果limit <= -1,那么什么都不做,因此如果只有一個字符串,那么- 1 + (limit) <= 0 返回空數(shù)組*/ } else { int allocated = EXPLODE_ALLOC_STEP, found = 0; long i, to_return; char **positions = emalloc(allocated * sizeof(char *)); // 第一個單詞的位置 positions[found++] = p1; do { if (found >= allocated) { allocated = found + EXPLODE_ALLOC_STEP;/* 保證有足夠的內(nèi)存空間 */ positions = erealloc(positions, allocated*sizeof(char *)); } // positions保存每個單詞的起始位置 positions[found++] = p1 = p2 + Z_STRLEN_P(delim); } while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL); // to_return 是return_value的數(shù)量,其實等于found - |limit| to_return = limit + found; /* limit至少是-1,因此不需要邊界檢查:i永遠小于found */ for (i = 0;i < to_return;i++) { /* 這個檢查是檢查to_return大于0 */ add_next_index_stringl(return_value, positions[i], (positions[i+1] - Z_STRLEN_P(delim)) - positions[i], 1 ); } efree(positions); }
php_explode_negative_limit也是跟php_implode類似的操作,找到分隔的字符串之后,調(diào)用add_next_index_string函數(shù)將limit + found個字符串添加到return_value數(shù)組中。
implodestring implode ( string $glue, array $pieces )
string implode ( array $pieces )
將一個一維數(shù)組的值轉(zhuǎn)換為字符串
implode函數(shù)可以接收兩種參數(shù)順序。
核心代碼if (arg2 == NULL) { // 第二個參數(shù)為空,第一個參數(shù)必須為數(shù)組 if (Z_TYPE_PP(arg1) != IS_ARRAY) { php_error_docref(NULL TSRMLS_CC, E_WARNING, 'Argument must be an array'); return; } MAKE_STD_ZVAL(delim);#define _IMPL_EMPTY '' // 默認使用''連接 ZVAL_STRINGL(delim, _IMPL_EMPTY, sizeof(_IMPL_EMPTY) - 1, 0); SEPARATE_ZVAL(arg1); arr = *arg1; } else { // 根據(jù)參數(shù)類型設(shè)置參數(shù)的值 if (Z_TYPE_PP(arg1) == IS_ARRAY) { arr = *arg1; convert_to_string_ex(arg2); delim = *arg2; } else if (Z_TYPE_PP(arg2) == IS_ARRAY) { arr = *arg2; convert_to_string_ex(arg1); delim = *arg1; } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, 'Invalid arguments passed'); return; } } // 調(diào)用php_implode函數(shù)進行轉(zhuǎn)換 php_implode(delim, arr, return_value TSRMLS_CC);
在底層實現(xiàn)中,implode函數(shù)處理好參數(shù)之后就調(diào)用php_implode函數(shù)進行轉(zhuǎn)換。
php_implode// 遍歷數(shù)組的每一個元素,判斷其類型,然后調(diào)用smart_str_appendl函數(shù)將值追加到字符串中 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void **) &tmp, &pos) == SUCCESS) { switch ((*tmp)->type) { case IS_STRING: smart_str_appendl(&implstr, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); break; case IS_LONG: { char stmp[MAX_LENGTH_OF_LONG + 1]; str_len = slprintf(stmp, sizeof(stmp), '%ld', Z_LVAL_PP(tmp)); smart_str_appendl(&implstr, stmp, str_len); } break; case IS_BOOL: if (Z_LVAL_PP(tmp) == 1) { smart_str_appendl(&implstr, '1', sizeof('1')-1); } break; case IS_NULL: break; case IS_DOUBLE: { char *stmp; str_len = spprintf(&stmp, 0, '%.*G', (int) EG(precision), Z_DVAL_PP(tmp)); smart_str_appendl(&implstr, stmp, str_len); efree(stmp); } break; case IS_OBJECT: { int copy; zval expr; zend_make_printable_zval(*tmp, &expr, ©); smart_str_appendl(&implstr, Z_STRVAL(expr), Z_STRLEN(expr)); if (copy) { zval_dtor(&expr); } } break; default: tmp_val = **tmp; zval_copy_ctor(&tmp_val); convert_to_string(&tmp_val); smart_str_appendl(&implstr, Z_STRVAL(tmp_val), Z_STRLEN(tmp_val)); zval_dtor(&tmp_val); break; } // 添加glue字符 if (++i != numelems) { smart_str_appendl(&implstr, Z_STRVAL_P(delim), Z_STRLEN_P(delim)); } zend_hash_move_forward_ex(Z_ARRVAL_P(arr), &pos); } // 在尾部添加字符0 smart_str_0(&implstr);
可以看到,php_implode函數(shù)遍歷數(shù)組的每一個元素,判斷其類型,并進行必要的類型轉(zhuǎn)換,然后調(diào)用smart_str_appendl函數(shù)將值追加到字符串中。smart_str_appendl是implode實現(xiàn)代碼中的核心函數(shù)。
smart_str_appendl#define smart_str_appendl(dest, src, len) smart_str_appendl_ex((dest), (src), (len), 0)#define smart_str_appendl_ex(dest, src, nlen, what) do { register size_t __nl; smart_str *__dest = (smart_str *) (dest); smart_str_alloc4(__dest, (nlen), (what), __nl); memcpy(__dest->c + __dest->len, (src), (nlen)); __dest->len = __nl; } while (0)
smart_str_appendl_ex主要調(diào)用memcpy函數(shù)進行字符串復制。
原創(chuàng)文章 http://www.cnblogs.com/h-hq/p/5509090.html
PHP編程鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。
新聞熱點
疑難解答
圖片精選