[PHP源码阅读]explode和implode函数
explode和implode函数主要用作字符串和数组间转换的操作,比如获取一段参数后根据某个字符分割字符串,或者将一个数组的结果使用一个字符合并成一个字符串输出。在PHP中经常会用到这两个函数,因此有必要了解一下其原理。
我在github有对PHP源码更详细的注解。感兴趣的可以围观一下,给个star。PHP5.4源码注解。可以通过commit记录查看已添加的注解。
explode
array explode ( string $delimiter, string $string, [ , $limit ] )
函数返回由字符串组成的数组,每个元素都是string的一个子串,被字符串$delimiter作为边界点分割出来。
参数说明
limit
如果设置了$limit,且为正数,则返回的数组最多包含$limit个元素,最后的那个元素将包含$string的剩余部分。
如果$limit是负数,则返回除了最后的-$limit个元素外的所有元素。
如果$limit是0,则会被当做1。
delimiter
如果$delimiter为空,则函数返回FALSE。如果delimiter不在string中,且$limit为负数,则返回空数组。
运行示例
$str = 'hello,world,heiheihei,php';
先来看看不设置limit的情况
$arr = explode(',', $str); print_r($arr);
limit为正数时,limit设为1,最多返回1个元素。
$arr = explode(',', $str, 1); print_r($arr);
limit为负数,limit为-1,返回最后的1个元素外的所有元素。
$arr = explode(',', $str, -1); print_r($arr);
limit为0,当作1处理。
$arr = explode(',', $str, 0); print_r($arr);
explode执行步骤
1、接收参数,处理参数为空的情况
2、创建函数中使用的局部变量
3、根据limit的值调用不同的函数分隔字符串
explode函数的核心实现是php_explode函数,下面是该函数的执行流程图:
php_explode函数核心代码:
if (p2 == NULL) { // 找不到分隔符,直接返回整个字符串 add_next_index_stringl(return_value, p1, Z_STRLEN_P(str), ); } else { do { // 将p1添加到return_value数组中 add_next_index_stringl(return_value, p1, p2 - p1, ); p1 = p2 + Z_STRLEN_P(delim); } while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL && --limit > ); // 将最后一个值添加到return_value if (p1 <= endp) add_next_index_stringl(return_value, p1, endp-p1, ); }
源码解读
sizeof("") == 0。sizeof有两种用法,sizeof(typename)和sizeof(expression),当参数为typename是,即类型名称,sizeof返回类型对应对象的大小;当参数为表达式时,sizeof计算表达式的返回类型对应对象的大小。此处,""是表达式,sizeof计算编译时编译器分配给""的空间,此时要算上\0的长度,因此是1,而strlen函数不会计算\0。
如果不设置limit,limit的默认值是LONG_MAX。在php.h文件中,LONG_MAX定义为2147483647L。
在实现里面,如果limit大于1,则调用php_explode函数;如果limit小于0,则调用php_explode_negative_limit函数;如果limit等于0,则被当做1处理,此时调用add_index_stringl函数将str添加到数组return_value中。
在查找分隔符delimiter时,调用了php_memnstr函数
php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp);
而php_memnstr是zend_memnstr的宏定义,zend_memnstr实现里面,因此实际上是调用了C里面的memchr来查找字符delimiter。
找到分隔符的位置之后,就调用add_next_index_stringl函数将分隔得到的字符串插入到返回数组里。
implode
string implode ( string $glue, array $pieces )
string implode ( array $pieces )
将一个一维数组的值转换为字符串
参数说明
implode函数可以接收两种参数顺序。另外,如果第一个参数为数组而第二个参数为空,则第二个参数为默认值''。此函数可以看作是explode的逆向过程。
当然,使用文档规定的顺序可避免混淆。
运行示例
$arr = array('hello', 'world');
按照文档顺序参数
$str = implode('-‘, $arr);// 输出"hello-world"
第一个参数为数组
$str = implode($arr); // 输出"helloworld" $str = implode($arr, '-'); // 输出"hello-world"
implode执行步骤
1、接收参数并赋值
2、如果第二个参数为空,则判断第一个参数的类型是否为数组,如果不是,则报错。否则,则使用""对glue赋值,使用其作为连接符。
3、如果第二个参数不为空,那么,如果第一个参数是数组类型,则将第二个参数转换成字符串类型;否则,如果第二个参数是数组类型,则将第一个参数转换成字符串类型。
4、调用php_implode函数做字符串的连接。
在implode函数设置完参数之后,底层就调用php_implode函数进行字符串连接,php_implode函数的执行流程图如下:
php_implode函数核心代码:
// 遍历数组的每一个元素,判断其类型,然后调用smart_str_appendl函数将值追加到字符串中 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: { ]; str_len = slprintf(stmp, sizeof(stmp), "%ld", Z_LVAL_PP(tmp)); smart_str_appendl(&implstr, stmp, str_len); } break; case IS_BOOL: ) { smart_str_appendl(&implstr, ); } break; case IS_NULL: break; case IS_DOUBLE: { char *stmp; str_len = spprintf(&stmp, , "%.*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会逐个获取数组里面的内容,然后判断每个元素的类型,再做必要的数据类型转换之后,调用smart_str_appendl函数将值追加到返回的字符串后面。最后,还要在字符串后面加上结束符,这是个必须的操作,以后编程时也应注意。
smart_str_appendl是函数smart_str_appendl_ex的宏定义,该函数调用了memcpy做字符串的复制。
小结
暂且写这么多吧,还有更多的优化和PHP源码中常用的函数,将会在以后的源码阅读中慢慢讲述。
原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。
如果本文对你有帮助,请点下推荐吧,谢谢^_^
最后再安利一下,我在github有对PHP源码更详细的注解。感兴趣的可以围观一下,给个star。PHP5.4源码注解。可以通过commit记录查看已添加的注解。
更多源码文章,欢迎访问个人主页继续查阅:hoohack
[PHP源码阅读]explode和implode函数的更多相关文章
- CI框架源码阅读笔记3 全局函数Common.php
从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap ...
- [PHP源码阅读]array_pop和array_shift函数
上篇文章介绍了PHP添加元素到数组的函数,那么当然有从数组中删除元素.array_pop和array_shift只从数组的头或尾删除一个元素.经过阅读源码,发现这两个函数的实现都是调用了同一个函数-- ...
- [PHP源码阅读]array_push和array_unshift函数
在PHP中,在数组中添加元素也是一种很常用的操作,分别有在数组尾部和头部添加元素,看看PHP内部是如何实现数组插入的操作. 我在github有对PHP源码更详细的注解.感兴趣的可以围观一下,给个sta ...
- [PHP源码阅读]empty和isset函数
近日被问到PHP中empty和isset函数时怎么判断变量的,刚开始我是一脸懵逼的,因为我自己也只是一知半解,为了弄懂其真正的原理,赶紧翻开源码研究研究.经过分析可发现两个函数调用的都是同一个函数,因 ...
- PHP源码阅读(一):str_split函数
注:源码版本:php5.6.33. 函数简介 str_split 原型: array str_split ( string $string [, int $split_length = 1 ] ) 说 ...
- [PHP源码阅读]array_slice和array_splice函数
array_slice和array_splice函数是用在取出数组的一段切片,array_splice还有用新的切片替换原删除切片位置的功能.类似javascript中的Array.prototype ...
- [PHP源码阅读]strtolower和strtoupper函数
字符串的操作函数中,字符串的大小写转换也算是比较常用的函数,其底层实现也比较简单,下面来一探究竟. 我在github上有对PHP源码更详细的注解.感兴趣的可以围观一下,给个star.PHP5.4源码注 ...
- ONNX Runtime 源码阅读:Graph::SetGraphInputsOutputs() 函数
目录 前言 正文 总结 前言 为了深入理解ONNX Runtime的底层机制,本文将对 Graph::SetGraphInputsOutputs() 的代码逐行分析. 正文 首先判断Graph是否从O ...
- CI框架源码阅读笔记4 引导文件CodeIgniter.php
到了这里,终于进入CI框架的核心了.既然是“引导”文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http://you.host.c ...
随机推荐
- 配置android sdk 环境
1:下载adnroid sdk安装包 官方下载地址无法打开,没有vpn,使用下面这个地址下载,地址:http://www.android-studio.org/
- Database Replay和Consolidated Database replay
简介 在数据库的迁移和升级场景中,我们经常会遇到一个问题:在做压力测试时,如何模拟真实的业务压力,解决这个问题的方法有很多,比如:应用方开发模拟程序或者使用压力测试工具模拟,如load runner, ...
- ASP.NET Core 1.1.0 Release Notes
ASP.NET Core 1.1.0 Release Notes We are pleased to announce the release of ASP.NET Core 1.1.0! Antif ...
- WCF学习之旅—第三个示例之四(三十)
上接WCF学习之旅—第三个示例之一(二十七) WCF学习之旅—第三个示例之二(二十八) WCF学习之旅—第三个示例之三(二十九) ...
- Bringing Whoops Back to Laravel 5
You might be missing the "prettier" Whoops error handler from Laravel 4. If so, here's how ...
- css常用hack
原文地址:css常用hack 突然想起今天早上在CNZZ看到的统计数据,使用IE6.7的用户比例还真多,看到之后我的心都碎了.微软都放弃了为毛还有这么多人不死心? 所以说,IE下的兼容还是得做的. – ...
- 由js apply与call方法想到的js数据类型(原始类型和引用类型)
原文地址:由js apply与call方法想到的js数据类型(原始类型和引用类型) js的call方法与apply方法的区别在于第二个参数的不同,他们都有2个参数,第一个为对象(即需要用对象a继承b, ...
- 用MongoDB分析合肥餐饮业
看了<从数据角度解析福州美食>后难免心痒,动了要分析合肥餐饮业的念头,因此特地写了Node.js爬虫爬取了合肥的大众点评数据.分析数据库我并没有采用MySQL而是用的MongoDB,是因为 ...
- Autofac - 生命周期
实例生命周期决定在同一个服务的每个请求的实例是如何共享的. 当请求一个服务的时候,Autofac会返回一个单例 (single instance作用域), 一个新的对象 (per lifetime作用 ...
- 用angular怎么缓存父页面数据
angular做单页面应用是一个比较好的框架,但是它有一定的入门难度,对于新手来说可能会碰到很多坑,也有许多难题,大部分仔细看文档,找社区是能解决的. 但有些问题也许资料比较少,最近遇到过一个要缓存父 ...