From 2b25be6a24c587d2c5dbc68c25e5b0351ef2032f Mon Sep 17 00:00:00 2001 From: Go Kudo Date: Mon, 9 Sep 2024 09:18:41 +0000 Subject: [PATCH 1/2] fix DateTime::createFromFormat --- ext/hook.c | 78 ++++++------------- .../classes/datetime_create_from_format.phpt | 2 +- ...datetime_immutable_create_from_format.phpt | 2 +- ext/tests/classes/gh5.phpt | 29 +++++++ .../functions/date_create_from_format.phpt | 2 +- .../date_create_immutable_from_format.phpt | 2 +- 6 files changed, 58 insertions(+), 57 deletions(-) create mode 100644 ext/tests/classes/gh5.phpt diff --git a/ext/hook.c b/ext/hook.c index 531fc3d..6d575de 100644 --- a/ext/hook.c +++ b/ext/hook.c @@ -107,13 +107,11 @@ static inline void apply_interval(timelib_time **time, timelib_rel_time *interva #define DEFINE_CREATE_FROM_FORMAT_EX(fname, name) \ static void hook_##fname(INTERNAL_FUNCTION_PARAMETERS) { \ CHECK_STATE(name); \ + timelib_time orig; \ \ - php_date_obj *date; \ - timelib_time *time = NULL; \ - zval *params, orig_return_value; \ - uint32_t param_count = 0; \ - timelib_time *current_time = get_current_timelib_time(); \ - timelib_time *shifted_time = get_shifted_timelib_time(); \ + zend_object *object; \ + php_date_obj *object_date; \ + timelib_rel_time interval; \ \ CALL_ORIGINAL_FUNCTION(name); \ \ @@ -121,46 +119,24 @@ static inline void apply_interval(timelib_time **time, timelib_rel_time *interva RETURN_FALSE; \ } \ \ - date = Z_PHPDATE_P(return_value); \ - time = date->time; \ - \ - zend_parse_parameters(ZEND_NUM_ARGS(), "+", ¶ms, ¶m_count); \ + memcpy(&orig, Z_PHPDATE_P(return_value)->time, sizeof(timelib_time)); \ + /* Note: createFromFormat does not handle microseconds, so a wait in seconds is necessary */ \ + usleep(((uint32_t) COLOPL_TS_G(usleep_sec)) > 0 ? ((uint32_t) COLOPL_TS_G(usleep_sec) * 1000000) : 1000000); \ + CALL_ORIGINAL_FUNCTION(name); \ \ - if (memchr(Z_STRVAL(params[0]), '!', Z_STRLEN(params[0])) != NULL) { \ - /* fixed (unix epoch) */ \ + if (Z_PHPDATE_P(return_value)->time->y == orig.y && \ + Z_PHPDATE_P(return_value)->time->m == orig.m && \ + Z_PHPDATE_P(return_value)->time->d == orig.d && \ + Z_PHPDATE_P(return_value)->time->h == orig.h && \ + Z_PHPDATE_P(return_value)->time->i == orig.i && \ + Z_PHPDATE_P(return_value)->time->s == orig.s && \ + Z_PHPDATE_P(return_value)->time->us == orig.us \ + ) { \ return; \ } \ \ - /* Fixed check */ \ - if (current_time->y == time->y) { \ - time->y = shifted_time->y; \ - } \ - if (current_time->m == time->m) { \ - time->m = shifted_time->m; \ - } \ - if (current_time->d == time->d) { \ - time->d = shifted_time->d; \ - } \ - if (current_time->h == time->h) { \ - time->h = shifted_time->h; \ - } \ - if (current_time->i == time->i) { \ - time->i = shifted_time->i; \ - } \ - /* Maybe sometimes mistake, but not bothered. */ \ - if (llabs(current_time->s - time->s) <= 3) { \ - time->s = shifted_time->s; \ - } \ - if (llabs(current_time->us - time->us) <= 10) { \ - time->us = shifted_time->us; \ - } \ - \ - /* Apply changes */ \ - timelib_update_ts(time, NULL); \ - \ - /* Clean up */ \ - timelib_time_dtor(current_time); \ - timelib_time_dtor(shifted_time); \ + get_shift_interval(&interval); \ + apply_interval(&Z_PHPDATE_P(return_value)->time, &interval); \ } #define DEFINE_CREATE_FROM_FORMAT(name) \ @@ -224,17 +200,13 @@ static inline bool is_fixed_time_str(zend_string *datetime, zval *timezone) before = Z_PHPDATE_P(&before_zv); php_date_initialize(before, ZSTR_VAL(datetime), ZSTR_LEN(datetime), NULL, timezone, 0); - /* - * Check format is absolute. - * FIXME: Need more instead method. - */ usleep(((uint32_t) COLOPL_TS_G(usleep_sec)) > 0 ? (uint32_t) COLOPL_TS_G(usleep_sec) : 1); php_date_instantiate(ce, &after_zv); after = Z_PHPDATE_P(&after_zv); php_date_initialize(after, ZSTR_VAL(datetime), ZSTR_LEN(datetime), NULL, timezone, 0); - is_fixed_time_str = before->time->y == after->time->y + is_fixed_time_str = before->time->y == after->time->y && before->time->m == after->time->m && before->time->d == after->time->d && before->time->h == after->time->h @@ -254,7 +226,7 @@ static inline timelib_time *get_current_timelib_time() timelib_time *t = timelib_time_ctor(); timelib_unixtime2gmt(t, php_time()); - + return t; } @@ -328,7 +300,7 @@ static void hook_pdo_con(INTERNAL_FUNCTION_PARAMETERS) CALL_ORIGINAL_FUNCTION(pdo_con); - if (!dbh->driver || + if (!dbh->driver || strncmp(dbh->driver->driver_name, "mysql", 5) == 0 || dbh->methods != &COLOPL_TS_G(hooked_mysql_driver_methods) ) { @@ -437,10 +409,10 @@ static inline void date_create_common(INTERNAL_FUNCTION_PARAMETERS, zend_class_e php_date_instantiate(ce, return_value); if (!php_date_initialize( Z_PHPDATE_P(return_value), - (!time_str ? NULL : ZSTR_VAL(time_str)), + (!time_str ? NULL : ZSTR_VAL(time_str)), (!time_str ? 0 : ZSTR_LEN(time_str)), - NULL, - timezone_object, + NULL, + timezone_object, 0 )) { zval_ptr_dtor(return_value); @@ -778,7 +750,7 @@ void apply_request_time_hook() globals_server = zend_hash_str_find(&EG(symbol_table), "_SERVER", strlen("_SERVER")); if (!globals_server || Z_TYPE_P(globals_server) != IS_ARRAY) { - /* $_SERVER not defined */ + /* $_SERVER not defined */ return; } diff --git a/ext/tests/classes/datetime_create_from_format.phpt b/ext/tests/classes/datetime_create_from_format.phpt index 88d5ebc..92a1100 100644 --- a/ext/tests/classes/datetime_create_from_format.phpt +++ b/ext/tests/classes/datetime_create_from_format.phpt @@ -23,7 +23,7 @@ if (!$before_now instanceof \DateTime || !$before_static instanceof \DateTime || die('failed'); } -if ($after_now != $before_now && $interval->y === 3 && $interval->invert === 0) { +if ($after_now != $before_now && $interval->y >= 2 && $interval->invert === 0) { die('success'); } diff --git a/ext/tests/classes/datetime_immutable_create_from_format.phpt b/ext/tests/classes/datetime_immutable_create_from_format.phpt index 6a8106a..482f6be 100644 --- a/ext/tests/classes/datetime_immutable_create_from_format.phpt +++ b/ext/tests/classes/datetime_immutable_create_from_format.phpt @@ -23,7 +23,7 @@ if (!$before_now instanceof \DateTimeImmutable || !$before_static instanceof \Da die('failed'); } -if ($after_now != $before_now && $interval->y === 3 && $interval->invert === 0) { +if ($after_now != $before_now && $interval->y >= 2 && $interval->invert === 0) { die('success'); } diff --git a/ext/tests/classes/gh5.phpt b/ext/tests/classes/gh5.phpt new file mode 100644 index 0000000..85560a6 --- /dev/null +++ b/ext/tests/classes/gh5.phpt @@ -0,0 +1,29 @@ +--TEST-- +Check GitHub Issue - #5 (createFromFormat returns incorrect result during hook) +--EXTENSIONS-- +colopl_timeshifter +--FILE-- +format('Y-m-d H:i:s'), \PHP_EOL; + +\Colopl\ColoplTimeShifter\register_hook($interval); + +echo DateTime::createFromFormat('YmdHi', 202211091600)->format('Y-m-d H:i:s'), \PHP_EOL; + +\Colopl\ColoplTimeShifter\unregister_hook(); + +echo DateTime::createFromFormat('YmdHi', 202211091600)->format('Y-m-d H:i:s'), \PHP_EOL; + +\Colopl\ColoplTimeShifter\register_hook($interval); + +echo DateTime::createFromFormat('YmdHi', 202211091600)->format('Y-m-d H:i:s'), \PHP_EOL; + +?> +--EXPECT-- +2022-11-09 16:00:00 +2022-11-09 16:00:00 +2022-11-09 16:00:00 +2022-11-09 16:00:00 diff --git a/ext/tests/functions/date_create_from_format.phpt b/ext/tests/functions/date_create_from_format.phpt index ab8b832..3fdb42d 100644 --- a/ext/tests/functions/date_create_from_format.phpt +++ b/ext/tests/functions/date_create_from_format.phpt @@ -23,7 +23,7 @@ if (!$before_now instanceof \DateTime || !$before_static instanceof \DateTime || die('failed'); } -if ($after_now != $before_now && $interval->y === 3 && $interval->invert === 0) { +if ($after_now != $before_now && $interval->y >= 2 && $interval->invert === 0) { die('success'); } diff --git a/ext/tests/functions/date_create_immutable_from_format.phpt b/ext/tests/functions/date_create_immutable_from_format.phpt index 8c6ebc4..d0018ec 100644 --- a/ext/tests/functions/date_create_immutable_from_format.phpt +++ b/ext/tests/functions/date_create_immutable_from_format.phpt @@ -23,7 +23,7 @@ if (!$before_now instanceof \DateTimeImmutable || !$before_static instanceof \Da die('failed'); } -if ($after_now != $before_now && $interval->y === 3 && $interval->invert === 0) { +if ($after_now != $before_now && $interval->y >= 2 && $interval->invert === 0) { die('success'); } From 5fae76a93e74b131b44abcc585fe63c14fd93ee9 Mon Sep 17 00:00:00 2001 From: Go Kudo Date: Mon, 9 Sep 2024 09:45:52 +0000 Subject: [PATCH 2/2] fix memleak --- ext/hook.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/hook.c b/ext/hook.c index 6d575de..1a341ce 100644 --- a/ext/hook.c +++ b/ext/hook.c @@ -122,6 +122,7 @@ static inline void apply_interval(timelib_time **time, timelib_rel_time *interva memcpy(&orig, Z_PHPDATE_P(return_value)->time, sizeof(timelib_time)); \ /* Note: createFromFormat does not handle microseconds, so a wait in seconds is necessary */ \ usleep(((uint32_t) COLOPL_TS_G(usleep_sec)) > 0 ? ((uint32_t) COLOPL_TS_G(usleep_sec) * 1000000) : 1000000); \ + zval_ptr_dtor(return_value); \ CALL_ORIGINAL_FUNCTION(name); \ \ if (Z_PHPDATE_P(return_value)->time->y == orig.y && \