今天遇到一个奇怪的问题
<?php
echo 1 + 2 + "3+4+5";
输出的结果为6; 当时有点想不通,1+2=3是没问题的 但是 3+“3+4+5” = 6 是为什么呢, 百思不得其解。所以我们还是从内核来看吧。
我们先看下opcode:
这里的 “3+4+5”被编译成3%2B4%2B5,以字符串形式存在,与整形相加,需要先转为整形,想到整形我们就intval()函数,那么我们就借助intval函数去找点启发吧
/* {{{ proto int intval(mixed var [, int base])
Get the integer value of a variable using the optional base for the conversion */
PHP_FUNCTION(intval)
{
zval **num;
long arg_base;
int base;
switch (ZEND_NUM_ARGS()) {
case 1:
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &num) == FAILURE) {
return;
}
base = 10;
break;
case 2:
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zl", &num, &arg_base) == FAILURE) {
return;
}
base = arg_base;
break;
default:
WRONG_PARAM_COUNT;
}
RETVAL_ZVAL(*num, 1, 0); //设置返回值给 return_value
convert_to_long_base(return_value, base); //根据base进制进行转换
}
/* }}} */
可以看出 问题的关键就在convert_to_long_base上,接上这个convert_to_long_base往下看
ZEND_API void convert_to_long_base(zval *op, int base) /* {{{ */
{
....省略若干....
case IS_STRING:
{
char *strval = Z_STRVAL_P(op); //获取op的string值作为指针返回给char类型的指针变量
Z_LVAL_P(op) = strtol(strval, NULL, base); //通过strtol函数把char类型指针变量转为long
STR_FREE(strval); //释放这个char类型的指针变量
}
break;
....省略若干....
Z_TYPE_P(op) = IS_LONG;
}
/* }}} */
哦了, 看来问题就出在这个strtol上, 我们来看看这个strol到底偷偷摸摸的对string做了什么
我们先来看看strol的作用
相关函数: atof, atoi, atol, strtod, strtoul
表头文件: #include <stdlib.h>
定义函数: long int strtol(const char *nptr, char **endptr, int base)
函数说明: strtol()会将参数nptr字符串根据参数base来转换成长整型数。参数base范围从2至36,或0。参数base代表采用的进制方式,如base值为10则采用10进制(字符串以10进制表示),若base值为16则采用16进制(字符串以16进制表示)。当base值为0时则是采用10进制做转换,但遇到如''0x''前置字符则会使用16进制做转换。<span style="color:#3366ff;">一开始strtol()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,再遇到非数字或字符串结束时(''\0'')结束转换,并将结果返回。</span>
若参数endptr不为NULL,则会将遇到不合条件而终止的nptr中的字符指针由endptr返回。
返回值: 返回转换后的长整型数,否则返回ERANGE并将错误代码存入errno中。
附加说明: ERANGE指定的转换字符串超出合法范围。
一开始strtol()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,再遇到非数字或字符串结束时(''\0'')结束转换,并将结果返回。
char buffer[20]="10379cend$3";
char *stop;
printf("%d\n",strtol(buffer, &stop, 10));
printf("%s\n", stop);
输出结果:
10379
cend$3
于是乎,我们明白了一切,“3%2B4%2B5” 最终出来的 3。
为了验证我们的猜想,我们来看看
<?php
echo 1 + 2 + "-5+6+7";
结果是不是-2呢?见证时刻到了,运行下, 结果果然是-2.
所以以后我们再遇到string转int时候就不要迷惑了。记得跳过空格,知道遇到数字或者正负号才开始转换,再遇到非数字或字符串结束时("\0")结束转换,并将结果返回。