PHP 中的 implode
- 正在 PHP 中,implode 的作用是:将一个一维数组的值转化为字符串。记住一维数组,假如是多维的,会发作甚么呢?正在本篇剖析中,会有所讨论。
- 现实上,经过民间的文档能够晓得,implode 有两种用法,经过函数署名能够看患上进去:
// 办法1 implode ( string $glue , array $pieces ) : string // 办法2 implode ( array $pieces ) : string
- 由于,正在没有传 glue 的时分,外部完成会默许空字符串。
- 经过一个简略的示例能够看出:
$pieces = [ 123, ',是一个', 'number!', ]; $str1 = implode($pieces); $str2 = implode('', $pieces); var_dump($str1, $str2); /* string(20) "123,是一个number!" string(20) "123,是一个number!" */
implode 源码完成
- 经过搜寻要害字
PHP_FUNCTION(implode)
能够找到,该函数界说于\ext\standard\string.c
文件中的 1288 行 - 一开端的几行是参数申明相干的信息。此中 *arg2 是用于接纳 pieces 参数的指针。
- 正在下方对 arg2 的判别中,假如 arg2 为空,则示意不传 pieces 对应的值
if (arg2 == NULL) { if (Z_TYPE_P(arg1) != IS_ARRAY) { php_error_docref(NULL, E_WARNING, "Argument must be an array"); return; } glue = ZSTR_EMPTY_ALLOC(); tmp_glue = NULL; pieces = arg1; } else { if (Z_TYPE_P(arg1) == IS_ARRAY) { glue = zval_get_tmp_string(arg2, &tmp_glue); pieces = arg1; } else if (Z_TYPE_P(arg2) == IS_ARRAY) { glue = zval_get_tmp_string(arg1, &tmp_glue); pieces = arg2; } else { php_error_docref(NULL, E_WARNING, "Invalid arguments passed"); return; } }
没有通报 pieces 参数
- 正在没有通报 pieces 参数的判别中,即
arg2 == NULL
,次要是对参数的一些解决 - 将 glue 初始化为空字符串,并将传出去的惟一的参数,赋值给 pieces 变量,接着就挪用
php_implode(glue, pieces, return_value);
非常要害的 php_implode
- 无论有无通报 pieces 参数,正在解决好参数后,终极城市挪用 PHPAPI 的相干函数 php_implode,可见,要害逻辑都是正在这个函数中完成的,那末咱们深化此中看一看它
- 正在挪用 php_implode 时,呈现了一个看起来不被申明的变量 return_value。没错,它仿佛就是平空呈现的
- 经过google搜寻
PHP源码中 return_value
,找到了谜底。 - 原来,这个变量是随同着宏 PHP_FUNCTION 而呈现的,而此处 implode 的完成就是经过
PHP_FUNCTION(implode)
来申明的。而 PHP_FUNCTION 的界说是:
#define PHP_FUNCTION ZEND_FUNCTION // 对应的 ZEND_FUNCTION 界说以下 #define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name)) // 对应的 ZEND_NAMED_FUNCTION 界说以下 #define ZEND_NAMED_FUNCTION(name) void ZEND_FASTCALL name(INTERNAL_FUNCTION_PARAMETERS) // 对应的 ZEND_FN 界说以下 #define ZEND_FN(name) zif_##name // 对应的 ZEND_FASTCALL 界说以下 # define ZEND_FASTCALL __attribute__((fastcall))
- (对于双井号,它起衔接符的作用,能够参考这里理解)
- 正在被预解决后,它的样子相似于下方所示:
void zif_implode(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC)
- 也就是说 return_value 是作为整个 implode 扩大函数界说的一个形参
- 正在 php_implode 的界说中,一开端,先界说了一些行将用到的变量,随后应用
ALLOCA_FLAG(use_heap)
进行标识,假如请求内存,则请求的是堆内存 - 经过
numelems = zend_hash_num_elements(Z_ARRVAL_P(pieces));
猎取 pieces 参数的单位数目,假如是空数组,则间接前往空字符串 - 此处另有判别,假如数组单位数为 1,则间接将惟一的单位作为字符串前往。
- 最初是解决少数组单位的状况,由于后面标识过,若请求内存则请求的是堆内存,堆内存绝对于栈来说,效率比拟低,以是只正在非用不成的情景下,才会请求堆内存,那此处的情景就是多单位数组的状况。
- 随后,针对 pieces 轮回,猎取其值进行拼接,正在源码中的 foreach 轮回是固定构造,以下:
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zend_array), tmp) { // ... } ZEND_HASH_FOREACH_END();
- 这类罕用写法我感觉,正在编写 PHP 扩大中是必不成少的吧。尽管我尚未编写过任何一个可用于消费环境的 PHP 扩大。但我正致力朝阿谁标的目的走呢!
正在轮回内,对数组单位分为三类:
- 字符串
- 整形数据
- 其它
- 现实上,正在轮回开端以前,源码中,先请求了一块内存,用于寄存上面的构造体,而且个数恰恰是 pieces 数组单位的个数。
struct { zend_string *str; zend_long lval; } *strings, *ptr;
- 能够看到,构造体成员蕴含 zend 字符串和 zend 整形数据。这个构造体的呈现,恰恰是为了寄存数组单位中的 zend 字符串/zend 整形数据。
字符串
- 先假定,pieces 数组单位中,都是字符串类型,此时轮回中执行的逻辑就是:
// tmp 是轮回中的单位值 ptr->str = Z_STR_P(tmp); len += ZSTR_LEN(ptr->str); ptr->lval = 0; ptr++;
- 此中,tmp 是轮回中的单位值。每一经验一次轮回,会将单位值放入构造体中,随落后行指针 +1 运算,指针就指向存储下一个构造体数据的地点:
- 而且,正在这时期,统计出了字符串的总长度
len += ZSTR_LEN(ptr->str);
整数类型
- 以上,探讨了数组单位中是字符串的状况。接上去看看,假如数组单位的类型是数值类型时会发作甚么?
- 判别一个变量能否是数值类型(实际上是 zend_long),通用办法是:
Z_TYPE_P(tmp) == IS_LONG
。一旦晓得以后的数据类型是 zend_long,则将其赋值给 ptr 的 lval 构造体成员。而后 ptr 指针后移一个单元长度。 - 然而,咱们晓得咱们不克不及像猎取 zend_string 的长度同样去猎取 zend_long 的字符长度。假如是 zend_string,则能够经过
len += ZSTR_LEN(val);
的形式猎取其字符长度。关于 zend_long,有甚么好的办法呢? - 正在源码中是经过对 10 做除了法运算,患上出后果的一局部,再缓缓的累加其长度:
while (val) { val /= 10; len++; }
- 假如是正数呢?不甚么特地的方法,间接判别解决:
if (val <= 0) { len++; }
字符串的解决以及拷贝
- 轮回完结后,ptr 就是指向这段内存的尾部的指针。
- 而后,请求了一段内存:
str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(glue), len, 0);
,用于寄存单位字符串总长度加之衔接字符的总长度,即(n-1)glue + len
。由于 n 个数组单位,只要要 n-1 个 glue 字符串。而后,将这段内存的尾地点,赋值给 cptr,为何要指向尾部呢?看下一局部,你就会明确了。 - 接上去,需求轮回掏出寄存正在 ptr 中的字符。咱们晓得,ptr 此时是所处内存区域的尾部,为了能有序展现衔接的字符串,源码中,是从后向前轮回解决。这也就是为何需求把 cptr 指向所正在内存区域的尾部的缘由。
- 进入轮回,进步前辈行
ptr--;
,而后针对 ptr->str 的判别if (EXPECTED(ptr->str))
,看了一下此处的 EXPECTED 的作用,能够参考这里。能够简略的将其了解一种汇编层面的优化,当实际执行的状况更倾向于以后前提下的分支而非 else 的分支时,就用 EXPECTED 宏将其包装起来:EXPECTED(ptr->str)
。我敢说,当你挪用 implode 通报的数组中都是数字而非字符串,那末这里的 EXPECTED 作用就会生效。 - 接上去的两行是比拟外围的:
cptr -= ZSTR_LEN(ptr->str); memcpy(cptr, ZSTR_VAL(ptr->str), ZSTR_LEN(ptr->str));
- cptr 的指针前移一个数组单位字符的长度,而后将
ptr->str
(某数组单位的值)经过 c 规范库函数 memcpy 拷贝到 cptr 内存空间中。 - 当
ptr == strings
餍足时,象征着 ptr 再也不有可被复制的字符串/数字。由于 strings 是 ptr 所正在区域的首地点。 - 经过下面,曾经胜利将一个数组单位的字符串拷贝到 cptr 对应的内存区域中,接上去若何解决 glue 呢?
- 只要要像解决
ptr->str
同样解决 glue 便可。至多源码中是这么做的。 - 代码中有一段是:
*cptr = 0
,它的作用相称于赋值空字符串。 - cptr 持续前移 glue 的长度,而后,将 glue 字符串拷贝到 cptr 对应的内存区域中。没错,仍是用 memcpy 函数。
- 到这里,第一次轮回完结了。我应该没有需求像实际轮回中那样形容这里的轮回吧?置信优秀的你,是齐全能够参考上方的形容脑补进去的 ^^
- 当然,解决前往的两句仍是要提一下:
free_alloca(strings, use_heap); RETURN_NEW_STR(str);
- strings 的那一片内存空间只是存储暂时值的,因而函数完结了,就必需跟 strings 说再会。咱们晓得 c 言语是手动治理内存的,不 GC,你要显示的开释内存,即
free_alloca(strings, use_heap);
。 - 正在下面的形容中,咱们只讲到了 cptr,但这里的前往值倒是 str。
- 不必嫌疑,这里是对的,咱们所讲的 cptr 那一片内存区域的首地点就是 str。并经过宏
RETURN_NEW_STR
会将终极的前往值写入 return_value 中
理论
- 为了可能愈加明晰 implode 源码中代码运转时的状况,接上去,咱们经过 PHP 扩大的形式对其进行 debug。正在这个进程中的代码,我都放正在 GitHub 的仓库中,分支名是
debug/implode
,可自行下载运转,看看成果。 - 新建 PHP 扩大模板的操作,能够参考这里。请确保操作完外面形容的步骤。
- 接上去,次要针对 su_dd.c 文件修正代码。为了能经过修正代码来看成果,将 php_implode 函数复制到扩大文件中,并将其定名为 su_php_implode:
static void su_php_implode(const zend_string *glue, zval *pieces, zval *return_value) { // 源码内容省略 }
- 正在扩大中新增一个扩大函数 su_test:
PHP_FUNCTION(su_test) { zval tmp; zend_string *str, *glue, *tmp_glue; zval *arg1, *arg2 = NULL, *pieces; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_ZVAL(arg1) Z_PARAM_OPTIONAL Z_PARAM_ZVAL(arg2) ZEND_PARSE_PARAMETERS_END(); glue = zval_get_tmp_string(arg1, &tmp_glue); pieces = arg2; su_php_implode(glue, pieces, return_value); }
- 由于扩大的编译和引入,后面的曾经说起。因而,此时只要编写 PHP 代码进行挪用:
// t1.php $res = su_test('-', [ 2019, '01', '01', ]); var_dump($res);
- PHP 运转该剧本,输入:
string(10) "2019-01-01"
,这象征着,你曾经胜利编写了一个扩大函数。别急,这只是迈出了第一步,别遗记咱们的指标:经过调试来学习 implode 源码。 - 接上去,咱们经过 gdb 对象,调试以上 PHP 代码正在源码层面的运转。为了避免初学者没有会用 gdb,这里就繁琐的写出这个进程。假如不装置 gdb,请自行google。
- 进步前辈入 PHP 剧本所正在门路。饬令行下:
gdb php b zval_get_tmp_string r t1.php
b
即 break,示意打一个断点r
即 run,示意运转剧本s
即 step,示意一步一步伐试,遇到办法挪用,会进入办法外部单步伐试n
即 next,示意一行一行调试。遇到办法,则调试间接略过间接执行前往,调试没有会进入其外部。p
即 print,示意打印以后作用域中的一个变量- 当运转完
r t1.php
,则会定位到第一个断点对应的行,显示以下:
Breakpoint 1, zif_su_test (execute_data=0x7ffff1a1d0c0, return_value=0x7ffff1a1d090) at /home/www/clang/php-7.3.3/ext/su_dd/su_dd.c:179 179 glue = zval_get_tmp_string(arg1, &tmp_glue);
- 此时,按下
n
,显示以下:
184 su_php_implode(glue, pieces, return_value);
- 此时,以后的作用域中存正在变量:
glue
,pieces
,return_value
- 咱们能够经过 gdb 调试,查看
pieces
的值。先应用饬令:p pieces
,此时正在终端会显示相似于以下内容:
$1 = (zval *) 0x7ffff1a1d120
- 标明
pieces
是一个 zval 类型的指针,0x7ffff1a1d120
是其地点,当然,你运转的时分对应的也是一个地点,只不外跟我的这个会没有太同样。 - 咱们持续应用
p
去打印存储于改地点的变量内容:p *$1
,$1
能够以为是一个暂时变量名,*
是取值运算符。运转完后,此时显示以下:
(gdb) p *$1 $2 = {value = {lval = 140737247576960, dval = 6.9533439118030153e-310, counted = 0x7ffff1a60380, str = 0x7ffff1a60380, arr = 0x7ffff1a60380, obj = 0x7ffff1a60380, res = 0x7ffff1a60380, ref = 0x7ffff1a60380, ast = 0x7ffff1a60380, zv = 0x7ffff1a60380, ptr = 0x7ffff1a60380, ce = 0x7ffff1a60380, func = 0x7ffff1a60380, ww = {w1 = 4054188928, w2 = 32767}}, u1 = {v = {type = 7 '\a', type_flags = 1 '\001', u = { call_info = 0, extra = 0}}, type_info = 263}, u2 = {next = 0, cache_slot = 0, opline_num = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, constant_flags = 0, extra = 0}}
- 打印的内容,看起来是一堆乱哄哄的字符,这其实是 zval 的构造体,此中的字段刚好是以及 zval 的成员逐个对应的,为了便于读者浏览,这里间接贴出 zval 的构造体信息:
struct _zval_struct { zend_value value; /* value */ union { struct { ZEND_ENDIAN_LOHI_3( zend_uchar type, /* active type */ zend_uchar type_flags, union { uint16_t call_info; /* call info for EX(This) */ uint16_t extra; /* not further specified */ } u) } v; uint32_t type_info; } u1; union { uint32_t next; /* hash collision chain */ uint32_t cache_slot; /* cache slot (for RECV_INIT) */ uint32_t opline_num; /* opline number (for FAST_CALL) */ uint32_t lineno; /* line number (for ast nodes) */ uint32_t num_args; /* arguments number for EX(This) */ uint32_t fe_pos; /* foreach position */ uint32_t fe_iter_idx; /* foreach iterator index */ uint32_t access_flags; /* class constant access flags */ uint32_t property_guard; /* single property guard */ uint32_t constant_flags; /* constant flags */ uint32_t extra; /* not further specified */ } u2; };
- 咱们直指关键 ——
value
,打印一下此中的内容。打印构造体成员能够应用.
运算符,例如:p $2.value
,运转这个饬令,显示以下:
(gdb) p $2.value $3 = {lval = 140737247576960, dval = 6.9533439118030153e-310, counted = 0x7ffff1a60380, str = 0x7ffff1a60380, arr = 0x7ffff1a60380, obj = 0x7ffff1a60380, res = 0x7ffff1a60380, ref = 0x7ffff1a60380, ast = 0x7ffff1a60380, zv = 0x7ffff1a60380, ptr = 0x7ffff1a60380, ce = 0x7ffff1a60380, func = 0x7ffff1a60380, ww = {w1 = 4054188928, w2 = 32767}}
- 经过 zval 构造体,咱们晓得 value 成员的类型是 zend_value,很可怜,这也是一个构造体:
typedef union _zend_value { zend_long lval; /* long value */ double dval; /* double value */ zend_refcounted *counted; zend_string *str; zend_array *arr; zend_object *obj; zend_resource *res; zend_reference *ref; zend_ast_ref *ast; zval *zv; void *ptr; zend_class_entry *ce; zend_function *func; struct { uint32_t w1; uint32_t w2; } ww; } zend_value;
- 咱们要打印的变量是 pieces,咱们晓得它是一个数组,因此此时咱们间接取 zend_value 构造体的
*arr
成员,它表面看起来就是一个指针,因而打印其内容,需求应用*
运算符
(gdb) p *$3.arr $4 = {gc = {refcount = 2, u = {type_info = 23}}, u = {v = {flags = 28 '\034', _unused = 0 '\000', nIteratorsCount = 0 '\000', _unused2 = 0 '\000'}, flags = 28}, nTableMask = 4294967294, arData = 0x7ffff1a67648, nNumUsed = 3, nNumOfElements = 3, nTableSize = 8, nInternalPointer = 0, nNextFreeElement = 3, pDestructor = 0x555555b6e200 <zval_ptr_dtor>}
- 真棒!到今朝为止,貌似所有都依照预约的道路进行。经过 zend_value 构造体,能够晓得
*arr
的类型是 zend_array:
struct _zend_array { zend_refcounted_h gc; union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar _unused, zend_uchar nIteratorsCount, zend_uchar _unused2) } v; uint32_t flags; } u; uint32_t nTableMask; Bucket *arData; uint32_t nNumUsed; uint32_t nNumOfElements; uint32_t nTableSize; uint32_t nInternalPointer; zend_long nNextFreeElement; dtor_func_t pDestructor; };
- 理解 PHP 数组的同窗肯定晓得它底层是一个 HashTable,感兴味的同窗,能够去自行理解一下 HashTable。这里,咱们打印
*arData
,应用:p *$4.arDaa
:
(gdb) p *$4.arData $5 = {val = {value = {lval = 2019, dval = 9.9751853895347677e-321, counted = 0x7e3, str = 0x7e3, arr = 0x7e3, obj = 0x7e3, res = 0x7e3, ref = 0x7e3, ast = 0x7e3, zv = 0x7e3, ptr = 0x7e3, ce = 0x7e3, func = 0x7e3, ww = {w1 = 2019, w2 = 0}}, u1 = {v = {type = 4 '\004', type_flags = 0 '\000', u = {call_info = 0, extra = 0}}, type_info = 4}, u2 = {next = 0, cache_slot = 0, opline_num = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, constant_flags = 0, extra = 0}}, h = 0, key = 0x0}
- 到这里,咱们曾经能够看到 pieces 数组第一个单位的值 —— 2019,就是那段
lval = 2019
。 - 好了,对于 gdb 的简略应用就先引见到这里。文章开篇,咱们提到,假如数组是多维数组,会发作甚么?咱们理论的次要指标就是简略完成二维数组的 implode
- 正在 PHP 的 implode 函数中,假如是多维数组,则会间接把里层的数组显示为 Array 字符串。
$res = implode('-', [ 2019, '01', '01', [1,2] ]); var_dump($res);
- 运转这段剧本,会输入以下:
PHP Notice: Array to string conversion in /path/to/t2.php on line 3 PHP Notice: Array to string conversion in /path/to/t2.php on line 3 string(16) "2019-01-01-Array"
- 为了可以支持衔接数组,咱们需求改写 php_implode,因而,先拷贝一下 php_implode 到写扩大代码的文件中:
PHPAPI void php_implode(const zend_string *glue, zval *pieces, zval *return_value) { zval *tmp; int numelems; zend_string *str; char *cptr; size_t len = 0; struct { zend_string *str; zend_long lval; } *strings, *ptr; ALLOCA_FLAG(use_heap) numelems = zend_hash_num_elements(Z_ARRVAL_P(pieces)); if (numelems == 0) { RETURN_EMPTY_STRING(); } else if (numelems == 1) { /* loop to search the first not undefined element... */ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pieces), tmp) { RETURN_STR(zval_get_string(tmp)); } ZEND_HASH_FOREACH_END(); } ptr = strings = do_alloca((sizeof(*strings)) * numelems, use_heap); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pieces), tmp) { if (EXPECTED(Z_TYPE_P(tmp) == IS_STRING)) { ptr->str = Z_STR_P(tmp); len += ZSTR_LEN(ptr->str); ptr->lval = 0; ptr++; } else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_LONG)) { zend_long val = Z_LVAL_P(tmp); ptr->str = NULL; ptr->lval = val; ptr++; if (val <= 0) { len++; } while (val) { val /= 10; len++; } } else { ptr->str = zval_get_string_func(tmp); len += ZSTR_LEN(ptr->str); ptr->lval = 1; ptr++; } } ZEND_HASH_FOREACH_END(); /* numelems can not be 0, we checked above */ str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(glue), len, 0); cptr = ZSTR_VAL(str) + ZSTR_LEN(str); *cptr = 0; while (1) { ptr--; if (EXPECTED(ptr->str)) { cptr -= ZSTR_LEN(ptr->str); memcpy(cptr, ZSTR_VAL(ptr->str), ZSTR_LEN(ptr->str)); if (ptr->lval) { zend_string_release_ex(ptr->str, 0); } } else { char *oldPtr = cptr; char oldVal = *cptr; cptr = zend_print_long_to_buf(cptr, ptr->lval); *oldPtr = oldVal; } if (ptr == strings) { break; } cptr -= ZSTR_LEN(glue); memcpy(cptr, ZSTR_VAL(glue), ZSTR_LEN(glue)); } free_alloca(strings, use_heap); RETURN_NEW_STR(str); }
- 先将函数署名略微调整成
static void su_php_implode(const zend_string *glue, zval *pieces, zval *return_value)
- 咱们能够看到此中有一段轮回 pieces 的解决:
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pieces), tmp) { if (EXPECTED(Z_TYPE_P(tmp) == IS_STRING)) { // ... } else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_LONG)) { // ... } else { // ... } } ZEND_HASH_FOREACH_END();
- 咱们只要将此中的 if 分支新增一个分支:
else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_ARRAY))
,其详细内容以下:
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pieces), tmp) { if (EXPECTED(Z_TYPE_P(tmp) == IS_STRING)) { // ... } else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_LONG)) { // ... } else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_ARRAY)) { // 假如值是数组,则挪用 php_implode,将其应用 glue 衔接成字符串 cptr = ZSTR_VAL(ptr->str); zend_string* str2 = origin_php_implode(glue, tmp, tmp_val); ptr->str = str2; // 此时,要拿到拼接后的字符串长度 len += ZSTR_LEN(str2); ptr++; } else { // ... } } ZEND_HASH_FOREACH_END();
- 正如正文中写的,当遇到数组的单位是数组类型时,咱们会挪用原先的 php_implode,只不外,这个“php_implode”会真的前往一个 zend_string 指针,正在此我将其更名为
origin_php_implode
:
static zend_string* origin_php_implode(const zend_string *glue, zval *pieces, zval *return_value) { zval *tmp; int numelems; zend_string *str; char *cptr; size_t len = 0; struct { zend_string *str; zend_long lval; } *strings, *ptr; ALLOCA_FLAG(use_heap) numelems = zend_hash_num_elements(Z_ARRVAL_P(pieces)); if (numelems == 0) { RETURN_EMPTY_STRING(); } else if (numelems == 1) { /* loop to search the first not undefined element... */ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pieces), tmp) { RETURN_STR(zval_get_string(tmp)); } ZEND_HASH_FOREACH_END(); } ptr = strings = do_alloca((sizeof(*strings)) * numelems, use_heap); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pieces), tmp) { if (EXPECTED(Z_TYPE_P(tmp) == IS_STRING)) { ptr->str = Z_STR_P(tmp); len += ZSTR_LEN(ptr->str); ptr->lval = 0; ptr++; } else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_LONG)) { zend_long val = Z_LVAL_P(tmp); ptr->str = NULL; ptr->lval = val; ptr++; if (val <= 0) { len++; } while (val) { val /= 10; len++; } } else { ptr->str = zval_get_string_func(tmp); len += ZSTR_LEN(ptr->str); ptr->lval = 1; ptr++; } } ZEND_HASH_FOREACH_END(); /* numelems can not be 0, we checked above */ str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(glue), len, 0); cptr = ZSTR_VAL(str) + ZSTR_LEN(str); *cptr = 0; while (1) { ptr--; if (EXPECTED(ptr->str)) { cptr -= ZSTR_LEN(ptr->str); memcpy(cptr, ZSTR_VAL(ptr->str), ZSTR_LEN(ptr->str)); if (ptr->lval) { zend_string_release_ex(ptr->str, 0); } } else { char *oldPtr = cptr; char oldVal = *cptr; cptr = zend_print_long_to_buf(cptr, ptr->lval); *oldPtr = oldVal; } if (ptr == strings) { break; } cptr -= ZSTR_LEN(glue); memcpy(cptr, ZSTR_VAL(glue), ZSTR_LEN(glue)); } free_alloca(strings, use_heap); // RETURN_NEW_STR(str); return str; }
- 内容大要没有变,只有函数署名和前往值之处略作调整了。
- 合营后面的
PHP_FUNCTION(su_test)
,性能完成的差没有多了。咱们去编译看看:
./configure sudo make sudo make install
- 太棒了,编译经过。咱们去执行一下 PHP 剧本:
$res = su_test('-', [ 2019, '01', '01', ['1', '2',], ]); var_dump($res);
- 输入以下:
string(14) "2019-01-01-1-2"
- 祝贺,咱们曾经半途而废!
以上就是PHP源码—implode函数源码剖析的具体内容,更多请存眷资源魔其它相干文章!
标签: php开发教程 php开发资料 php开发自学 PHP源码 implode函数
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。
抱歉,评论功能暂时关闭!