Skip to content

Commit

Permalink
Implement timecop_gettimeofday() and timecop_microtime()
Browse files Browse the repository at this point in the history
  • Loading branch information
hnw committed Apr 17, 2016
1 parent be52867 commit c58f43d
Show file tree
Hide file tree
Showing 6 changed files with 372 additions and 6 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ extension=timecop.so
- `strtotime()`
- `strftime()`
- `gmstrftime()`
- `microtime()`
- `gettimeofday()`
- `unixtojd()`
- `DateTime::_construct()`
- `DateTime::createFromFormat()` (PHP >= 5.3.4)
Expand Down Expand Up @@ -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)
Expand All @@ -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()"
Expand Down
13 changes: 13 additions & 0 deletions php_timecop.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
32 changes: 32 additions & 0 deletions tests/017.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
--TEST--
Check for timecop_microtime
--SKIPIF--
<?php
extension_loaded('timecop') or die('skip timecop not available');
$required_func = array("timecop_freeze", "timecop_travel", "timecop_return", "timecop_microtime");
foreach ($required_func as $func_name) {
if (!function_exists($func_name)) {
die("skip $func_name() function is not available.");
}
}
?>
--INI--
date.timezone=America/Los_Angeles
--FILE--
<?php
timecop_freeze(172800);
var_dump(timecop_microtime());
var_dump(timecop_microtime(true));
timecop_travel(172800);
var_dump(timecop_microtime());
var_dump(timecop_microtime(true));
timecop_return();
var_dump(timecop_microtime());
var_dump(timecop_microtime(true));
--EXPECTREGEX--
string\(17\) "0.00000000 172800"
float\(172800\)
string\(17\) "0.\d{8} 1728\d{2}"
float\(1728\d{2}.*\)
string\(21\) "0.\d{8} \d{10}"
float\(\d{10}.*\)
53 changes: 53 additions & 0 deletions tests/018.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
--TEST--
Check for timecop_gettimeofday
--SKIPIF--
<?php
extension_loaded('timecop') or die('skip timecop not available');
$required_func = array("timecop_freeze", "timecop_travel", "timecop_return", "timecop_gettimeofday");
foreach ($required_func as $func_name) {
if (!function_exists($func_name)) {
die("skip $func_name() function is not available.");
}
}
?>
--INI--
date.timezone=America/Los_Angeles
--FILE--
<?php
timecop_freeze(12300000);
$timeofday1 = timecop_gettimeofday();
var_dump($timeofday1['sec']);
var_dump($timeofday1['usec']);
var_dump($timeofday1['minuteswest']);
var_dump($timeofday1['dsttime']);
var_dump(timecop_gettimeofday(true));
timecop_travel(12300000);
$timeofday2 = timecop_gettimeofday();
var_dump($timeofday2['sec']);
var_dump($timeofday2['usec']);
var_dump($timeofday2['minuteswest']);
var_dump($timeofday2['dsttime']);
var_dump(timecop_gettimeofday(true));
timecop_return();
$timeofday3 = timecop_gettimeofday();
var_dump($timeofday3['sec']);
var_dump($timeofday3['usec']);
var_dump($timeofday3['minuteswest']);
var_dump($timeofday3['dsttime']);
var_dump(timecop_gettimeofday(true));
--EXPECTREGEX--
int\(12300000\)
int\(0\)
int\(420\)
int\(1\)
float\(12300000\)
int\(123000\d{2}\)
int\(\d+\)
int\(420\)
int\([01]\)
float\(123000\d{2}(\.\d+)?\)
int\(\d+\)
int\(\d+\)
int\(4[28]0\)
int\([01]\)
float\(\d+(\.\d+)?\)
139 changes: 137 additions & 2 deletions timecop_php5.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,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"),
#if PHP_VERSION_ID >= 50300
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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(&timestamp, 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, &timestamp);
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, &timestamp);
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)
Expand Down Expand Up @@ -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) {
Expand Down
Loading

0 comments on commit c58f43d

Please sign in to comment.