一. 字符串连接符

<?php
$x = 4;
echo "x minus one equals " . $x-1 . ", or so I hope";
?>

打印出来:

"-1, or so I hope"

二. and/or
嗯.php也有and/or这样的逻辑运算符.但是…

$bool = true && false;
var_dump($bool); // false, that's expected

$bool = true and false;
var_dump($bool); // true, ouch!
?>

原来’=’比’and/or’优先级高.

php类型比较表

最近在家又在看php手册。发现对类型转换还是没有那么精通..总是有疑惑..为什么这样.为什么那样..死记是记不住的..还是从源码来动手吧.最终比较函数是zend_operators.c的1599行:

ZEND_API int compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{{ */
{
        int ret;
        int converted = 0;
        zval op1_copy, op2_copy;
        zval *op_free;

        while (1) {
                switch (TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2))) {
                        case TYPE_PAIR(IS_LONG, IS_LONG):
                                ZVAL_LONG(result, Z_LVAL_P(op1)&gt;Z_LVAL_P(op2)?1:(Z_LVAL_P(op1)&lt;Z_LVAL_P(op2)?-1:0));                                 return SUCCESS;                         case TYPE_PAIR(IS_DOUBLE, IS_LONG):                                 Z_DVAL_P(result) = Z_DVAL_P(op1) - (double)Z_LVAL_P(op2);                                 ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_DVAL_P(result)));                                 return SUCCESS;                         case TYPE_PAIR(IS_LONG, IS_DOUBLE):                                 Z_DVAL_P(result) = (double)Z_LVAL_P(op1) - Z_DVAL_P(op2);                                 ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_DVAL_P(result)));                                 return SUCCESS;                         case TYPE_PAIR(IS_DOUBLE, IS_DOUBLE):                                 if (Z_DVAL_P(op1) == Z_DVAL_P(op2)) {                                         ZVAL_LONG(result, 0);                                 } else {                                         Z_DVAL_P(result) = Z_DVAL_P(op1) - Z_DVAL_P(op2);                                         ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_DVAL_P(result)));                                 }                                 return SUCCESS;                         case TYPE_PAIR(IS_ARRAY, IS_ARRAY):                                 zend_compare_arrays(result, op1, op2 TSRMLS_CC);                                 return SUCCESS;                         case TYPE_PAIR(IS_NULL, IS_NULL):                                 ZVAL_LONG(result, 0);                                 return SUCCESS;                         case TYPE_PAIR(IS_NULL, IS_BOOL):                                 ZVAL_LONG(result, Z_LVAL_P(op2) ? -1 : 0);                                 return SUCCESS;                         case TYPE_PAIR(IS_BOOL, IS_NULL):                                 ZVAL_LONG(result, Z_LVAL_P(op1) ? 1 : 0);                                 return SUCCESS;                         case TYPE_PAIR(IS_BOOL, IS_BOOL):                                 ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_LVAL_P(op1) - Z_LVAL_P(op2)));                                 return SUCCESS;                         case TYPE_PAIR(IS_STRING, IS_STRING):                                 zendi_smart_strcmp(result, op1, op2);                                 return SUCCESS;                         case TYPE_PAIR(IS_NULL, IS_STRING):                                 ZVAL_LONG(result, zend_binary_strcmp("", 0, Z_STRVAL_P(op2), Z_STRLEN_P(op2)));                                 return SUCCESS;                         case TYPE_PAIR(IS_STRING, IS_NULL):                                 ZVAL_LONG(result, zend_binary_strcmp(Z_STRVAL_P(op1), Z_STRLEN_P(op1), "", 0));                                 return SUCCESS;                         case TYPE_PAIR(IS_OBJECT, IS_NULL):                                 ZVAL_LONG(result, 1);                                 return SUCCESS;                         case TYPE_PAIR(IS_NULL, IS_OBJECT):                                 ZVAL_LONG(result, -1);                                 return SUCCESS;                         default:                                 if (Z_TYPE_P(op1) == IS_OBJECT &amp;&amp; Z_OBJ_HANDLER_P(op1, compare)) {                                         return Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2 TSRMLS_CC);                                 } else if (Z_TYPE_P(op2) == IS_OBJECT &amp;&amp; Z_OBJ_HANDLER_P(op2, compare)) {                                         return Z_OBJ_HANDLER_P(op2, compare)(result, op1, op2 TSRMLS_CC);                                 }                                 if (Z_TYPE_P(op1) == IS_OBJECT &amp;&amp; Z_TYPE_P(op2) == IS_OBJECT) {                                         if (Z_OBJ_HANDLE_P(op1) == Z_OBJ_HANDLE_P(op2)) {                                                 /* object handles are identical, apparently this is the same object */                                                 ZVAL_LONG(result, 0);                                                 return SUCCESS;                                         }                                         if (Z_OBJ_HANDLER_P(op1, compare_objects) == Z_OBJ_HANDLER_P(op2, compare_objects)) {                                                 ZVAL_LONG(result, Z_OBJ_HANDLER_P(op1, compare_objects)(op1, op2 TSRMLS_CC));                                                 return SUCCESS;                                         }                                 }                                 if (Z_TYPE_P(op1) == IS_OBJECT) {                                         if (Z_OBJ_HT_P(op1)-&gt;get) {
                                                op_free = Z_OBJ_HT_P(op1)-&gt;get(op1 TSRMLS_CC);
                                                ret = compare_function(result, op_free, op2 TSRMLS_CC);
                                                zend_free_obj_get_result(op_free TSRMLS_CC);
                                                return ret;
                                        } else if (Z_TYPE_P(op2) != IS_OBJECT &amp;&amp; Z_OBJ_HT_P(op1)-&gt;cast_object) {
                                                ALLOC_INIT_ZVAL(op_free);
                                                if (Z_OBJ_HT_P(op1)-&gt;cast_object(op1, op_free, Z_TYPE_P(op2) TSRMLS_CC) == FAILURE) {
                                                        ZVAL_LONG(result, 1);
                                                        zend_free_obj_get_result(op_free TSRMLS_CC);
                                                        return SUCCESS;
                                                }
                                                ret = compare_function(result, op_free, op2 TSRMLS_CC);
                                                zend_free_obj_get_result(op_free TSRMLS_CC);
                                                return ret;
                                        }
                                }
                                if (Z_TYPE_P(op2) == IS_OBJECT) {
                                        if (Z_OBJ_HT_P(op2)-&gt;get) {
                                                op_free = Z_OBJ_HT_P(op2)-&gt;get(op2 TSRMLS_CC);
                                                ret = compare_function(result, op1, op_free TSRMLS_CC);
                                                zend_free_obj_get_result(op_free TSRMLS_CC);
                                                return ret;
                                        } else if (Z_TYPE_P(op1) != IS_OBJECT &amp;&amp; Z_OBJ_HT_P(op2)-&gt;cast_object) {
                                                ALLOC_INIT_ZVAL(op_free);
                                                if (Z_OBJ_HT_P(op2)-&gt;cast_object(op2, op_free, Z_TYPE_P(op1) TSRMLS_CC) == FAILURE) {
                                                        ZVAL_LONG(result, -1);
                                                        zend_free_obj_get_result(op_free TSRMLS_CC);
                                                        return SUCCESS;
                                                }
                                                ret = compare_function(result, op1, op_free TSRMLS_CC);
                                                zend_free_obj_get_result(op_free TSRMLS_CC);
                                                return ret;
                                        } else if (Z_TYPE_P(op1) == IS_OBJECT) {
                                                ZVAL_LONG(result, 1);
                                                return SUCCESS;
                                        }
                                }
                                if (!converted) {
                                        if (Z_TYPE_P(op1) == IS_NULL) {
                                                zendi_convert_to_boolean(op2, op2_copy, result);
                                                ZVAL_LONG(result, Z_LVAL_P(op2) ? -1 : 0);
                                                return SUCCESS;
                                        } else if (Z_TYPE_P(op2) == IS_NULL) {
                                                zendi_convert_to_boolean(op1, op1_copy, result);
                                                ZVAL_LONG(result, Z_LVAL_P(op1) ? 1 : 0);
                                                return SUCCESS;
                                        } else if (Z_TYPE_P(op1) == IS_BOOL) {
                                                zendi_convert_to_boolean(op2, op2_copy, result);
                                                ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_LVAL_P(op1) - Z_LVAL_P(op2)));
                                                return SUCCESS;
                                        } else if (Z_TYPE_P(op2) == IS_BOOL) {
                                                zendi_convert_to_boolean(op1, op1_copy, result);
                                                ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_LVAL_P(op1) - Z_LVAL_P(op2)));
                                                return SUCCESS;
                                        } else {
                                                zendi_convert_scalar_to_number(op1, op1_copy, result);
                                                zendi_convert_scalar_to_number(op2, op2_copy, result);
                                                converted = 1;
                                        }
                                } else if (Z_TYPE_P(op1)==IS_ARRAY) {
                                        ZVAL_LONG(result, 1);
                                        return SUCCESS;
                                } else if (Z_TYPE_P(op2)==IS_ARRAY) {
                                        ZVAL_LONG(result, -1);
                                        return SUCCESS;
                                } else if (Z_TYPE_P(op1)==IS_OBJECT) {
                                        ZVAL_LONG(result, 1);
                                        return SUCCESS;
                                } else if (Z_TYPE_P(op2)==IS_OBJECT) {
                                        ZVAL_LONG(result, -1);
                                        return SUCCESS;
                                } else {
                                        ZVAL_LONG(result, 0);
                                        return FAILURE;
                                }
                }
        }
}
/* }}} */

省略object类型
代码逻辑如下:

while(1) {
	1. long和double都转为double进行比较;break;
	2. 相同类型的进行比较;break;
	3. string,object和null进行比较;break;
	if (!converted) {
		4. 其它与null,bool比较的都转换为bool再比较;break;
		5. 其它的情况都转换为整形
	} else {
		6. 数组都大于其它类型的值;break;
		7. object都大于其它类型的值;break;
		8. 其它情况的比较都是true;break;
	}
};

好了.这就是PHP各种不同类型比较的逻辑..所以的问题都遵循这个逻辑.

一次诡异的问题处理

话说一位实习生同学把环境部署好。但是登陆不上系统。总是被自动logout

一顿var_export();exit;之后。定位到用户信息存不到memcache中。但是set方法也返回true。
1. 有可能是memcache客户端版本问题?
2. 其它

抓包之后。发现set命令参数为timestamp。刚开始没在意。。然后顺手在机器上date了一下。发现系统时间不正确。。回想起刚才抓包的timestamp。猜测有可能是这个导致的。修改完系统时间马上就好了。。

php调用memcache函数set中。使用的是当前时间偏移。被yii框架自动转为当前机器的ts + 设置的秒数。。导致设置的时间总是落后于memcache服务器时间。

好了。问题解决。

压测openresty记录

由于项目需要,新房的售罄楼盘需要展示二手房的页面,即url一致,有以下几种方案:
1. 新房php使用curl调用二手房页面并返回给客户端
2. php再套一遍二手房的模板,不过这样二手房新做的页面和以后的页面维护都会落在新房这里。
3. 使用openresty将重合的url分离出group_id,然后判断销售状态,再决定是代理到新房的服务器还是二手房的服务器

第一种简单。但是性能是最差的。
第二种对于业务来说也是不合理的。项目过来以后。会再给项目组多一个从吗?显然不会。。而且以后维护会到新房这里
第三种是最优的。写一小段lua代码。连上redis。取销售状态。再决定proxy_pass到哪个upstream。

然而运维对于这种方式不太感冒。觉得性能会牺牲。那么咱们就来测一下。。

1. redis进程池使用100,keepalive_time 设置为60s
2. lua_code_cache on;

为了公平起见。不做真正的proxy_pass代理。只ngx.say出来需要选择的upstream

    location / {
        default_type text/html;
        set $target "";
        set $r_status "";
        rewrite_by_lua_file "/home/shijingbo/lua/rewrite.lua";
         content_by_lua '
            ngx.say(ngx.var.target)
         ';
        #content_by_lua_file "/home/shijingbo/lua/content.lua";
        #proxy_set_header Host "$host";
        #proxy_pass http://$target;
    }

webbench 5000并发 运行60s结果如下:

不加lua dict缓存的情况下:

root@hackers365-HP-Compaq-Elite-8300-SFF:/data/software/webbench-1.5# ./webbench -t 60 -c 5000 'http://bd.focus.cn:8080/loupan/123.html'
Webbench - Simple Web Benchmark 1.5
Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.

Benchmarking: GET http://bd.focus.cn:8080/loupan/123.html
5000 clients, running 60 sec.

Speed=925107 pages/min, 2343632 bytes/sec.
Requests: 925107 susceed, 0 failed.

加lua dict缓存的情况下:

root@hackers365-HP-Compaq-Elite-8300-SFF:/data/software/webbench-1.5# ./webbench -t 60 -c 5000 'http://bd.focus.cn:8080/loupan/123.html'
Webbench - Simple Web Benchmark 1.5
Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.

Benchmarking: GET http://bd.focus.cn:8080/loupan/123.html
5000 clients, running 60 sec.

Speed=1182816 pages/min, 2996619 bytes/sec.
Requests: 1182816 susceed, 0 failed.

差不多每秒2w的并发。。谁还敢说慢?

php重载函数

php中可以重载类。但是一些函数不能重载。比如我想hook curl_xxx的函数。然后分析一下请求调用。记录到日志中。。进到日志系统中。。。有以下几个作用

1. 绘制调用拓扑。
2. 准实时分析函数调用频率。

当然。改项目代码也能完成这些功能。。通过写一个扩展来重载curl_xxx函数。就可以用扩展来实现。对应用透明的来实现这个功能。

扩展已经写完了。放到了github上。已完成概念性的功能。。之后还需要定制curl_xxx来写日志的功能。。

https://github.com/hackers365/replace_function

一次诡异的种cookie事件

有个需求。需要获取passport然后种到根域下。。然后浏览器就登陆了。。。嗯。很简单。可是调试了半天也没成功。用网页上登陆就没问题。php使用setcookie就出问题了。
很简单的问题。愣是登陆不上。。。应该有以下几种可能:
1. 返回的passport错了
2. 不只是这两个cookie。还有其它cookie
3. cookie设错了。

用burp对比了两次请求。肉眼看都一样。但是。点开内容看。错误的是编码以后的。正确的是未编码的。

setcookie是会自己编码的。。用setrawcookie就解决了。

php手册阅读之int,float坑

一. 类型转换
根据前一篇的内容,找到强制类型转换成int的函数在zend_operators.c的convert_to_long_base, 实现如下:

ZEND_API void convert_to_long_base(zval *op, int base) /* {{{ */
{
	long tmp;

	switch (Z_TYPE_P(op)) {
		case IS_NULL:
			Z_LVAL_P(op) = 0;
			break;
		case IS_RESOURCE: {
				TSRMLS_FETCH();

				zend_list_delete(Z_LVAL_P(op));
			}
			/* break missing intentionally */
		case IS_BOOL:
		case IS_LONG:
			break;
		case IS_DOUBLE:
			Z_LVAL_P(op) = zend_dval_to_lval(Z_DVAL_P(op));
			break;
		case IS_STRING:
			{
				char *strval = Z_STRVAL_P(op);

				Z_LVAL_P(op) = strtol(strval, NULL, base);
				STR_FREE(strval);
			}
			break;
		case IS_ARRAY:
			tmp = (zend_hash_num_elements(Z_ARRVAL_P(op))?1:0);
			zval_dtor(op);
			Z_LVAL_P(op) = tmp;
			break;
		case IS_OBJECT:
			{
				int retval = 1;
				TSRMLS_FETCH();

				convert_object_to_type(op, IS_LONG, convert_to_long);

				if (Z_TYPE_P(op) == IS_LONG) {
					return;
				}
				zend_error(E_NOTICE, "Object of class %s could not be converted to int", Z_OBJCE_P(op)->name);

				zval_dtor(op);
				ZVAL_LONG(op, retval);
				return;
			}
		default:
			zend_error(E_WARNING, "Cannot convert to ordinal value");
			zval_dtor(op);
			Z_LVAL_P(op) = 0;
			break;
	}

	Z_TYPE_P(op) = IS_LONG;
}

总结如下:
1. null => 0
2. bool => 由于bool在php内部也是用0,1来表示的。所以true为1, false为0
3. double => 强制转换为long,即向下取整
4. string => Z_LVAL_P(op) = strtol(strval, NULL, base); 使用c语言的strtol函数来处理。即总为0
5. array => 数组不为空则为1否则为0

二. 字符串的前导符为0不是八进制

$x = 0123; // 83
$y = “0123” + 0 // 123

三. 边界
var_dump(-2147483648); //float(-2147483648)
var_dump(-2147483647 – 1); //int(-2147483648)

php手册阅读之惑类型转换

结合源码看一遍PHP手册吧。。
看到BOOL类型这里:

当转换为 boolean 时,以下值被认为是 FALSE:

布尔值 FALSE 本身
整型值 0(零)
浮点型值 0.0(零)
空字符串,以及字符串 "0"
不包括任何元素的数组
不包括任何成员变量的对象(仅 PHP 4.0 适用)
特殊类型 NULL(包括尚未赋值的变量)
从空标记生成的 SimpleXML 对象

打个疑问。为什么是这样。PHP内部是怎么做的。。咱从源码入手吧。。强制类型转换是(bool)符号。好。那么搜一下源码

root@hackers365-HP-Compaq-Elite-8300-SFF:/data/software/php-5.5.14/Zend# g '(bool)'
...
zend_language_parser.y:119:%token T_BOOL_CAST   "(bool) (T_BOOL_CAST)"
...

好。看到他将(bool)转换为T_BOOL_CAST这样的token符号了。继续搜’T_BOOL_CAST’

root@hackers365-HP-Compaq-Elite-8300-SFF:/data/software/php-5.5.14/Zend# g 'T_BOOL_CAST'
zend_language_parser.c:194:     T_BOOL_CAST = 291,
zend_language_parser.c:327:#define T_BOOL_CAST 291
.....
...
zend_language_parser.y:802:	|	T_BOOL_CAST expr	{ zend_do_cast(&$$, &$2, IS_BOOL TSRMLS_CC); }
....

好。看到他是用zend_do_cast来执行的。再搜一下zend_do_cast是做什么的。

root@hackers365-HP-Compaq-Elite-8300-SFF:/data/software/php-5.5.14/Zend# g 'zend_do_cast('
zend_compile.c:6057:void zend_do_cast(znode *result, const znode *expr, int type TSRMLS_DC) /* {{{ */
zend_compile.h:597:void zend_do_cast(znode *result, const znode *expr, int type TSRMLS_DC);
zend_language_parser.c:4889:    { zend_do_cast(&(yyval), &(yyvsp[(2) - (2)]), IS_LONG TSRMLS_CC); }
zend_language_parser.c:4894:    { zend_do_cast(&(yyval), &(yyvsp[(2) - (2)]), IS_DOUBLE TSRMLS_CC); }
zend_language_parser.c:4899:    { zend_do_cast(&(yyval), &(yyvsp[(2) - (2)]), IS_STRING TSRMLS_CC); }
zend_language_parser.c:4904:    { zend_do_cast(&(yyval), &(yyvsp[(2) - (2)]), IS_ARRAY TSRMLS_CC); }

root@hackers365-HP-Compaq-Elite-8300-SFF:/data/software/php-5.5.14/Zend# vi zend_compile.c +6057
.....

void zend_do_cast(znode *result, const znode *expr, int type TSRMLS_DC) /* {{{ */
{
        zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);

        opline->opcode = ZEND_CAST;
        opline->result_type = IS_TMP_VAR;
        opline->result.var = get_temporary_variable(CG(active_op_array));
        SET_NODE(opline->op1, expr);
        SET_UNUSED(opline->op2);
        opline->extended_value = type;
        GET_NODE(result, opline->result);
}

看opline->opcode = ZEND_CAST这行。它的opcode是ZEND_CAST。继续搜ZEND_CAST

root@hackers365-HP-Compaq-Elite-8300-SFF:/data/software/php-5.5.14/Zend# g 'ZEND_CAST'
zend_compile.c:6061:	opline->opcode = ZEND_CAST;
匹配到二进制文件 zend_execute.o
zend_vm_def.h:3674:ZEND_VM_HANDLER(21, ZEND_CAST, CONST|TMP|VAR|CV, ANY)
zend_vm_execute.h:2581:static int ZEND_FASTCALL  ZEND_CAST_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
zend_vm_execute.h:7893:static int ZEND_FASTCALL  ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
zend_vm_execute.h:13210:static int ZEND_FASTCALL  ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
zend_vm_execute.h:30804:static int ZEND_FASTCALL  ZEND_CAST_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
zend_vm_execute.h:41386:  	ZEND_CAST_SPEC_CONST_HANDLER,
zend_vm_execute.h:41387:  	ZEND_CAST_SPEC_CONST_HANDLER,
zend_vm_execute.h:41388:  	ZEND_CAST_SPEC_CONST_HANDLER,
zend_vm_execute.h:41389:  	ZEND_CAST_SPEC_CONST_HANDLER,
zend_vm_execute.h:41390:  	ZEND_CAST_SPEC_CONST_HANDLER,
zend_vm_execute.h:41391:  	ZEND_CAST_SPEC_TMP_HANDLER,
zend_vm_execute.h:41392:  	ZEND_CAST_SPEC_TMP_HANDLER,
zend_vm_execute.h:41393:  	ZEND_CAST_SPEC_TMP_HANDLER,

看这一行
zend_vm_execute.h:7893:static int ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)

static int ZEND_FASTCALL  ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
        USE_OPLINE
        zend_free_op free_op1;
        zval *expr;
        zval *result = &EX_T(opline->result.var).tmp_var;

        SAVE_OPLINE();
        expr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC);

        if (opline->extended_value != IS_STRING) {
                ZVAL_COPY_VALUE(result, expr);
                if (!1) {
                        zendi_zval_copy_ctor(*result);
                }
        }
        switch (opline->extended_value) {
                case IS_NULL:
                        convert_to_null(result);
                        break;
                case IS_BOOL:
                        convert_to_boolean(result);
                        break;
                case IS_LONG:
                        convert_to_long(result);
                        break;
                case IS_DOUBLE:
                        convert_to_double(result);
                        break;
                case IS_STRING: {
                        zval var_copy;
                        int use_copy;

                        zend_make_printable_zval(expr, &var_copy, &use_copy);
                        if (use_copy) {
                                ZVAL_COPY_VALUE(result, &var_copy);
                                if (1) {
                                        zval_dtor(free_op1.var);
                                }
                        } else {
                                ZVAL_COPY_VALUE(result, expr);
                                if (!1) {
                                        zendi_zval_copy_ctor(*result);
                                }
                        }
                        break;
                }
                case IS_ARRAY:
                        convert_to_array(result);
                        break;
                case IS_OBJECT:
                        convert_to_object(result);
                        break;
        }

        CHECK_EXCEPTION();
        ZEND_VM_NEXT_OPCODE();
}

好了。看着位置了。。opline->extended_value这行在
zend_language_parser.y:802: | T_BOOL_CAST expr { zend_do_cast(&$$, &$2, IS_BOOL TSRMLS_CC); }
定义为IS_BOOL。所以这里继续看convert_to_boolean这个函数

root@hackers365-HP-Compaq-Elite-8300-SFF:/data/software/php-5.5.14/Zend# g 'convert_to_boolean('
zend_execute.h:135:						convert_to_boolean(tmp);
zend_operators.c:292:#define zendi_convert_to_boolean(op, holder, result)				\
zend_operators.c:294:		convert_to_boolean(op);										\
zend_operators.c:321:				convert_to_boolean(&(holder));						\
zend_operators.c:511:ZEND_API void convert_to_boolean(zval *op) /* {{{ */

找着了。在zend_operators.c:511行这里。。


ZEND_API void convert_to_boolean(zval *op) /* {{{ */
{
        int tmp;

        switch (Z_TYPE_P(op)) {
                case IS_BOOL:
                        break;
                case IS_NULL:
                        Z_LVAL_P(op) = 0;
                        break;
                case IS_RESOURCE: {
                                TSRMLS_FETCH();

                                zend_list_delete(Z_LVAL_P(op));
                        }
                        /* break missing intentionally */
                case IS_LONG:
                        Z_LVAL_P(op) = (Z_LVAL_P(op) ? 1 : 0);
                        break;
                case IS_DOUBLE:
                        Z_LVAL_P(op) = (Z_DVAL_P(op) ? 1 : 0);
                        break;
                case IS_STRING:
                        {
                                char *strval = Z_STRVAL_P(op);

                                if (Z_STRLEN_P(op) == 0
                                        || (Z_STRLEN_P(op)==1 && Z_STRVAL_P(op)[0]=='0')) {
                                        Z_LVAL_P(op) = 0;
                                } else {
                                        Z_LVAL_P(op) = 1;
                                }
                                STR_FREE(strval);
                        }
                        break;
                case IS_ARRAY:
                        tmp = (zend_hash_num_elements(Z_ARRVAL_P(op))?1:0);
                        zval_dtor(op);
                        Z_LVAL_P(op) = tmp;
                        break;
                case IS_OBJECT:
                        {
                                zend_bool retval = 1;
                                TSRMLS_FETCH();

                                convert_object_to_type(op, IS_BOOL, convert_to_boolean);

                                if (Z_TYPE_P(op) == IS_BOOL) {
                                        return;
                                }

                                zval_dtor(op);
                                ZVAL_BOOL(op, retval);
                                break;
                        }
                default:
                        zval_dtor(op);
                        Z_LVAL_P(op) = 0;
                        break;
        }
        Z_TYPE_P(op) = IS_BOOL;
}

终于看着核心位置了。。这里就是做转换的函数。。那么我们接着上边手册的看

布尔值 FALSE 本身
整型值 0(零)
浮点型值 0.0(零)
空字符串,以及字符串 "0"
不包括任何元素的数组
不包括任何成员变量的对象(仅 PHP 4.0 适用)
特殊类型 NULL(包括尚未赋值的变量)
从空标记生成的 SimpleXML 对象

1. 布尔值本身。这就不用说了。

             case IS_BOOL:
                   break;

直接break了。。

2. 整型值/浮点值

                case IS_LONG:
                        Z_LVAL_P(op) = (Z_LVAL_P(op) ? 1 : 0);
                        break;

跟c语言的定义一样。如果表达式的值为真就是1(即bool的true),如果为假就是0(false)

3. 空字符串

                 if (Z_STRLEN_P(op) == 0
                         || (Z_STRLEN_P(op)==1 && Z_STRVAL_P(op)[0]=='0')) {
                         Z_LVAL_P(op) = 0;
                  } else {
                         Z_LVAL_P(op) = 1;
                  }

如果字符串长度为空或只有一个值’0’就为假。。否则为真

4. 不包括任何元素的数组

                case IS_ARRAY:
                        tmp = (zend_hash_num_elements(Z_ARRVAL_P(op))?1:0);
                        zval_dtor(op);
                        Z_LVAL_P(op) = tmp;

只看数组元素为不为空。为空就为假。否则为真

5. 特殊类型 NULL(包括尚未赋值的变量)

                case IS_NULL:
                        Z_LVAL_P(op) = 0;
                        break;

不用解释。。。

好了。到这里已经完整的从源码来解释了强制类型转换到bool的各种情况。。