From 7bbcf855d12b74e73f177ffc1d186f06bf87790e Mon Sep 17 00:00:00 2001 From: Go Kudo Date: Wed, 11 Sep 2024 18:21:19 +0900 Subject: [PATCH] Wrong create_from_format (#7) more instead method for create_from_format --- ext/hook.c | 127 +++++++++++++++++++++++++------ ext/tests/{classes => }/gh5.phpt | 0 ext/tests/gh7.phpt | 83 ++++++++++++++++++++ 3 files changed, 187 insertions(+), 23 deletions(-) rename ext/tests/{classes => }/gh5.phpt (100%) create mode 100644 ext/tests/gh7.phpt diff --git a/ext/hook.c b/ext/hook.c index 1a341ce..08f2d79 100644 --- a/ext/hook.c +++ b/ext/hook.c @@ -15,7 +15,6 @@ | Author: Go Kudo | +----------------------------------------------------------------------+ */ - #include "hook.h" #include "php.h" @@ -32,9 +31,82 @@ # include #endif +typedef struct _format_flags_t { + bool y, m, d, h, i, s, us; +} format_flags_t; + +static inline void parse_format(char *format, format_flags_t *flags) { + memset(flags, 0, sizeof(format_flags_t)); + bool skip_next = false; + + for (char *c = format; *c != '\0'; c++) { + if (skip_next) { + skip_next = false; + continue; + } + switch (*c) { + case '\\': + skip_next = true; + continue; + case 'X': + case 'x': + case 'Y': + case 'y': + flags->y = true; + continue; + case 'F': + case 'M': + case 'm': + case 'n': + flags->m = true; + continue; + case 'd': + case 'j': + case 'D': + /* case 'l': */ + /* case 'S': */ + case 'z': + flags->d = true; + continue; + /* case 'a': */ + /* case 'A': */ + case 'g': + case 'h': + case 'G': + case 'H': + flags->h = true; + continue; + case 'i': + flags->i = true; + continue; + case 's': + flags->s = true; + continue; + case 'v': + case 'u': + flags->us = true; + continue; + case '!': + case '|': + case 'U': + flags->y = true; + flags->m = true; + flags->d = true; + flags->h = true; + flags->i = true; + flags->s = true; + flags->us = true; + continue; + default: + continue; + } + } +} + static inline void apply_interval(timelib_time **time, timelib_rel_time *interval) { timelib_time *new_time = timelib_sub(*time, interval); + timelib_update_ts(new_time, NULL); timelib_time_dtor(*time); *time = new_time; } @@ -107,37 +179,46 @@ 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; \ \ - zend_object *object; \ - php_date_obj *object_date; \ - timelib_rel_time interval; \ + zend_string *format, *_datetime; \ + zval *_timezone_object; \ + format_flags_t flags; \ \ CALL_ORIGINAL_FUNCTION(name); \ - \ - if (EG(exception) || Z_TYPE_P(return_value) == IS_FALSE) { \ - RETURN_FALSE; \ + if (!return_value || Z_TYPE_P(return_value) == IS_FALSE || !Z_PHPDATE_P(return_value)->time) { \ + return; \ } \ \ - 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); \ + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 2, 3); \ + Z_PARAM_STR(format) \ + Z_PARAM_STR(_datetime) \ + Z_PARAM_OPTIONAL \ + Z_PARAM_OBJECT_OF_CLASS_OR_NULL(_timezone_object, php_date_get_timezone_ce()) \ + ZEND_PARSE_PARAMETERS_END(); \ \ - 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; \ - } \ + parse_format(ZSTR_VAL(format), &flags); \ \ + timelib_time temp; \ + timelib_rel_time interval; \ get_shift_interval(&interval); \ + memcpy(&temp, Z_PHPDATE_P(return_value)->time, sizeof(timelib_time)); \ + \ apply_interval(&Z_PHPDATE_P(return_value)->time, &interval); \ + if (flags.h || flags.i || flags.s || flags.us) { \ + Z_PHPDATE_P(return_value)->time->h = 0; \ + Z_PHPDATE_P(return_value)->time->i = 0; \ + Z_PHPDATE_P(return_value)->time->s = 0; \ + Z_PHPDATE_P(return_value)->time->us = 0; \ + } \ + if (flags.y) { Z_PHPDATE_P(return_value)->time->y = temp.y; } \ + if (flags.m) { Z_PHPDATE_P(return_value)->time->m = temp.m; } \ + if (flags.d) { Z_PHPDATE_P(return_value)->time->d = temp.d; } \ + if (flags.h) { Z_PHPDATE_P(return_value)->time->h = temp.h; } \ + if (flags.i) { Z_PHPDATE_P(return_value)->time->i = temp.i; } \ + if (flags.s) { Z_PHPDATE_P(return_value)->time->s = temp.s; } \ + if (flags.us) { Z_PHPDATE_P(return_value)->time->us = temp.us; } \ + \ + timelib_update_ts(Z_PHPDATE_P(return_value)->time, NULL); \ } #define DEFINE_CREATE_FROM_FORMAT(name) \ diff --git a/ext/tests/classes/gh5.phpt b/ext/tests/gh5.phpt similarity index 100% rename from ext/tests/classes/gh5.phpt rename to ext/tests/gh5.phpt diff --git a/ext/tests/gh7.phpt b/ext/tests/gh7.phpt new file mode 100644 index 0000000..df60ae6 --- /dev/null +++ b/ext/tests/gh7.phpt @@ -0,0 +1,83 @@ +--TEST-- +Check GitHub PR - #7 (wrong createFromFormat) +--EXTENSIONS-- +colopl_timeshifter +--FILE-- +diff($second)->format('%h%i%s') !== '100' && $first->diff($second)->format('%h%i%s') !== '05959') { + die('fail: ' . $first->diff($second)->format('%h%i%s')); +} +echo $first->diff($second)->format('%y-%m-%d %h:%i:%s.%F'), \PHP_EOL; +\Colopl\ColoplTimeShifter\unregister_hook(); + +$first = date_create_from_format('i', '30'); +\Colopl\ColoplTimeShifter\register_hook(new DateInterval('P1MT30S')); +$second = date_create_from_format('i', '30'); +echo $first->diff($second)->format('%y-%m-%d %h:%i:%s.%F'), \PHP_EOL; +\Colopl\ColoplTimeShifter\unregister_hook(); + +$first = date_create_from_format('YmdHisu', '19941026112233444444'); +\Colopl\ColoplTimeShifter\register_hook(new DateInterval('PT30M')); +$second = date_create_from_format('YmdHisu', '19941026112233444444'); +echo $first->diff($second)->format('%y-%m-%d %h:%i:%s.%F'), \PHP_EOL; +\Colopl\ColoplTimeShifter\unregister_hook(); + +$first = date_create_from_format('!Hisu', '112233444444'); +\Colopl\ColoplTimeShifter\register_hook(new DateInterval('PT30M')); +$second = date_create_from_format('!Hisu', '112233444444'); +echo $first->diff($second)->format('%y-%m-%d %h:%i:%s.%F'), \PHP_EOL; +\Colopl\ColoplTimeShifter\unregister_hook(); + +$first = date_create_from_format('dm|', '2610'); +\Colopl\ColoplTimeShifter\register_hook(new DateInterval('P1D')); +$second = date_create_from_format('dm|', '2610'); +echo $first->diff($second)->format('%y-%m-%d %h:%i:%s.%F'), \PHP_EOL; +\Colopl\ColoplTimeShifter\unregister_hook(); + +$first = date_create_from_format('P', 'Asia/Tokyo'); +\Colopl\ColoplTimeShifter\register_hook(new DateInterval('PT30M')); +$second = date_create_from_format('P', 'Asia/Tokyo'); +echo $first->diff($second)->format('%y-%m-%d %h:%i:%s.%F'), \PHP_EOL; +\Colopl\ColoplTimeShifter\unregister_hook(); + +$first = date_create_from_format('Ym\\d', '199410d'); +\Colopl\ColoplTimeShifter\register_hook(new DateInterval('P1M')); +$second = date_create_from_format('Ym\\d', '199410d'); +echo $first->diff($second)->format('%y-%m-%d %h:%i:%s.%F'), \PHP_EOL; +\Colopl\ColoplTimeShifter\unregister_hook(); + +$first = date_create_from_format('u\\Y\\m\\d', '123456Ymd'); +\Colopl\ColoplTimeShifter\register_hook(new DateInterval('P1M')); +$second = date_create_from_format('u\\Y\\m\\d', '123456Ymd'); +echo $first->diff($second)->format('%y-%m-%d %h:%i:%s.%F'), \PHP_EOL; +\Colopl\ColoplTimeShifter\unregister_hook(); + +$first = date_create_from_format('YmdP', '19941026Asia/Tokyo'); +\Colopl\ColoplTimeShifter\register_hook(new DateInterval('P1M')); +$second = date_create_from_format('YmdP', '19941026Asia/Tokyo'); +echo $first->diff($second)->format('%y-%m-%d %h:%i:%s.%F'), \PHP_EOL; +\Colopl\ColoplTimeShifter\unregister_hook(); + +$first = date_create_from_format('uYmdP', '12345619941026Asia/Tokyo'); +\Colopl\ColoplTimeShifter\register_hook(new DateInterval('P1M')); +$second = date_create_from_format('uYmdP', '12345619941026Asia/Tokyo'); +echo $first->diff($second)->format('%y-%m-%d %h:%i:%s.%F'), \PHP_EOL; +\Colopl\ColoplTimeShifter\unregister_hook(); + +?> +--EXPECTF-- +0-0-0 %d:%d:%d.000000 +0-1-0 0:0:0.000000 +0-0-0 0:0:0.000000 +0-0-0 0:0:0.000000 +0-0-0 0:0:0.000000 +0-0-0 0:%d:%d.%d +0-0-0 0:0:0.000000 +0-1-0 0:0:0.000000 +0-0-0 0:0:0.000000 +0-0-0 0:0:0.000000