实现大致如下:
$expiretime = 2014-05-01 00:00:00;$currenttime = date('y-m-d h:i:s', time());if($currenttime < $expiretime) { return false;} else { return true;}
如果两个时间需要进行比较,通常是转换成unix时间戳,用两个int型的数字进行比较。该实现却特意将时间表示成string,然后对两个string进行比较运算。
撇开写法不谈,我很好奇的是php内部是如何进行比较的。
闲话少说,还是从源码开始跟踪。
编译期在zend_language_parse.y中可以发现类似下述语法:
result.u.var = get_temporary_variable(cg(active_op_array)); opline->op1 = *op1; opline->op2 = *op2; *result = opline->result;}
该函数并没有做什么特别的处理,仅仅是简单保存了opcode、操作数1和操作数2。
执行期根据opcode,跳转到相应的处理函数:zend_is_smaller_spec_const_const_handler。
static int zend_fastcall zend_is_smaller_spec_const_const_handler(zend_opcode_handler_args){ zend_op *opline = ex(opline); zval *result = &ex_t(opline->result.u.var).tmp_var; compare_function(result, &opline->op1.u.constant, &opline->op2.u.constant tsrmls_cc); zval_bool(result, (z_lval_p(result) 2 && *str == '0' && (str[1] == 'x' || str[1] == 'x')) { base = 16; ptr += 2; } /* 忽略后续的若干0 */ while (*ptr == '0') { ptr++; } /* 计算数字的位数,并决定是整型还是浮点 */ for (type = is_long; !(digits >= max_length_of_long && (dval || allow_errors == 1)); digits++, ptr++) {check_digits: if (zend_is_digit(*ptr) || (base == 16 && zend_is_xdigit(*ptr))) { continue; } else if (base == 10) { if (*ptr == '.' && dp_or_e < 1) { goto process_double; } else if ((*ptr == 'e' || *ptr == 'e') && dp_or_e = max_length_of_long) { dp_or_e = -1; goto process_double; } } else if (!(digits < sizeof_long * 2 || (digits == sizeof_long * 2 && ptr[-digits] <= '7'))) { if (dval) { local_dval = zend_hex_strtod(str, (char **)&ptr); } type = is_double; } } else if (*ptr == '.' && zend_is_digit(ptr[1])) { // 处理浮点数 } else { return 0; } // 如果不允许容错,则报错退出 if (ptr != str + length) { if (!allow_errors) { return 0; } if (allow_errors == -1) { zend_error(e_notice, a non well formed numeric value encountered); } } // 允许容错,则尝试将str转成数字 if (type == is_long) { if (digits == max_length_of_long - 1) { int cmp = strcmp(&ptr[-digits], long_min_digits); if (!(cmp < 0 || (cmp == 0 && *str == '-'))) { if (dval) { *dval = zend_strtod(str, null); } return is_double; } } if (lval) { *lval = strtol(str, null, base); } return is_long; } else { if (dval) { *dval = local_dval; } return is_double; }}
代码比较长,不过仔细阅读,str转num的规则还是很清晰的。
尤其注意的是allow_errors这个参数,它直接决定了本例中无法将“2014-05-01 00:00:00”转化成数字。
因而最后其实“2014-04-17 00:00:00” < “2014-05-01 00:00:00” 的运行是走的memcmp分支。
既然是memcmp,便不难理解为何文章开始提到的写法也能正确运行。
容错转换何时allow_errors为true呢?一个极好的例子便是zend_parse_parameters,zend_parse_parameters的实现不再细述,有兴趣的读者可以自行研究。其中调用is_numeric_string时将allow_errors置为了-1。
举个例子:
static void php_date(internal_function_parameters, int localtime){ char *format; int format_len; long ts; char *string; // 期望的第二个参数为timestamp,为long // 假设上层调用时,误传入了string,那么zend_parse_parameters依然会尽可能的尝试将string解析为long if (zend_parse_parameters(zend_num_args() tsrmls_cc, s|l, &format, &format_len, &ts) == failure) { return_false; } if (zend_num_args() == 1) { ts = time(null); } string = php_format_date(format, format_len, ts, localtime tsrmls_cc); retval_string(string, 0);}
这是php的date函数内部实现。
在我们调用date时,如果将第二个参数传入string,效果如下:
echo date('y-m-d', '0-1-2');// 输出php notice: a non well formed numeric value encountered in command line code on line 11970-01-01
虽然报出notice级别的错误,但依然成功将'0-1-2'转成了0
http://www.bkjia.com/phpjc/755938.htmlwww.bkjia.comtruehttp://www.bkjia.com/phpjc/755938.htmltecharticle项目中有个功能是比较会员是否过期,review同事的代码,发现其写法比较奇葩,但线上竟也未出现bug。 实现大致如下: $expiretime = 2014-05...
