diff --git a/README.md b/README.md index 7222f09..dfa623c 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,8 @@ extension=timecop.so - `strtotime()` - `strftime()` - `gmstrftime()` + - `microtime()` + - `gettimeofday()` - `unixtojd()` - `DateTime::_construct()` - `DateTime::createFromFormat()` (PHP >= 5.3.4) @@ -85,10 +87,11 @@ var_dump($new_time == time()); // bool(false) ## CHANGELOG -###version 1.1.0(alpha), 2016/04/15 +###version 1.1.0(alpha), 2016/04/18 - Support PHP 7.0.x - Now `new DateTime()` always returns `DateTime` instance - The previous version returns `TimecopDateTime` instance when `timecop.func_override=1`. +- Implement `timecop_gettimeofday()` and `timecop_microtime()` ###version 1.0.6, 2016/04/15 - Fix #10 (Timecop segfaults when set_error_handler throws an exception) @@ -114,7 +117,7 @@ var_dump($new_time == time()); // bool(false) - Implement `timecop_date_create()` -###version 1.0.1, 2013/03/04 +###Version 1.0.1, 2013/03/04 - Implement time traveling feature for `TimecopDateTime::__construct()` with 1 or 2 arguments - The previous version works correctly only for no arguments calling: "new TimecopDateTime()" diff --git a/php_timecop.h b/php_timecop.h index d4ff438..7e9f612 100644 --- a/php_timecop.h +++ b/php_timecop.h @@ -55,6 +55,10 @@ PHP_FUNCTION(timecop_localtime); PHP_FUNCTION(timecop_strtotime); PHP_FUNCTION(timecop_strftime); PHP_FUNCTION(timecop_gmstrftime); +#ifdef HAVE_GETTIMEOFDAY +PHP_FUNCTION(timecop_microtime); +PHP_FUNCTION(timecop_gettimeofday); +#endif PHP_FUNCTION(timecop_unixtojd); PHP_FUNCTION(timecop_date_create); PHP_FUNCTION(timecop_date_create_from_format); @@ -122,6 +126,15 @@ struct timecop_override_class_entry { char *save_method; }; +#define timecop_call_orig_method_with_0_params(obj, obj_ce, fn_proxy, function_name, retval) \ + zend_call_method(obj, obj_ce, fn_proxy, ORIG_FUNC_NAME(function_name), ORIG_FUNC_NAME_SIZEOF(function_name)-1, retval, 0, NULL, NULL TSRMLS_CC) + +#define timecop_call_orig_method_with_1_params(obj, obj_ce, fn_proxy, function_name, retval, arg1) \ + zend_call_method(obj, obj_ce, fn_proxy, ORIG_FUNC_NAME(function_name), ORIG_FUNC_NAME_SIZEOF(function_name)-1, retval, 1, arg1, NULL TSRMLS_CC) + +#define timecop_call_orig_method_with_2_params(obj, obj_ce, fn_proxy, function_name, retval, arg1, arg2) \ + zend_call_method(obj, obj_ce, fn_proxy, ORIG_FUNC_NAME(function_name), ORIG_FUNC_NAME_SIZEOF(function_name)-1, retval, 2, arg1, arg2 TSRMLS_CC) + /* In every utility function you add that needs to use variables in php_timecop_globals, call TSRMLS_FETCH(); after declaring other variables used by that function, or better yet, pass in TSRMLS_CC diff --git a/tests/017.phpt b/tests/017.phpt new file mode 100644 index 0000000..c8dd7f7 --- /dev/null +++ b/tests/017.phpt @@ -0,0 +1,32 @@ +--TEST-- +Check for timecop_microtime +--SKIPIF-- + +--INI-- +date.timezone=America/Los_Angeles +--FILE-- + +--INI-- +date.timezone=America/Los_Angeles +--FILE-- += 50300 @@ -141,6 +145,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_gmstrftime, 0, 0, 1) ZEND_ARG_INFO(0, timestamp) ZEND_END_ARG_INFO() +#ifdef HAVE_GETTIMEOFDAY +ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_microtime, 0, 0, 0) + ZEND_ARG_INFO(0, get_as_float) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_gettimeofday, 0, 0, 0) + ZEND_ARG_INFO(0, get_as_float) +ZEND_END_ARG_INFO() +#endif + ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_unixtojd, 0, 0, 0) ZEND_ARG_INFO(0, timestamp) ZEND_END_ARG_INFO() @@ -183,6 +197,10 @@ const zend_function_entry timecop_functions[] = { PHP_FE(timecop_strtotime, arginfo_timecop_strtotime) PHP_FE(timecop_strftime, arginfo_timecop_strftime) PHP_FE(timecop_gmstrftime, arginfo_timecop_gmstrftime) +#ifdef HAVE_GETTIMEOFDAY + PHP_FE(timecop_microtime, arginfo_timecop_microtime) + PHP_FE(timecop_gettimeofday, arginfo_timecop_gettimeofday) +#endif PHP_FE(timecop_unixtojd, arginfo_timecop_unixtojd) PHP_FE(timecop_date_create, arginfo_timecop_date_create) #if PHP_VERSION_ID >= 50300 @@ -708,7 +726,7 @@ static int fix_datetime_timestamp(zval **datetime_obj, zval *time, zval *timezon } /* - * $fixed_timestamp = timecop_strtotime(); + * $fixed_timestamp = timecop_strtotime($time); */ zend_call_method_with_1_params(NULL, NULL, NULL, "timecop_strtotime", &fixed_timestamp, time); @@ -962,6 +980,123 @@ PHP_FUNCTION(timecop_gmstrftime) } /* }}} */ +#ifdef HAVE_GETTIMEOFDAY + +#define MICRO_IN_SEC 1000000.00 +#define SEC_IN_MIN 60 + +static void _timecop_gettimeofday(INTERNAL_FUNCTION_PARAMETERS, int mode) +{ + zend_bool get_as_float = 0; + long fixed_sec = 0, fixed_usec = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &get_as_float) == FAILURE) { + return; + } + + switch (TIMECOP_G(timecop_mode)) { + case TIMECOP_MODE_FREEZE: + fixed_sec = TIMECOP_G(freezed_timestamp); + fixed_usec = 0; + break; + case TIMECOP_MODE_TRAVEL: + { + zval *timeofday, **zv_sec, **zv_usec; + timecop_call_orig_method_with_0_params(NULL, NULL, NULL, "gettimeofday", &timeofday); + if (zend_hash_find(Z_ARRVAL_P(timeofday), "sec", sizeof("sec"), (void **)&zv_sec) != FAILURE && + zv_sec) { + fixed_sec = Z_LVAL_PP(zv_sec) + TIMECOP_G(travel_offset); + } + if (zend_hash_find(Z_ARRVAL_P(timeofday), "usec", sizeof("usec"), (void **)&zv_usec) != FAILURE && + zv_usec) { + fixed_usec = Z_LVAL_PP(zv_usec); + } + zval_ptr_dtor(&timeofday); + } + break; + default: + { + const char *func_name; + size_t func_name_len; + zval *arg1 = NULL, *retval_ptr, tmp; + int param_count = 0; + + if (mode) { + func_name = ORIG_FUNC_NAME("gettimeofday"); + func_name_len = ORIG_FUNC_NAME_SIZEOF("gettimeofday")-1; + } else { + func_name = ORIG_FUNC_NAME("microtime"); + func_name_len = ORIG_FUNC_NAME_SIZEOF("microtime")-1; + } + if (get_as_float) { + INIT_ZVAL(tmp); + ZVAL_TRUE(&tmp); + arg1 = &tmp; + param_count = 1; + } + zend_call_method(NULL, NULL, NULL, func_name, func_name_len, &retval_ptr, param_count, arg1, NULL TSRMLS_CC); + if (retval_ptr) { + RETURN_ZVAL(retval_ptr, 1, 1); + } + + return; + } + } + + if (get_as_float) { + RETURN_DOUBLE((double)(fixed_sec + fixed_usec / MICRO_IN_SEC)); + } + if (mode) { + zval *zv_offset, *zv_dst, format, timestamp; + long offset = 0, is_dst = 0; + + INIT_ZVAL(timestamp); + ZVAL_LONG(×tamp, fixed_sec); + + /* offset */ + INIT_ZVAL(format); + ZVAL_STRING(&format, "Z", 0); + timecop_call_orig_method_with_2_params(NULL, NULL, NULL, "date", &zv_offset, &format, ×tamp); + convert_to_long(zv_offset); + offset = Z_LVAL_P(zv_offset); + zval_ptr_dtor(&zv_offset); + + /* is_dst */ + ZVAL_STRING(&format, "I", 0); + timecop_call_orig_method_with_2_params(NULL, NULL, NULL, "date", &zv_dst, &format, ×tamp); + convert_to_long(zv_dst); + is_dst = Z_LVAL_P(zv_dst); + zval_ptr_dtor(&zv_dst); + + array_init(return_value); + add_assoc_long(return_value, "sec", fixed_sec); + add_assoc_long(return_value, "usec", fixed_usec); + add_assoc_long(return_value, "minuteswest", -offset / SEC_IN_MIN); + add_assoc_long(return_value, "dsttime", is_dst); + } else { + char ret[100]; + snprintf(ret, 100, "%.8F %ld", fixed_usec / MICRO_IN_SEC, fixed_sec); + RETURN_STRING(ret, 1); + } +} + +/* {{{ proto mixed microtime([bool get_as_float]) + Returns either a string or a float containing the current time in seconds and microseconds */ +PHP_FUNCTION(timecop_microtime) +{ + _timecop_gettimeofday(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto array gettimeofday([bool get_as_float]) + Returns the current time as array */ +PHP_FUNCTION(timecop_gettimeofday) +{ + _timecop_gettimeofday(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ +#endif + /* {{{ proto int timecop_unixtojd([int timestamp]) Convert UNIX timestamp to Julian Day */ PHP_FUNCTION(timecop_unixtojd) @@ -1133,7 +1268,7 @@ static inline void timecop_call_constructor(zval **obj, zend_class_entry *ce, zv static void timecop_call_constructor_ex(zval **obj, zend_class_entry *ce, zval ***params, int param_count, int call_original TSRMLS_DC) { zval *arg1 = NULL, *arg2 = NULL; - char *func_name; + const char *func_name; size_t func_name_len; if (param_count > 2) { diff --git a/timecop_php7.c b/timecop_php7.c index 1ae09b1..9740af2 100644 --- a/timecop_php7.c +++ b/timecop_php7.c @@ -53,6 +53,10 @@ static const struct timecop_override_func_entry timecop_override_func_table[] = TIMECOP_OFE("strtotime"), TIMECOP_OFE("strftime"), TIMECOP_OFE("gmstrftime"), +#ifdef HAVE_GETTIMEOFDAY + TIMECOP_OFE("microtime"), + TIMECOP_OFE("gettimeofday"), +#endif TIMECOP_OFE("unixtojd"), TIMECOP_OFE("date_create"), TIMECOP_OFE("date_create_from_format"), @@ -135,6 +139,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_gmstrftime, 0, 0, 1) ZEND_ARG_INFO(0, timestamp) ZEND_END_ARG_INFO() +#ifdef HAVE_GETTIMEOFDAY +ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_microtime, 0, 0, 0) + ZEND_ARG_INFO(0, get_as_float) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_gettimeofday, 0, 0, 0) + ZEND_ARG_INFO(0, get_as_float) +ZEND_END_ARG_INFO() +#endif + ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_unixtojd, 0, 0, 0) ZEND_ARG_INFO(0, timestamp) ZEND_END_ARG_INFO() @@ -166,6 +180,10 @@ const zend_function_entry timecop_functions[] = { PHP_FE(timecop_strtotime, arginfo_timecop_strtotime) PHP_FE(timecop_strftime, arginfo_timecop_strftime) PHP_FE(timecop_gmstrftime, arginfo_timecop_gmstrftime) +#ifdef HAVE_GETTIMEOFDAY + PHP_FE(timecop_microtime, arginfo_timecop_microtime) + PHP_FE(timecop_gettimeofday, arginfo_timecop_gettimeofday) +#endif PHP_FE(timecop_unixtojd, arginfo_timecop_unixtojd) PHP_FE(timecop_date_create, arginfo_timecop_date_create) PHP_FE(timecop_date_create_from_format, arginfo_timecop_date_create_from_format) @@ -661,7 +679,7 @@ static int fix_datetime_timestamp(zval *datetime_obj, zval *time, zval *timezone } /* - * $fixed_timestamp = timecop_strtotime(); + * $fixed_timestamp = timecop_strtotime($time); */ zend_call_method_with_1_params(NULL, NULL, NULL, "timecop_strtotime", &fixed_timestamp, time); @@ -910,6 +928,118 @@ PHP_FUNCTION(timecop_gmstrftime) } /* }}} */ +#ifdef HAVE_GETTIMEOFDAY + +#define MICRO_IN_SEC 1000000.00 +#define SEC_IN_MIN 60 + +static void _timecop_gettimeofday(INTERNAL_FUNCTION_PARAMETERS, int mode) +{ + zend_bool get_as_float = 0; + long fixed_sec = 0, fixed_usec = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &get_as_float) == FAILURE) { + return; + } + + switch (TIMECOP_G(timecop_mode)) { + case TIMECOP_MODE_FREEZE: + fixed_sec = TIMECOP_G(freezed_timestamp); + fixed_usec = 0; + break; + case TIMECOP_MODE_TRAVEL: + { + zval timeofday, *zv_sec, *zv_usec; + timecop_call_orig_method_with_0_params(NULL, NULL, NULL, "gettimeofday", &timeofday); + zv_sec = zend_hash_str_find(Z_ARR(timeofday), "sec", sizeof("sec")-1); + if (zv_sec) { + fixed_sec = Z_LVAL_P(zv_sec) + TIMECOP_G(travel_offset); + } + zv_usec = zend_hash_str_find(Z_ARR(timeofday), "usec", sizeof("usec")-1); + if (zv_sec) { + fixed_usec = Z_LVAL_P(zv_usec); + } + zval_ptr_dtor(&timeofday); + } + break; + default: + { + const char *func_name; + size_t func_name_len; + zval *arg1 = NULL, tmp; + int param_count = 0; + + if (mode) { + func_name = ORIG_FUNC_NAME("gettimeofday"); + func_name_len = ORIG_FUNC_NAME_SIZEOF("gettimeofday")-1; + } else { + func_name = ORIG_FUNC_NAME("microtime"); + func_name_len = ORIG_FUNC_NAME_SIZEOF("microtime")-1; + } + if (get_as_float) { + ZVAL_TRUE(&tmp); + arg1 = &tmp; + param_count = 1; + } + zend_call_method(NULL, NULL, NULL, func_name, func_name_len, return_value, param_count, arg1, NULL); + return; + } + } + + if (get_as_float) { + RETURN_DOUBLE((double)(fixed_sec + fixed_usec / MICRO_IN_SEC)); + } + if (mode) { + zval zv_offset, zv_dst, format, timestamp; + long offset = 0, is_dst = 0; + + ZVAL_LONG(×tamp, fixed_sec); + + /* offset */ + ZVAL_STRING(&format, "Z"); + timecop_call_orig_method_with_2_params(NULL, NULL, NULL, "date", &zv_offset, &format, ×tamp); + convert_to_long(&zv_offset); + offset = Z_LVAL(zv_offset); + zval_ptr_dtor(&zv_offset); + zval_ptr_dtor(&format); + + /* is_dst */ + ZVAL_STRING(&format, "I"); + timecop_call_orig_method_with_2_params(NULL, NULL, NULL, "date", &zv_dst, &format, ×tamp); + convert_to_long(&zv_dst); + is_dst = Z_LVAL(zv_dst); + zval_ptr_dtor(&zv_dst); + zval_ptr_dtor(&format); + + array_init(return_value); + add_assoc_long(return_value, "sec", fixed_sec); + add_assoc_long(return_value, "usec", fixed_usec); + add_assoc_long(return_value, "minuteswest", -offset / SEC_IN_MIN); + add_assoc_long(return_value, "dsttime", is_dst); + } else { + char ret[100]; + snprintf(ret, 100, "%.8F %ld", fixed_usec / MICRO_IN_SEC, fixed_sec); + RETURN_STRING(ret); + } +} + +/* {{{ proto mixed microtime([bool get_as_float]) + Returns either a string or a float containing the current time in seconds and microseconds */ +PHP_FUNCTION(timecop_microtime) +{ + _timecop_gettimeofday(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto array gettimeofday([bool get_as_float]) + Returns the current time as array */ +PHP_FUNCTION(timecop_gettimeofday) +{ + _timecop_gettimeofday(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ +#endif + /* {{{ proto int timecop_unixtojd([int timestamp]) Convert UNIX timestamp to Julian Day */ PHP_FUNCTION(timecop_unixtojd) @@ -1026,7 +1156,7 @@ static inline void timecop_call_constructor(zval *obj, zend_class_entry *ce, zva static void timecop_call_constructor_ex(zval *obj, zend_class_entry *ce, zval *params, int param_count, int call_original) { zval *arg1 = NULL, *arg2 = NULL; - char *func_name; + const char *func_name; size_t func_name_len; if (param_count > 2) {