[PHP源碼閱讀]explode和implode函數(shù)

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);
運行結(jié)果1

limit為正數(shù)時用踩,limit設為1渠退,最多返回1個元素。

$arr = explode(',', $str, 1);
print_r($arr);
運行結(jié)果2

limit為負數(shù)脐彩,limit為-1碎乃,返回最后的1個元素外的所有元素。

$arr = explode(',', $str, -1);
print_r($arr);
運行結(jié)果3

limit為0惠奸,當作1處理梅誓。

$arr = explode(',', $str, 0);
print_r($arr);
運行結(jié)果4

explode執(zhí)行步驟

1、接收參數(shù)佛南,處理參數(shù)為空的情況

2梗掰、創(chuàng)建函數(shù)中使用的局部變量

3、根據(jù)limit的值調(diào)用不同的函數(shù)分隔字符串

explode函數(shù)的核心實現(xiàn)是php_explode函數(shù)嗅回,下面是該函數(shù)的執(zhí)行流程圖:

explode流程

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í)行流程圖如下:

implode流程

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, &copy);
                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記錄查看已添加的注解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拦耐,隨后出現(xiàn)的幾起案子耕腾,更是在濱河造成了極大的恐慌,老刑警劉巖杀糯,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扫俺,死亡現(xiàn)場離奇詭異,居然都是意外死亡固翰,警方通過查閱死者的電腦和手機狼纬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骂际,“玉大人疗琉,你說我怎么就攤上這事∏嘎粒” “怎么了盈简?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長太示。 經(jīng)常有香客問我柠贤,道長,這世上最難降的妖魔是什么类缤? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任臼勉,我火速辦了婚禮,結(jié)果婚禮上餐弱,老公的妹妹穿的比我還像新娘宴霸。我一直安慰自己,他們只是感情好岸裙,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布猖败。 她就那樣靜靜地躺著,像睡著了一般降允。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上艺糜,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天剧董,我揣著相機與錄音,去河邊找鬼破停。 笑死翅楼,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的真慢。 我是一名探鬼主播毅臊,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼黑界!你這毒婦竟也來了管嬉?” 一聲冷哼從身側(cè)響起皂林,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚯撩,沒想到半個月后础倍,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡胎挎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年沟启,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片犹菇。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡德迹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出揭芍,到底是詐尸還是另有隱情浦辨,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布沼沈,位于F島的核電站流酬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏列另。R本人自食惡果不足惜芽腾,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望页衙。 院中可真熱鬧摊滔,春花似錦、人聲如沸店乐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽眨八。三九已至腺兴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間廉侧,已是汗流浹背页响。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留段誊,地道東北人闰蚕。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像连舍,于是被迫代替她去往敵國和親没陡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容