Saturday, February 21, 2009

== ?

一段代码
$a = 0; 

if ($a=='offline') {
echo 'OK';
} else {
echo 'noOK';
}


结果居然是 OK


试过了
offline -> offx
' -> "
$a == 'offline' -> 'offline' == $a
依旧

很诡异的 ==
翻源码

估摸着是强制转换的问题,因为 $a 初始化是个 0,也可以当成boolean false, 而off这个开头的字符串在强制转换成boolean / integer 的时候,估计被当成了 false / 0

待考证

...


[edit]
在php的源码中,对==这个operator的定义是 T_IS_EQUAL
/Zend/zend_language_parser.(ch) 中,只是 T_IS_EQUAL = 283
/Zend/zend_language_parser.y 中,
[code]Line=596
expr T_IS_EQUAL expr { zend_do_binary_op(ZEND_IS_EQUAL, &$$, &$1, &$3 TSRMLS_CC); }
[/code]
但是 对zend engine 不熟悉

不过,在php的/ext/filter/logical_filters.c中

void php_filter_boolean(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
{
char *str = Z_STRVAL_P(value);
int len = Z_STRLEN_P(value);
int ret;

PHP_FILTER_TRIM_DEFAULT(str, len);

/* returns true for "1", "true", "on" and "yes"
* returns false for "0", "false", "off", "no", and ""
* null otherwise. */
switch (len) {
case 1:
if (*str == '1') {
ret = 1;
} else if (*str == '0') {
ret = 0;
} else {
ret = -1;
}
break;
case 2:
if (strncasecmp(str, "on", 2) == 0) {
ret = 1;
} else if (strncasecmp(str, "no", 2) == 0) {
ret = 0;
} else {
ret = -1;
}
break;
case 3:
if (strncasecmp(str, "yes", 3) == 0) {
ret = 1;
} else if (strncasecmp(str, "off", 3) == 0) {
ret = 0;
} else {
ret = -1;
}
break;
case 4:
if (strncasecmp(str, "true", 4) == 0) {
ret = 1;
} else {
ret = -1;
}
break;
case 5:
if (strncasecmp(str, "false", 5) == 0) {
ret = 0;
} else {
ret = -1;
}
break;
default:
ret = -1;
}

if (ret == -1) {
RETURN_VALIDATION_FAILED
} else {
zval_dtor(value);
ZVAL_BOOL(value, ret);
}
}
/* }}} */



还是不能说明问题




。。。。。。。

再说吧,找空再看


==============================================================

再次更新:
$a = 0; 

$t = 'offline';
#if ("xxxx" == $a)
if ($t == $a) {
echo 'OK';
} else {
echo 'noOK';
}

这样的代码,不管注释哪个if,都是返回OK

而,查看 php的源码 : /Zend/zend_operators.c里
	if (op1->type == IS_STRING && op2->type == IS_STRING) {

zendi_smart_strcmp(result, op1, op2);
COMPARE_RETURN_AND_FREE(SUCCESS);
}

if (Z_TYPE_P(op1) == IS_BOOL Z_TYPE_P(op2) == IS_BOOL
Z_TYPE_P(op1) == IS_NULL Z_TYPE_P(op2) == IS_NULL) {
zendi_convert_to_boolean(op1, op1_copy, result);
zendi_convert_to_boolean(op2, op2_copy, result);
ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_LVAL_P(op1) - Z_LVAL_P(op2)));
COMPARE_RETURN_AND_FREE(SUCCESS);
}

字符串的比较,要求两个变量都是字符串型
而下边的判断却是只需要任何一个为boolean或null即进行
而$a=0应该是符合这个条件的
于是,两个都被转换成了boolean
但是诡异的是,理论上说,'asdf'这样的,boolean应该为true啊
如果这里不符合的话,继续往后看

	/* If both are objects sharing the same comparision handler then use is */

if (eq_comp) {
if (Z_OBJ_HANDLE_P(op1) == Z_OBJ_HANDLE_P(op2)) {
/* object handles are identical, apprently this is the same object */
ZVAL_LONG(result, 0);
COMPARE_RETURN_AND_FREE(SUCCESS);
}
ZVAL_LONG(result, Z_OBJ_HT_P(op1)->compare_objects(op1, op2 TSRMLS_CC));
COMPARE_RETURN_AND_FREE(SUCCESS);
}

zendi_convert_scalar_to_number(op1, op1_copy, result);
zendi_convert_scalar_to_number(op2, op2_copy, result);

if (Z_TYPE_P(op1) == IS_LONG && Z_TYPE_P(op2) == IS_LONG) {
ZVAL_LONG(result, Z_LVAL_P(op1)>Z_LVAL_P(op2)?1:(Z_LVAL_P(op1) COMPARE_RETURN_AND_FREE(SUCCESS);
}
if ((Z_TYPE_P(op1) == IS_DOUBLE || Z_TYPE_P(op1) == IS_LONG)
&& (Z_TYPE_P(op2) == IS_DOUBLE || Z_TYPE_P(op2) == IS_LONG)) {
Z_DVAL_P(result) = (Z_TYPE_P(op1) == IS_LONG ? (double) Z_LVAL_P(op1) : Z_DVAL_P(op1)) - (Z_TYPE_P(op2) == IS_LONG ? (double) Z_LVAL_P(op2) : Z_DVAL_P(op2));
ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_DVAL_P(result)));
COMPARE_RETURN_AND_FREE(SUCCESS);
}


第一块儿的的判断,肯定不是了,对象的比较
再往后,就是一个强制转换为数字
如果这样的话,一个字符串,只要不是以数字开头的,转成数字,那就是0了
测试一下php的代码
$a = 0; 

$t = '0offline';
//$t = '1offline';
if ($t == $a) {
echo 'OK';
} else {
echo 'noOK';
}

结果很明显,况且php手册上也详细说了这个字符串->数字的转换原则
也就是说,这个原因发生在这里


==========================================
回过头再想,php手册里,按照字符串转数字的原则,就应该很容易判断出这个if的条件
而错误就发生在我们没考虑到这里的自动转换的过程


END




==========================================

最后,感谢 冰雪峰谷 提供的php代码测试,之前从没遇到过这种字符串转换、判断问题


然后补充感谢:叫兽同学中午的烤翅bg

No comments:

Post a Comment