LCOV - code coverage report
Current view: top level - /ext - hook.c (source / functions) Coverage Total Hit
Test: Coverage Report Lines: 83.9 % 378 317
Test Date: 2025-09-24 08:46:11 Functions: - 0 0

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

Generated by: LCOV version 2.0-1