当我们需要将一个数组根据某个字符或字串进行分割成数组的时候,explode()函数很好用,但是你知道explode()是怎么工作的么?截取字串的问题,都会避免不了重新分配空间的消耗,explode也是会分配空间的,毫无疑问。
//文件1:ext/standard/string.c //先来看下explode的源代码 PHP_FUNCTION(explode) { char *str, *delim; int str_len = 0, delim_len = 0; long limit = LONG_MAX; /* No limit */ zval zdelim, zstr; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &delim, &delim_len, &str, &str_len, &limit) == FAILURE) { return; } if (delim_len == 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter"); RETURN_FALSE; } //这里会开辟一个数组,用来存放分割后的数据 array_init(return_value); //因为这个,我们用explode('|', '');成为了合法的 if (str_len == 0) { if (limit >= 0) { add_next_index_stringl(return_value, "", sizeof("") - 1, 1); } return; } //下面这两个是将原字串和分割符都构建成_zval_struct 结构, //ZVAL_STRINGL会分配空间哦~~源代码随后贴出 ZVAL_STRINGL(&zstr, str, str_len, 0); ZVAL_STRINGL(&zdelim, delim, delim_len, 0); //limit值是explode中允许传递的explode的第三个参数,它允许正负 if (limit > 1) { php_explode(&zdelim, &zstr, return_value, limit); } else if (limit < 0) { php_explode_negative_limit(&zdelim, &zstr, return_value, limit); } else { add_index_stringl(return_value, 0, str, str_len, 1); } }
再来看一段:
//ZVAL_STRINGL的源代码: //文件2:zend/zend_API.c #define ZVAL_STRINGL(z, s, l, duplicate) { \ const char *__s=(s); int __l=l; \ Z_STRLEN_P(z) = __l; \ Z_STRVAL_P(z) = (duplicate?estrndup(__s, __l):(char*)__s);\ Z_TYPE_P(z) = IS_STRING; \ } .... //estrndup才是主菜: //文件3:zend/zend_alloc.h #define estrndup(s, length) _estrndup((s), (length) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) .... //_estrndup的实现: zend/zend_alloc.c ZEND_API char *_estrndup(const char *s, uint length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { char *p; p = (char *) _emalloc(length+1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); if (UNEXPECTED(p == NULL)) { return p; } memcpy(p, s, length); //分配空间 p[length] = 0; return p; } //另外在substr和strrchr strstr中用到的ZVAL_STRING也是使用了上诉的实现
下面根据explode的第三个参数limit来分析调用:条件对应的是explode中最后的三行,对limit条件的不同。注: limit在缺省的时候(没有传递),他的默认值是LONG_MAX,也就是属于分支1的情况。
1、limit > 1 :
调用php_explode方法,该方法也可以在ext/standard/string.c中找到,并且是紧接着explode实现的上面出现(所以在查找本函数中调用来自本文件的方法的时候很方便,几乎无一列外都是在该函数的紧接着的上面^_^)。
PHPAPI void php_explode(zval *delim, zval *str, zval *return_value, long limit) { char *p1, *p2, *endp; //先得到的是源字串的末尾位置的指针 endp = Z_STRVAL_P(str) + Z_STRLEN_P(str); //记录开始位置 p1 = Z_STRVAL_P(str); //下面这个是获得分割符在str中的位置,可以看到在strrpos和strpos中也用到了这个方法去定位 p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp); if (p2 == NULL) { //因为这个,所以当我们调用explode('|', 'abc');是合法的,出来的的就是array(0 => 'abc') add_next_index_stringl(return_value, p1, Z_STRLEN_P(str), 1); } else { //依次循环获得下一个分隔符的位置,直到结束 do { //将得到的子字串(上个位置到这个位置中间的一段,第一次的时候上个位置就是开始 add_next_index_stringl(return_value, p1, p2 - p1, 1); //定位到分隔符位置p2+分隔符的长度的位置 //比如,分隔符='|', 原字串= ’ab|c', p2 = 2, 则p1=2+1=3 p1 = p2 + Z_STRLEN_P(delim); } while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL && --limit > 1); //将最后的一个分隔符后面的字串放到结果数组中 //explode('|', 'avc|sdf'); => array(0 => 'avc', 1= > 'sdf') if (p1 <= endp) add_next_index_stringl(return_value, p1, endp-p1, 1); } }
2、limit
调用php_explode_negative_limit方法:
PHPAPI void php_explode_negative_limit(zval *delim, zval *str, zval *return_value, long limit) { #define EXPLODE_ALLOC_STEP 64 char *p1, *p2, *endp; endp = Z_STRVAL_P(str) + Z_STRLEN_P(str); p1 = Z_STRVAL_P(str); p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp); if (p2 == NULL) { //它这里竟然没有处理,那explode('|', 'abc', -1) 就成非法的了,获得不了任何值 /* do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0 by doing nothing we return empty array */ } else { int allocated = EXPLODE_ALLOC_STEP, found = 0; long i, to_return; char **positions = emalloc(allocated * sizeof(char *)); //注意这里的positions的声明,这个数组是用来保存所有子字串的读取位置 positions[found++] = p1; //当然起始位置还是需要保存 //下面两个循环,第一个是循环所有在字符串中出现的分隔符位置,并保存下一个子字串读取位置起来 do { if (found >= allocated) { allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */ positions = erealloc(positions, allocated*sizeof(char *)); } 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 = limit + found; /* limit is at least -1 therefore no need of bounds checking : i will be always less than found */ for (i = 0;i < to_return;i++) { /* this checks also for to_return > 0 */ add_next_index_stringl(return_value, positions[i], (positions[i+1] - Z_STRLEN_P(delim)) - positions[i], 1 ); } efree(positions);//很重要,释放内存 } #undef EXPLODE_ALLOC_STEP }
3、limit = 1 or limit = 0 :
当所有第一和第二条件都不满足的时候,就进入的这个分支,这个分支很简单就是将源字串放到输出数组中,explode('|', 'avc|sd', 1) or explode('|', 'avc|sd', 0) 都将返回array(0 => 'avc|sd');
//add_index_stringl源代码 //文件4:zend/zend_API.c 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); } //zend_hash_next_index_insert //zend/zend_hash.h #define zend_hash_next_index_insert(ht, pData, nDataSize, pDest) \ _zend_hash_index_update_or_next_insert(ht, 0, pData, nDataSize, pDest, HASH_NEXT_INSERT ZEND_FILE_LINE_CC) //zend/zend_hash.c ///太长了~~~~不贴了
可见(不包含分配空间这些),当limit>1的时候,效率是O(N)【N为limit值】,当limit

PHP在现代编程中仍然是一个强大且广泛使用的工具,尤其在web开发领域。1)PHP易用且与数据库集成无缝,是许多开发者的首选。2)它支持动态内容生成和面向对象编程,适合快速创建和维护网站。3)PHP的性能可以通过缓存和优化数据库查询来提升,其广泛的社区和丰富生态系统使其在当今技术栈中仍具重要地位。

在PHP中,弱引用是通过WeakReference类实现的,不会阻止垃圾回收器回收对象。弱引用适用于缓存系统和事件监听器等场景,需注意其不能保证对象存活,且垃圾回收可能延迟。

\_\_invoke方法允许对象像函数一样被调用。1.定义\_\_invoke方法使对象可被调用。2.使用$obj(...)语法时,PHP会执行\_\_invoke方法。3.适用于日志记录和计算器等场景,提高代码灵活性和可读性。

Fibers在PHP8.1中引入,提升了并发处理能力。1)Fibers是一种轻量级的并发模型,类似于协程。2)它们允许开发者手动控制任务的执行流,适合处理I/O密集型任务。3)使用Fibers可以编写更高效、响应性更强的代码。

PHP社区提供了丰富的资源和支持,帮助开发者成长。1)资源包括官方文档、教程、博客和开源项目如Laravel和Symfony。2)支持可以通过StackOverflow、Reddit和Slack频道获得。3)开发动态可以通过关注RFC了解。4)融入社区可以通过积极参与、贡献代码和学习分享来实现。

PHP和Python各有优势,选择应基于项目需求。1.PHP适合web开发,语法简单,执行效率高。2.Python适用于数据科学和机器学习,语法简洁,库丰富。

PHP不是在消亡,而是在不断适应和进化。1)PHP从1994年起经历多次版本迭代,适应新技术趋势。2)目前广泛应用于电子商务、内容管理系统等领域。3)PHP8引入JIT编译器等功能,提升性能和现代化。4)使用OPcache和遵循PSR-12标准可优化性能和代码质量。

PHP的未来将通过适应新技术趋势和引入创新特性来实现:1)适应云计算、容器化和微服务架构,支持Docker和Kubernetes;2)引入JIT编译器和枚举类型,提升性能和数据处理效率;3)持续优化性能和推广最佳实践。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

禅工作室 13.0.1
功能强大的PHP集成开发环境

WebStorm Mac版
好用的JavaScript开发工具

螳螂BT
Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

SublimeText3 Linux新版
SublimeText3 Linux最新版

记事本++7.3.1
好用且免费的代码编辑器