explode和implode函數(shù)主要用作字符串和數(shù)組間轉(zhuǎn)換的操作体斩,比如獲取一段參數(shù)后根據(jù)某個字符分割字符串,或者將一個數(shù)組的結(jié)果使用一個字符合并成一個字符串輸出颖低。在PHP中經(jīng)常會用到這兩個函數(shù)絮吵,因此有必要了解一下其原理。
explode
array explode ( string $delimiter, string $string, [ , $limit ] )
函數(shù)返回由字符串組成的數(shù)組忱屑,每個元素都是string的一個子串蹬敲,被字符串$delimiter作為邊界點分割出來扼褪。
參數(shù)說明
limit
如果設置了limit,且為正數(shù)粱栖,則返回的數(shù)組最多包含limit個元素话浇,最后的那個元素將包含string的剩余部分。
如果limit是負數(shù)闹究,則返回除了最后的-$limit個元素外的所有元素幔崖。
如果limit是0,則會被當做1渣淤。
delimiter
如果delimiter為空赏寇,則函數(shù)返回FALSE。如果delimiter不在string中价认,且limit為負數(shù)嗅定,則返回空數(shù)組。
運行示例
$str = 'hello,world,heiheihei,php';
先來看看不設置limit的情況
$arr = explode(',', $str);
print_r($arr);
limit為正數(shù)時用踩,limit設為1渠退,最多返回1個元素。
$arr = explode(',', $str, 1);
print_r($arr);
limit為負數(shù)脐彩,limit為-1碎乃,返回最后的1個元素外的所有元素。
$arr = explode(',', $str, -1);
print_r($arr);
limit為0惠奸,當作1處理梅誓。
$arr = explode(',', $str, 0);
print_r($arr);
explode執(zhí)行步驟
1、接收參數(shù)佛南,處理參數(shù)為空的情況
2梗掰、創(chuàng)建函數(shù)中使用的局部變量
3、根據(jù)limit的值調(diào)用不同的函數(shù)分隔字符串
explode函數(shù)的核心實現(xiàn)是php_explode函數(shù)嗅回,下面是該函數(shù)的執(zhí)行流程圖:
php_explode函數(shù)核心代碼:
if (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);
}
源碼解讀
sizeof("") == 0。sizeof有兩種用法妈拌,sizeof(typename)和sizeof(expression)拥坛,當參數(shù)為typename是,即類型名稱尘分,sizeof返回類型對應對象的大胁峦铩;當參數(shù)為表達式時培愁,sizeof計算表達式的返回類型對應對象的大小著摔。此處,""是表達式定续,sizeof計算編譯時編譯器分配給""的空間谍咆,此時要算上\0的長度禾锤,因此是1,而strlen函數(shù)不會計算\0
摹察。
如果不設置limit恩掷,limit的默認值是LONG_MAX。在php.h文件中供嚎,LONG_MAX定義為2147483647L黄娘。
在實現(xiàn)里面,如果limit大于1克滴,則調(diào)用php_explode函數(shù)逼争;如果limit小于0,則調(diào)用php_explode_negative_limit函數(shù)劝赔;如果limit等于0誓焦,則被當做1處理,此時調(diào)用add_index_stringl函數(shù)將str添加到數(shù)組return_value中着帽。
在查找分隔符delimiter時杂伟,調(diào)用了php_memnstr函數(shù)
php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp);
而php_memnstr是zend_memnstr的宏定義,zend_memnstr實現(xiàn)里面启摄,因此實際上是調(diào)用了C里面的memchr來查找字符delimiter稿壁。
找到分隔符的位置之后幽钢,就調(diào)用add_next_index_stringl函數(shù)將分隔得到的字符串插入到返回數(shù)組里歉备。
implode
string implode ( string $glue, array $pieces )
string implode ( array $pieces )
將一個一維數(shù)組的值轉(zhuǎn)換為字符串
參數(shù)說明
implode函數(shù)可以接收兩種參數(shù)順序。另外匪燕,如果第一個參數(shù)為數(shù)組而第二個參數(shù)為空蕾羊,則第二個參數(shù)為默認值''。此函數(shù)可以看作是explode的逆向過程帽驯。
當然龟再,使用文檔規(guī)定的順序可避免混淆。
運行示例
$arr = array('hello', 'world');
按照文檔順序參數(shù)
$str = implode('-‘, $arr);// 輸出"hello-world"
第一個參數(shù)為數(shù)組
$str = implode($arr); // 輸出"helloworld"
$str = implode($arr, '-'); // 輸出"hello-world"
implode執(zhí)行步驟
1尼变、接收參數(shù)并賦值
2利凑、如果第二個參數(shù)為空,則判斷第一個參數(shù)的類型是否為數(shù)組嫌术,如果不是哀澈,則報錯。否則度气,則使用""對glue賦值割按,使用其作為連接符。
3磷籍、如果第二個參數(shù)不為空适荣,那么现柠,如果第一個參數(shù)是數(shù)組類型,則將第二個參數(shù)轉(zhuǎn)換成字符串類型;否則佣耐,如果第二個參數(shù)是數(shù)組類型砍聊,則將第一個參數(shù)轉(zhuǎn)換成字符串類型。
4废恋、調(diào)用php_implode函數(shù)做字符串的連接。
在implode函數(shù)設置完參數(shù)之后扒寄,底層就調(diào)用php_implode函數(shù)進行字符串連接鱼鼓,php_implode函數(shù)的執(zhí)行流程圖如下:
php_implode函數(shù)核心代碼:
// 遍歷數(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);
}
// 在尾部添加結(jié)束字符0
smart_str_0(&implstr);
源碼解讀
php_implode會逐個獲取數(shù)組里面的內(nèi)容迄本,然后判斷每個元素的類型,再做必要的數(shù)據(jù)類型轉(zhuǎn)換之后课竣,調(diào)用smart_str_appendl函數(shù)將值追加到返回的字符串后面嘉赎。最后,還要在字符串后面加上結(jié)束符于樟,這是個必須的操作公条,以后編程時也應注意。
smart_str_appendl是函數(shù)smart_str_appendl_ex的宏定義迂曲,該函數(shù)調(diào)用了memcpy做字符串的復制靶橱。
小結(jié)
暫且寫這么多,還有更多的優(yōu)化和PHP源碼中常用的函數(shù)路捧,將會在以后的源碼閱讀中慢慢講述关霸。
原創(chuàng)文章,文筆有限杰扫,才疏學淺队寇,文中若有不正之處,萬望告知章姓。
如果本文對你有幫助佳遣,請點下推薦吧,謝謝_
我在github有對PHP源碼更詳細的注解凡伊。感興趣的可以圍觀一下零渐,給個star。PHP5.4源碼注解窗声∠嗍眩可以通過commit記錄查看已添加的注解。