LCOV - code coverage report
Current view: top level - ext - hook.c (source / functions) Coverage Total Hit
Test: Extension code coverage Lines: 91.5 % 378 346
Test Date: 2026-05-15 07:07:45 Functions: 100.0 % 37 37

            Line data    Source code
       1              : /*
       2              :   +----------------------------------------------------------------------+
       3              :   | COLOPL PHP TimeShifter.                                              |
       4              :   +----------------------------------------------------------------------+
       5              :   | Copyright (c) COLOPL, Inc.                                           |
       6              :   +----------------------------------------------------------------------+
       7              :   | This source file is subject to the BSD-3-Clause license that is      |
       8              :   | bundled with this package in the file LICENSE.                       |
       9              :   +----------------------------------------------------------------------+
      10              :   | Author: Go Kudo <g-kudo@colopl.co.jp>                                |
      11              :   +----------------------------------------------------------------------+
      12              : */
      13              : #include "hook.h"
      14              : 
      15              : #include "php.h"
      16              : #include "php_colopl_timeshifter.h"
      17              : #include "ext/date/php_date.h"
      18              : #include "ext/pdo/php_pdo.h"
      19              : #include "ext/pdo/php_pdo_driver.h"
      20              : 
      21              : #include "third_party/timelib/timelib.h"
      22              : 
      23              : #ifdef PHP_WIN32
      24              : # include "win32/time.h"
      25              : #else
      26              : # include <sys/time.h>
      27              : #endif
      28              : 
      29              : typedef struct _format_flags_t {
      30              :         bool y, m, d, h, i, s, us;
      31              : } format_flags_t;
      32              : 
      33           84 : static inline void parse_format(char *format, format_flags_t *flags) {
      34           84 :         memset(flags, 0, sizeof(format_flags_t));
      35           84 :         bool skip_next = false;
      36              : 
      37          512 :         for (char *c = format; *c != '\0'; c++) {
      38          428 :                 if (skip_next) {
      39           16 :                         skip_next = false;
      40           16 :                         continue;
      41              :                 }
      42          412 :                 switch (*c) {
      43           16 :                         case '\\':
      44           16 :                                 skip_next = true;
      45           16 :                                 continue;
      46           44 :                         case 'X':
      47              :                         case 'x':
      48              :                         case 'Y':
      49              :                         case 'y':
      50           44 :                                 flags->y = true;
      51           44 :                                 continue;
      52           64 :                         case 'F':
      53              :                         case 'M':
      54              :                         case 'm':
      55              :                         case 'n':
      56           64 :                                 flags->m = true;
      57           64 :                                 continue;
      58           48 :                         case 'd':
      59              :                         case 'j':
      60              :                         case 'D':
      61              :                         /* case 'l': */
      62              :                         /* case 'S': */
      63              :                         case 'z':
      64           48 :                                 flags->d = true;
      65           48 :                                 continue;
      66              :                         /* case 'a': */
      67              :                         /* case 'A': */
      68           32 :                         case 'g':
      69              :                         case 'h':
      70              :                         case 'G':
      71              :                         case 'H':
      72           32 :                                 flags->h = true;
      73           32 :                                 continue;
      74           36 :                         case 'i':
      75           36 :                                 flags->i = true;
      76           36 :                                 continue;
      77           24 :                         case 's':
      78           24 :                                 flags->s = true;
      79           24 :                                 continue;
      80           32 :                         case 'v':
      81              :                         case 'u':
      82           32 :                                 flags->us = true;
      83           32 :                                 continue;
      84            8 :                         case '!':
      85              :                         case '|':
      86              :                         case 'U':
      87            8 :                                 flags->y = true;
      88            8 :                                 flags->m = true;
      89            8 :                                 flags->d = true;
      90            8 :                                 flags->h = true;
      91            8 :                                 flags->i = true;
      92            8 :                                 flags->s = true;
      93            8 :                                 flags->us = true;
      94            8 :                                 continue;
      95          108 :                         default:
      96          108 :                                 continue;
      97              :                 }
      98              :         }
      99           84 : }
     100              : 
     101          448 : static inline void apply_interval(timelib_time **time, timelib_rel_time *interval)
     102              : {
     103          448 :         timelib_time *new_time = timelib_sub(*time, interval);
     104          448 :         timelib_update_ts(new_time, NULL);
     105          448 :         timelib_time_dtor(*time);
     106          448 :         *time = new_time;
     107          448 : }
     108              : 
     109              : #define CALL_ORIGINAL_FUNCTION_WITH_PARAMS(_name, _params, _param_count) \
     110              :         do { \
     111              :                 zend_fcall_info fci = { \
     112              :                         .size = sizeof(zend_fcall_info), \
     113              :                         .retval = return_value, \
     114              :                         .param_count = _param_count, \
     115              :                         .params = _params, \
     116              :                 }; \
     117              :                 zend_function fnc = { \
     118              :                         .type = ZEND_INTERNAL_FUNCTION, \
     119              :                 }; \
     120              :                 zend_fcall_info_cache fcc = { \
     121              :                         .function_handler = &fnc, \
     122              :                 }; \
     123              :                 zend_function *src = (zend_function *) zend_hash_str_find_ptr(CG(function_table), #_name, strlen(#_name)); \
     124              :                 ZEND_ASSERT(src); \
     125              :                 memcpy(&fnc.internal_function, &src->internal_function, sizeof(zend_internal_function)); \
     126              :                 fnc.internal_function.handler = COLOPL_TS_G(orig_##_name); \
     127              :                 zend_call_function(&fci, &fcc); \
     128              :         } while (0);
     129              : 
     130              : #define CALL_ORIGINAL_FUNCTION(name) \
     131              :         do { \
     132              :                 COLOPL_TS_G(orig_##name)(INTERNAL_FUNCTION_PARAM_PASSTHRU); \
     133              :         } while (0);
     134              : 
     135              : #define CHECK_STATE(name) \
     136              :         do { \
     137              :                 if (!get_is_hooked()) { \
     138              :                         CALL_ORIGINAL_FUNCTION(name); \
     139              :                         return; \
     140              :                 } \
     141              :         } while (0);
     142              : 
     143              : #define DEFINE_DT_HOOK_CONSTRUCTOR(name) \
     144              :         static void hook_##name##_con(INTERNAL_FUNCTION_PARAMETERS) \
     145              :         { \
     146              :                 CHECK_STATE(name##_con); \
     147              :                 \
     148              :                 CALL_ORIGINAL_FUNCTION(name##_con); \
     149              :                 \
     150              :                 zend_string *datetime = NULL; \
     151              :                 zval *timezone = NULL; \
     152              :                 php_date_obj *date = NULL; \
     153              :                 timelib_rel_time interval; \
     154              :                 \
     155              :                 ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 0, 2) \
     156              :                         Z_PARAM_OPTIONAL; \
     157              :                         Z_PARAM_STR_OR_NULL(datetime); \
     158              :                         Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone, php_date_get_timezone_ce()); \
     159              :                 ZEND_PARSE_PARAMETERS_END(); \
     160              :                 \
     161              :                 date = Z_PHPDATE_P(ZEND_THIS); \
     162              :                 \
     163              :                 /* Early return if construction failed. */ \
     164              :                 if (!date || !date->time) { \
     165              :                         return; \
     166              :                 } \
     167              :                 \
     168              :                 if (datetime && is_fixed_time_str(datetime, timezone) == 1) { \
     169              :                         return; \
     170              :                 } \
     171              :                 \
     172              :                 get_shift_interval(&interval); \
     173              :                 apply_interval(&date->time, &interval); \
     174              :         }
     175              : 
     176              : #define DEFINE_CREATE_FROM_FORMAT_EX(fname, name) \
     177              :         static void hook_##fname(INTERNAL_FUNCTION_PARAMETERS) { \
     178              :                 CHECK_STATE(name); \
     179              :                 \
     180              :                 zend_string *format, *_datetime; \
     181              :                 zval *_timezone_object; \
     182              :                 format_flags_t flags; \
     183              :                 timelib_time *orig, *shifted; \
     184              :                 \
     185              :                 CALL_ORIGINAL_FUNCTION(name); \
     186              :                 if (!return_value || Z_TYPE_P(return_value) == IS_FALSE || !Z_PHPDATE_P(return_value)->time) { \
     187              :                         return; \
     188              :                 } \
     189              :                 \
     190              :                 ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 2, 3); \
     191              :                         Z_PARAM_STR(format) \
     192              :                         Z_PARAM_STR(_datetime) \
     193              :                         Z_PARAM_OPTIONAL \
     194              :                         Z_PARAM_OBJECT_OF_CLASS_OR_NULL(_timezone_object, php_date_get_timezone_ce()) \
     195              :                 ZEND_PARSE_PARAMETERS_END(); \
     196              :                 \
     197              :                 parse_format(ZSTR_VAL(format), &flags); \
     198              :                 \
     199              :                 /* backup original method result */ \
     200              :                 orig = timelib_time_clone(Z_PHPDATE_P(return_value)->time); \
     201              :                 shifted = get_shifted_timelib_time(orig->tz_info); \
     202              :                 \
     203              :                 /* overwrite current shifted datetime */ \
     204              :                 Z_PHPDATE_P(return_value)->time->y = shifted->y; \
     205              :                 Z_PHPDATE_P(return_value)->time->m = shifted->m; \
     206              :                 Z_PHPDATE_P(return_value)->time->d = shifted->d; \
     207              :                 Z_PHPDATE_P(return_value)->time->h = shifted->h; \
     208              :                 Z_PHPDATE_P(return_value)->time->i = shifted->i; \
     209              :                 Z_PHPDATE_P(return_value)->time->s = shifted->s; \
     210              :                 Z_PHPDATE_P(return_value)->time->us = shifted->us; \
     211              :                 \
     212              :                 /* restore original method result if required */ \
     213              :                 if (flags.h || flags.i || flags.s || flags.us) { \
     214              :                         Z_PHPDATE_P(return_value)->time->h = 0; \
     215              :                         Z_PHPDATE_P(return_value)->time->i = 0; \
     216              :                         Z_PHPDATE_P(return_value)->time->s = 0; \
     217              :                         Z_PHPDATE_P(return_value)->time->us = 0; \
     218              :                 } \
     219              :                 if (flags.y) { Z_PHPDATE_P(return_value)->time->y = orig->y; } \
     220              :                 if (flags.m) { Z_PHPDATE_P(return_value)->time->m = orig->m; } \
     221              :                 if (flags.d) { Z_PHPDATE_P(return_value)->time->d = orig->d; } \
     222              :                 if (flags.h) { Z_PHPDATE_P(return_value)->time->h = orig->h; } \
     223              :                 if (flags.i) { Z_PHPDATE_P(return_value)->time->i = orig->i; } \
     224              :                 if (flags.s) { Z_PHPDATE_P(return_value)->time->s = orig->s; } \
     225              :                 if (flags.us) { Z_PHPDATE_P(return_value)->time->us = orig->us; } \
     226              :                 \
     227              :                 /* release shifted time */ \
     228              :                 timelib_time_dtor(orig); \
     229              :                 timelib_time_dtor(shifted); \
     230              :                 timelib_update_ts(Z_PHPDATE_P(return_value)->time, NULL); \
     231              :         }
     232              : 
     233              : #define DEFINE_CREATE_FROM_FORMAT(name) \
     234              :         DEFINE_CREATE_FROM_FORMAT_EX(name, name);
     235              : 
     236              : #define HOOK_CONSTRUCTOR(ce, name) \
     237              :         do { \
     238              :                 COLOPL_TS_G(orig_##name##_con) = ce->constructor->internal_function.handler; \
     239              :                 ce->constructor->internal_function.handler = hook_##name##_con; \
     240              :         } while (0);
     241              : 
     242              : #define HOOK_METHOD(ce, name, method) \
     243              :         do { \
     244              :                 zend_function *php_function_entry = zend_hash_str_find_ptr(&ce->function_table, #method, strlen(#method)); \
     245              :                 ZEND_ASSERT(php_function_entry); \
     246              :                 COLOPL_TS_G(orig_##name##_##method) = php_function_entry->internal_function.handler; \
     247              :                 php_function_entry->internal_function.handler = hook_##name##_##method; \
     248              :         } while (0);
     249              : 
     250              : #define HOOK_FUNCTION(name) \
     251              :         do { \
     252              :                 zend_function *php_function_entry = zend_hash_str_find_ptr(CG(function_table), #name, strlen(#name)); \
     253              :                 ZEND_ASSERT(php_function_entry); \
     254              :                 COLOPL_TS_G(orig_##name) = php_function_entry->internal_function.handler; \
     255              :                 php_function_entry->internal_function.handler = hook_##name; \
     256              :         } while (0);
     257              : 
     258              : #define RESTORE_CONSTRUCTOR(ce, name) \
     259              :         do { \
     260              :                 ZEND_ASSERT(COLOPL_TS_G(orig_##name##_con)); \
     261              :                 ce->constructor->internal_function.handler = COLOPL_TS_G(orig_##name##_con); \
     262              :                 COLOPL_TS_G(orig_##name##_con) = NULL; \
     263              :         } while (0);
     264              : 
     265              : #define RESTORE_METHOD(ce, name, method) \
     266              :         do { \
     267              :                 zend_function *php_function_entry = zend_hash_str_find_ptr(&ce->function_table, #method, strlen(#method)); \
     268              :                 ZEND_ASSERT(php_function_entry); \
     269              :                 ZEND_ASSERT(COLOPL_TS_G(orig_##name##_##method)); \
     270              :                 php_function_entry->internal_function.handler = COLOPL_TS_G(orig_##name##_##method); \
     271              :                 COLOPL_TS_G(orig_##name##_##method) = NULL; \
     272              :         } while (0);
     273              : 
     274              : #define RESTORE_FUNCTION(name) \
     275              :         do { \
     276              :                 zend_function *php_function_entry = zend_hash_str_find_ptr(CG(function_table), #name, strlen(#name)); \
     277              :                 ZEND_ASSERT(php_function_entry); \
     278              :                 ZEND_ASSERT(COLOPL_TS_G(orig_##name)); \
     279              :                 php_function_entry->internal_function.handler = COLOPL_TS_G(orig_##name); \
     280              :                 COLOPL_TS_G(orig_##name) = NULL; \
     281              :         } while (0);
     282              : 
     283          288 : static inline int is_fixed_time_str(zend_string *datetime, zval *timezone)
     284              : {
     285              :         zend_string *datetime_lower;
     286              :         zval before_zv, after_zv;
     287              :         php_date_obj *before, *after;
     288          288 :         zend_class_entry *ce = php_date_get_immutable_ce();
     289              :         bool is_fixed_time_str;
     290              : 
     291          288 :         datetime_lower = zend_string_tolower(datetime);
     292          288 :         if (strncmp(ZSTR_VAL(datetime_lower), "now", 3) == 0 ||
     293          260 :                 strncmp(ZSTR_VAL(datetime_lower), "yesterday", 9) == 0 ||
     294          252 :                 strncmp(ZSTR_VAL(datetime_lower), "today", 5) == 0 ||
     295          244 :                 strncmp(ZSTR_VAL(datetime_lower), "tomorrow", 8) == 0
     296              :         ) {
     297           52 :                 zend_string_release(datetime_lower);
     298           52 :                 return 2;
     299              :         }
     300              : 
     301          236 :         zend_string_release(datetime_lower);
     302              : 
     303          236 :         php_date_instantiate(ce, &before_zv);
     304          236 :         before = Z_PHPDATE_P(&before_zv);
     305          236 :         if (!php_date_initialize(before, ZSTR_VAL(datetime), ZSTR_LEN(datetime), NULL, timezone, 0)) {
     306            4 :                 zval_ptr_dtor(&before_zv);
     307            4 :                 return FAILURE;
     308              :         }
     309              : 
     310          232 :         usleep(((uint32_t) COLOPL_TS_G(usleep_sec)) > 0 ? (uint32_t) COLOPL_TS_G(usleep_sec) : 1);
     311              : 
     312          232 :         php_date_instantiate(ce, &after_zv);
     313          232 :         after = Z_PHPDATE_P(&after_zv);
     314          232 :         if (!php_date_initialize(after, ZSTR_VAL(datetime), ZSTR_LEN(datetime), NULL, timezone, 0)) {
     315            0 :                 zval_ptr_dtor(&before_zv);
     316            0 :                 zval_ptr_dtor(&after_zv);
     317            0 :                 return FAILURE;
     318              :         }
     319              : 
     320          464 :         is_fixed_time_str = before->time->y == after->time->y
     321          232 :                 && before->time->m == after->time->m
     322          232 :                 && before->time->d == after->time->d
     323          232 :                 && before->time->h == after->time->h
     324          232 :                 && before->time->i == after->time->i
     325          232 :                 && before->time->s == after->time->s
     326          464 :                 && before->time->us == after->time->us
     327              :         ;
     328              : 
     329          232 :         zval_ptr_dtor(&before_zv);
     330          232 :         zval_ptr_dtor(&after_zv);
     331              : 
     332          232 :         return (int) is_fixed_time_str;
     333              : }
     334              : 
     335          208 : static inline timelib_time *get_current_timelib_time(timelib_tzinfo *tzi)
     336              : {
     337          208 :         timelib_time *t = timelib_time_ctor();
     338              : 
     339          208 :         if (tzi != NULL) {
     340           84 :                 timelib_set_timezone(t, tzi);
     341           84 :                 timelib_unixtime2local(t, (timelib_sll) php_time());
     342              :         } else {
     343          124 :                 timelib_unixtime2gmt(t, php_time());
     344              :         }
     345              : 
     346          208 :         return t;
     347              : }
     348              : 
     349          208 : static inline timelib_time *get_shifted_timelib_time(timelib_tzinfo *tzi)
     350              : {
     351          208 :         timelib_time *t = get_current_timelib_time(tzi);
     352              :         timelib_rel_time interval;
     353              : 
     354          208 :         get_shift_interval(&interval);
     355          208 :         apply_interval(&t, &interval);
     356              : 
     357          208 :         return t;
     358              : }
     359              : 
     360          124 : static inline time_t get_shifted_time(timelib_tzinfo *tzi)
     361              : {
     362              :         time_t timestamp;
     363          124 :         timelib_time *t = get_shifted_timelib_time(tzi);
     364              : 
     365          124 :         timestamp = t->sse;
     366              : 
     367          124 :         timelib_time_dtor(t);
     368              : 
     369          124 :         return timestamp;
     370              : }
     371              : 
     372           64 : static inline bool pdo_time_apply(pdo_dbh_t *dbh)
     373              : {
     374              :         zend_string *sql;
     375              :         char buf[1024];
     376              : 
     377           64 :         if (!COLOPL_TS_G(pdo_mysql_orig_methods) || !COLOPL_TS_G(pdo_mysql_orig_methods)->doer) {
     378            0 :                 return false;
     379              :         }
     380              : 
     381           64 :         zend_sprintf(buf, "SET @@session.timestamp = %ld;", get_shifted_time(NULL));
     382           64 :         sql = zend_string_init_fast(buf, strlen(buf));
     383           64 :         COLOPL_TS_G(pdo_mysql_orig_methods)->doer(dbh, sql);
     384           64 :         zend_string_release(sql);
     385              : 
     386           64 :         return true;
     387              : }
     388              : 
     389           24 : static bool hook_pdo_driver_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options)
     390              : {
     391              :         bool retval;
     392              : 
     393           24 :         if (get_is_hooked()) {
     394           24 :                 pdo_time_apply(dbh);
     395              :         }
     396              : 
     397           24 :         retval = COLOPL_TS_G(pdo_mysql_orig_methods)->preparer(dbh, sql, stmt, driver_options);
     398              : 
     399           24 :         return retval;
     400              : }
     401              : 
     402           40 : static zend_long hook_pdo_driver_doer(pdo_dbh_t *dbh, const zend_string *sql)
     403              : {
     404           40 :         if (get_is_hooked()) {
     405           40 :                 pdo_time_apply(dbh);
     406              :         }
     407              : 
     408           40 :         return COLOPL_TS_G(pdo_mysql_orig_methods)->doer(dbh, sql);
     409              : }
     410              : 
     411           16 : static void hook_pdo_con(INTERNAL_FUNCTION_PARAMETERS)
     412              : {
     413           16 :         CHECK_STATE(pdo_con);
     414              : 
     415           16 :         pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
     416              : 
     417           16 :         CALL_ORIGINAL_FUNCTION(pdo_con);
     418              : 
     419           16 :         if (!dbh->driver ||
     420           16 :                 strncmp(dbh->driver->driver_name, "mysql", 5) == 0 ||
     421            0 :                 dbh->methods != &COLOPL_TS_G(hooked_mysql_driver_methods)
     422              :         ) {
     423           16 :                 if (!COLOPL_TS_G(pdo_mysql_orig_methods)) {
     424              :                         /* Check pdo_mysql driver. */
     425           16 :                         if (!dbh->methods) {
     426            0 :                                 return;
     427              :                         }
     428              : 
     429              :                         /* Copy original methods struct. */
     430           16 :                         COLOPL_TS_G(pdo_mysql_orig_methods) = dbh->methods;
     431           16 :                         memcpy(&COLOPL_TS_G(hooked_mysql_driver_methods), dbh->methods, sizeof(struct pdo_dbh_methods));
     432              : 
     433              :                         /* Override function pointer. */
     434           16 :                         COLOPL_TS_G(hooked_mysql_driver_methods).preparer = hook_pdo_driver_preparer;
     435           16 :                         COLOPL_TS_G(hooked_mysql_driver_methods).doer = hook_pdo_driver_doer;
     436              :                 }
     437              : 
     438              :                 /* Override MySQL specific driver methods pointer. */
     439           16 :                 dbh->methods = &COLOPL_TS_G(hooked_mysql_driver_methods);
     440              :         }
     441              : }
     442              : 
     443            8 : static inline void mktime_common(INTERNAL_FUNCTION_PARAMETERS, zend_long timestamp)
     444              : {
     445              :         zend_long hou, min, sec, mon, day, yea;
     446            8 :         bool min_is_null = true, sec_is_null = true, mon_is_null = true, day_is_null = true, yea_is_null = true;
     447            8 :         timelib_time *t = timelib_time_ctor();
     448              :         timelib_rel_time interval;
     449              : 
     450            8 :         timelib_unixtime2gmt(t, timestamp);
     451            8 :         get_shift_interval(&interval);
     452            8 :         apply_interval(&t, &interval);
     453              : 
     454            8 :         ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 1, 6)
     455            8 :                 Z_PARAM_LONG(hou)
     456            8 :                 Z_PARAM_OPTIONAL
     457            8 :                 Z_PARAM_LONG_OR_NULL(min, min_is_null)
     458            0 :                 Z_PARAM_LONG_OR_NULL(sec, sec_is_null)
     459            0 :                 Z_PARAM_LONG_OR_NULL(mon, mon_is_null)
     460            0 :                 Z_PARAM_LONG_OR_NULL(day, day_is_null)
     461            0 :                 Z_PARAM_LONG_OR_NULL(yea, yea_is_null)
     462            8 :         ZEND_PARSE_PARAMETERS_END();
     463              : 
     464            8 :         if (!min_is_null) {
     465            0 :                 t->i = min;
     466              :         }
     467              : 
     468            8 :         if (!sec_is_null) {
     469            0 :                 t->s = sec;
     470              :         }
     471              : 
     472            8 :         if (!mon_is_null) {
     473            0 :                 t->m = mon;
     474              :         }
     475              : 
     476            8 :         if (!day_is_null) {
     477            0 :                 t->d = day;
     478              :         }
     479              : 
     480            8 :         if (!yea_is_null) {
     481            0 :                 if (yea >= 0 && yea < 70) {
     482            0 :                         yea += 2000;
     483            0 :                 } else if (yea >= 70 && yea <= 100) {
     484            0 :                         yea += 1900;
     485              :                 }
     486            0 :                 t->y = yea;
     487              :         }
     488              : 
     489            8 :         RETVAL_LONG(t->sse);
     490            8 :         timelib_time_dtor(t);
     491              : }
     492              : 
     493           12 : static inline void date_common(INTERNAL_FUNCTION_PARAMETERS, int localtime)
     494              : {
     495              :         zend_string *format;
     496              :         zend_long ts;
     497           12 :         bool ts_is_null = true;
     498              : 
     499           12 :         ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 1, 2)
     500           12 :                 Z_PARAM_STR(format)
     501           12 :                 Z_PARAM_OPTIONAL;
     502           12 :                 Z_PARAM_LONG_OR_NULL(ts, ts_is_null)
     503           12 :         ZEND_PARSE_PARAMETERS_END();
     504              : 
     505           12 :         if (ts_is_null) {
     506            8 :                 ts = get_shifted_time(NULL);
     507              :         }
     508              : 
     509           12 :         RETVAL_STR(php_format_date(ZSTR_VAL(format), ZSTR_LEN(format), ts, localtime));
     510              : }
     511              : 
     512           16 : static inline void date_create_common(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *ce)
     513              : {
     514           16 :         zval *timezone_object = NULL;
     515           16 :         zend_string *time_str = NULL;
     516           16 :         php_date_obj *date = NULL;
     517              :         timelib_rel_time interval;
     518              : 
     519           16 :         ZEND_PARSE_PARAMETERS_START(0, 2)
     520           16 :                 Z_PARAM_OPTIONAL;
     521           16 :                 Z_PARAM_STR(time_str)
     522            8 :                 Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, php_date_get_timezone_ce())
     523           24 :         ZEND_PARSE_PARAMETERS_END();
     524              : 
     525           16 :         php_date_instantiate(ce, return_value);
     526           32 :         if (!php_date_initialize(
     527              :                 Z_PHPDATE_P(return_value),
     528           16 :                 (!time_str ? NULL : ZSTR_VAL(time_str)),
     529           16 :                 (!time_str ? 0 : ZSTR_LEN(time_str)),
     530              :                 NULL,
     531              :                 timezone_object,
     532              :                 0
     533              :         )) {
     534            0 :                 zval_ptr_dtor(return_value);
     535            0 :                 RETVAL_FALSE;
     536              :         }
     537              : 
     538           16 :         if (time_str && is_fixed_time_str(time_str, timezone_object) == 1) {
     539            8 :                 return;
     540              :         }
     541              : 
     542            8 :         get_shift_interval(&interval);
     543            8 :         apply_interval(&Z_PHPDATE_P(return_value)->time, &interval);
     544              : }
     545              : 
     546          220 : DEFINE_DT_HOOK_CONSTRUCTOR(dt);
     547              : 
     548           80 : DEFINE_DT_HOOK_CONSTRUCTOR(dti);
     549              : 
     550           16 : static void hook_time(INTERNAL_FUNCTION_PARAMETERS)
     551              : {
     552           16 :         CHECK_STATE(time);
     553              : 
     554            8 :         CALL_ORIGINAL_FUNCTION(time);
     555            8 :         RETURN_LONG(get_shifted_time(NULL));
     556              : }
     557              : 
     558            8 : static void hook_mktime(INTERNAL_FUNCTION_PARAMETERS)
     559              : {
     560            8 :         CHECK_STATE(mktime);
     561              : 
     562            4 :         CALL_ORIGINAL_FUNCTION(mktime);
     563            4 :         mktime_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, Z_LVAL_P(return_value));
     564              : }
     565              : 
     566            8 : static void hook_gmmktime(INTERNAL_FUNCTION_PARAMETERS)
     567              : {
     568            8 :         CHECK_STATE(gmmktime);
     569              : 
     570            4 :         CALL_ORIGINAL_FUNCTION(gmmktime);
     571            4 :         mktime_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, Z_LVAL_P(return_value));
     572              : }
     573              : 
     574           16 : static void hook_date_create(INTERNAL_FUNCTION_PARAMETERS)
     575              : {
     576           16 :         CHECK_STATE(date_create);
     577              : 
     578            8 :         date_create_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_date_get_date_ce());
     579              : }
     580              : 
     581           16 : static void hook_date_create_immutable(INTERNAL_FUNCTION_PARAMETERS)
     582              : {
     583           16 :         CHECK_STATE(date_create_immutable);
     584              : 
     585            8 :         date_create_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_date_get_immutable_ce());
     586              : }
     587              : 
     588          100 : DEFINE_CREATE_FROM_FORMAT(date_create_from_format);
     589              : 
     590           16 : DEFINE_CREATE_FROM_FORMAT(date_create_immutable_from_format);
     591              : 
     592           32 : DEFINE_CREATE_FROM_FORMAT_EX(dt_createfromformat, date_create_from_format);
     593              : 
     594           16 : DEFINE_CREATE_FROM_FORMAT_EX(dti_createfromformat, date_create_immutable_from_format);
     595              : 
     596           16 : static void hook_date(INTERNAL_FUNCTION_PARAMETERS)
     597              : {
     598           16 :         CHECK_STATE(date);
     599              : 
     600            8 :         date_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
     601              : }
     602              : 
     603            8 : static void hook_gmdate(INTERNAL_FUNCTION_PARAMETERS)
     604              : {
     605            8 :         CHECK_STATE(gmdate);
     606              : 
     607            4 :         date_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
     608              : }
     609              : 
     610            8 : static void hook_idate(INTERNAL_FUNCTION_PARAMETERS)
     611              : {
     612            8 :         CHECK_STATE(idate);
     613              : 
     614              :         zend_string *format;
     615              :         zend_long ts;
     616            4 :         bool ts_is_null = 1;
     617              : 
     618            4 :         if (Z_TYPE_P(return_value) == IS_FALSE) {
     619            0 :                 return;
     620              :         }
     621              : 
     622            4 :         ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET,1, 2)
     623            4 :                 Z_PARAM_STR(format)
     624            4 :                 Z_PARAM_OPTIONAL
     625            4 :                 Z_PARAM_LONG_OR_NULL(ts, ts_is_null)
     626            4 :         ZEND_PARSE_PARAMETERS_END();
     627              : 
     628            4 :         if (ts_is_null) {
     629            4 :                 ts = get_shifted_time(NULL);
     630              :         }
     631              : 
     632            4 :         RETURN_LONG(php_idate(ZSTR_VAL(format)[0], ts, 0));
     633              : }
     634              : 
     635            8 : static void hook_getdate(INTERNAL_FUNCTION_PARAMETERS)
     636              : {
     637            8 :         CHECK_STATE(getdate);
     638              : 
     639              :         zend_long timestamp;
     640            4 :         bool timestamp_is_null = true;
     641              : 
     642            4 :         ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 0, 1)
     643            4 :                 Z_PARAM_OPTIONAL
     644            4 :                 Z_PARAM_LONG_OR_NULL(timestamp, timestamp_is_null)
     645            4 :         ZEND_PARSE_PARAMETERS_END();
     646              : 
     647            4 :         if (!timestamp_is_null) {
     648            0 :                 return;
     649              :         }
     650              : 
     651              :         /* Call original function with timestamp params. */
     652              :         zval params[1];
     653            4 :         ZVAL_LONG(&params[0], get_shifted_time(NULL));
     654            4 :         CALL_ORIGINAL_FUNCTION_WITH_PARAMS(getdate, params, 1);
     655              : }
     656              : 
     657           16 : static void hook_localtime(INTERNAL_FUNCTION_PARAMETERS)
     658              : {
     659           16 :         CHECK_STATE(localtime);
     660              : 
     661              :         zend_long timestamp;
     662            8 :         bool timestamp_is_null = true, associative = false;
     663              : 
     664            8 :         ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 0, 2)
     665            8 :                 Z_PARAM_OPTIONAL;
     666            8 :                 Z_PARAM_LONG_OR_NULL(timestamp, timestamp_is_null);
     667            4 :                 Z_PARAM_BOOL(associative);
     668            8 :         ZEND_PARSE_PARAMETERS_END();
     669              : 
     670              :         /* Call original function with params. */
     671              :         zval params[2];
     672            8 :         ZVAL_LONG(&params[0], get_shifted_time(NULL));
     673            8 :         ZVAL_BOOL(&params[1], associative);
     674            8 :         CALL_ORIGINAL_FUNCTION_WITH_PARAMS(localtime, params, 2);
     675              : }
     676              : 
     677           60 : static void hook_strtotime(INTERNAL_FUNCTION_PARAMETERS)
     678              : {
     679           72 :         CHECK_STATE(strtotime);
     680              : 
     681              :         zend_string *times, *times_lower;
     682              :         zend_long preset_ts;
     683           40 :         bool preset_ts_is_null = true;
     684              :         int is_fixed_ret;
     685              : 
     686           40 :         ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 1, 2)
     687           40 :                 Z_PARAM_STR(times);
     688           40 :                 Z_PARAM_OPTIONAL;
     689           40 :                 Z_PARAM_LONG_OR_NULL(preset_ts, preset_ts_is_null);
     690           40 :         ZEND_PARSE_PARAMETERS_END();
     691              : 
     692           40 :         is_fixed_ret = is_fixed_time_str(times, NULL);
     693              : 
     694           40 :         if (!preset_ts_is_null || is_fixed_ret == 1 || is_fixed_ret == FAILURE) {
     695           12 :                 CALL_ORIGINAL_FUNCTION(strtotime);
     696           12 :                 return;
     697              :         }
     698              : 
     699              :         /* Call original function based on shifted time */
     700              :         zval params[2];
     701           28 :         ZVAL_STR(&params[0], times);
     702           28 :         ZVAL_LONG(&params[1], get_shifted_time(NULL));
     703           28 :         CALL_ORIGINAL_FUNCTION_WITH_PARAMS(strtotime, params, 2);
     704              : }
     705              : 
     706              : #if HAVE_GETTIMEOFDAY
     707           16 : static inline void gettimeofday_common(INTERNAL_FUNCTION_PARAMETERS, int mode)
     708              : {
     709           16 :         bool get_as_float = false;
     710           16 :         struct timeval tp = {0};
     711           16 :         timelib_time *tm = timelib_time_ctor();
     712              :         timelib_rel_time interval;
     713              : 
     714           16 :         ZEND_PARSE_PARAMETERS_START(0, 1)
     715           16 :                 Z_PARAM_OPTIONAL;
     716           16 :                 Z_PARAM_BOOL(get_as_float);
     717           16 :         ZEND_PARSE_PARAMETERS_END();
     718              : 
     719           16 :         if (gettimeofday(&tp, NULL)) {
     720            0 :                 ZEND_ASSERT(0 && "gettimeofday() can't fail");
     721              :         }
     722              : 
     723           16 :         timelib_unixtime2gmt(tm, tp.tv_sec);
     724           16 :         tm->us = tp.tv_usec;
     725           16 :         get_shift_interval(&interval);
     726           16 :         apply_interval(&tm, &interval);
     727              : 
     728           16 :         if (get_as_float) {
     729            8 :                 RETVAL_DOUBLE((double)(tm->sse + tm->us / 1000000.00));
     730              :         } else {
     731            8 :                 if (mode) {
     732              :                         timelib_time_offset *offset;
     733              : 
     734            4 :                         offset = timelib_get_time_zone_info(tm->sse, get_timezone_info());
     735              : 
     736            4 :                         array_init(return_value);
     737            4 :                         add_assoc_long(return_value, "sec", tm->sse);
     738            4 :                         add_assoc_long(return_value, "usec", tm->us);
     739              : 
     740            4 :                         add_assoc_long(return_value, "minuteswest", -offset->offset / 60);
     741            4 :                         add_assoc_long(return_value, "dsttime", -offset->is_dst);
     742              : 
     743            4 :                         timelib_time_offset_dtor(offset);
     744              :                 } else {
     745            4 :                         RETVAL_NEW_STR(zend_strpprintf(0, "%.8F %ld", tm->us / 1000000.00, (long) tm->sse));
     746              :                 }
     747              :         }
     748              : 
     749           16 :         timelib_time_dtor(tm);
     750              : }
     751              : 
     752           16 : static void hook_microtime(INTERNAL_FUNCTION_PARAMETERS)
     753              : {
     754           16 :         CHECK_STATE(microtime);
     755              : 
     756            8 :         gettimeofday_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
     757              : }
     758              : 
     759           16 : static void hook_gettimeofday(INTERNAL_FUNCTION_PARAMETERS)
     760              : {
     761           16 :         CHECK_STATE(gettimeofday);
     762              : 
     763            8 :         gettimeofday_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
     764              : }
     765              : #endif
     766              : 
     767          164 : bool register_hooks()
     768              : {
     769              :         /* \DateTime::__construct */
     770          164 :         HOOK_CONSTRUCTOR(php_date_get_date_ce(), dt);
     771              : 
     772              :         /* \DateTimeImmutabel::__construct */
     773          164 :         HOOK_CONSTRUCTOR(php_date_get_immutable_ce(), dti);
     774              : 
     775              :         /* \DateTime::createFromFormat */
     776          164 :         HOOK_METHOD(php_date_get_date_ce(), dt, createfromformat);
     777              : 
     778              :         /* \DateTimeImmutable::createFromFormat */
     779          164 :         HOOK_METHOD(php_date_get_immutable_ce(), dti, createfromformat);
     780              : 
     781          164 :         HOOK_FUNCTION(time);
     782          164 :         HOOK_FUNCTION(mktime);
     783          164 :         HOOK_FUNCTION(gmmktime);
     784          164 :         HOOK_FUNCTION(date_create);
     785          164 :         HOOK_FUNCTION(date_create_immutable);
     786          164 :         HOOK_FUNCTION(date_create_from_format);
     787          164 :         HOOK_FUNCTION(date_create_immutable_from_format);
     788          164 :         HOOK_FUNCTION(date);
     789          164 :         HOOK_FUNCTION(gmdate);
     790          164 :         HOOK_FUNCTION(idate);
     791          164 :         HOOK_FUNCTION(getdate);
     792          164 :         HOOK_FUNCTION(localtime);
     793          164 :         HOOK_FUNCTION(strtotime);
     794              : 
     795              : #if HAVE_GETTIMEOFDAY
     796          164 :         HOOK_FUNCTION(microtime);
     797          164 :         HOOK_FUNCTION(gettimeofday);
     798              : #endif
     799              : 
     800          164 :         return true;
     801              : }
     802              : 
     803          164 : void register_pdo_hook()
     804              : {
     805              :         /* \PDO::__construct */
     806          164 :         HOOK_CONSTRUCTOR(php_pdo_get_dbh_ce(), pdo);
     807          164 : }
     808              : 
     809          164 : bool unregister_hooks()
     810              : {
     811              :         /* \DateTime::__construct */
     812          164 :         RESTORE_CONSTRUCTOR(php_date_get_date_ce(), dt);
     813              : 
     814              :         /* \DateTimeImmutabel::__construct */
     815          164 :         RESTORE_CONSTRUCTOR(php_date_get_immutable_ce(), dti);
     816              : 
     817              :         /* \DateTime::createFromFormat */
     818          164 :         RESTORE_METHOD(php_date_get_date_ce(), dt, createfromformat);
     819              : 
     820              :         /* \DateTimeImmutable::createFromFormat */
     821          164 :         RESTORE_METHOD(php_date_get_immutable_ce(), dti, createfromformat);
     822              : 
     823          164 :         RESTORE_FUNCTION(time);
     824          164 :         RESTORE_FUNCTION(mktime);
     825          164 :         RESTORE_FUNCTION(gmmktime);
     826          164 :         RESTORE_FUNCTION(date_create);
     827          164 :         RESTORE_FUNCTION(date_create_immutable);
     828          164 :         RESTORE_FUNCTION(date_create_from_format);
     829          164 :         RESTORE_FUNCTION(date_create_immutable_from_format);
     830          164 :         RESTORE_FUNCTION(date);
     831          164 :         RESTORE_FUNCTION(gmdate);
     832          164 :         RESTORE_FUNCTION(idate);
     833          164 :         RESTORE_FUNCTION(getdate);
     834          164 :         RESTORE_FUNCTION(localtime);
     835          164 :         RESTORE_FUNCTION(strtotime);
     836              : 
     837              : #if HAVE_GETTIMEOFDAY
     838          164 :         RESTORE_FUNCTION(microtime);
     839          164 :         RESTORE_FUNCTION(gettimeofday);
     840              : #endif
     841              : 
     842          164 :         return true;
     843              : }
     844              : 
     845          172 : void apply_request_time_hook()
     846              : {
     847              :         zval *globals_server, *request_time, *request_time_float;
     848              :         timelib_time *t;
     849              :         timelib_rel_time interval;
     850              : 
     851          172 :         globals_server = zend_hash_str_find(&EG(symbol_table), "_SERVER", strlen("_SERVER"));
     852              : 
     853          172 :         if (!globals_server || Z_TYPE_P(globals_server) != IS_ARRAY) {
     854              :                 /* $_SERVER not defined */
     855            0 :                 return;
     856              :         }
     857              : 
     858          172 :         request_time = zend_hash_str_find(Z_ARR_P(globals_server), "REQUEST_TIME", strlen("REQUEST_TIME"));
     859          172 :         request_time_float = zend_hash_str_find(Z_ARR_P(globals_server), "REQUEST_TIME_FLOAT", strlen("REQUEST_TIME_FLOAT"));
     860              : 
     861              :         /* Get original request time at once */
     862          172 :         if (COLOPL_TS_G(orig_request_time) == 0 && COLOPL_TS_G(orig_request_time_float) == 0) {
     863          128 :                 if (request_time_float) {
     864          128 :                         COLOPL_TS_G(orig_request_time_float) = Z_DVAL_P(request_time_float);
     865            0 :                 } else if (request_time) {
     866            0 :                         COLOPL_TS_G(orig_request_time) = Z_LVAL_P(request_time);
     867              :                 } else {
     868              :                         /* Missing REQUEST_TIME or REQUEST_TIME_FLOAT */
     869            0 :                         return;
     870              :                 }
     871              :         }
     872              : 
     873          172 :         if (COLOPL_TS_G(orig_request_time_float) != 0) {
     874          172 :                 timelib_sll ts = (timelib_sll) COLOPL_TS_G(orig_request_time_float);
     875          172 :                 timelib_sll tus = (timelib_sll) ((COLOPL_TS_G(orig_request_time_float) - ts) * 1e6);
     876              : 
     877          172 :                 t = timelib_time_ctor();
     878          172 :                 timelib_unixtime2gmt(t, ts);
     879          172 :                 t->us = tus;
     880          172 :                 timelib_update_ts(t, NULL);
     881            0 :         } else if (COLOPL_TS_G(orig_request_time) != 0) {
     882            0 :                 t = timelib_time_ctor();
     883            0 :                 timelib_unixtime2gmt(t, (timelib_sll) COLOPL_TS_G(orig_request_time));
     884              :         } else {
     885              :                 /* REQUEST_TIME or REQUEST_TIME_FLOAT not found */
     886            0 :                 return;
     887              :         }
     888              : 
     889              :         /* Apply interval. */
     890          172 :         get_shift_interval(&interval);
     891          172 :         apply_interval(&t, &interval);
     892              : 
     893          172 :         if (request_time) {
     894          172 :                 ZVAL_LONG(request_time, (zend_long) t->sse);
     895              :         }
     896              : 
     897          172 :         if (request_time_float) {
     898          172 :                 ZVAL_DOUBLE(request_time_float, ((double) t->sse + ((double) t->us / 1000000.0)));
     899              :         }
     900              : 
     901          172 :         timelib_time_dtor(t);
     902              : }
        

Generated by: LCOV version 2.0-1