LCOV - code coverage report
Current view: top level - ext - hook.c (source / functions) Coverage Total Hit
Test: Extension code coverage Lines: 89.3 % 819 731
Test Date: 2026-09-01 07:24:29 Functions: 100.0 % 75 75

            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              : 
      14              : #ifdef HAVE_CONFIG_H
      15              : # include "config.h"
      16              : #endif
      17              : 
      18              : #include <stdint.h>
      19              : #include <time.h>
      20              : 
      21              : #include "hook.h"
      22              : #include "php_colopl_timeshifter.h"
      23              : 
      24              : #include "php.h"
      25              : 
      26              : #include "Zend/zend_exceptions.h"
      27              : #include "Zend/zend_interfaces.h"
      28              : #include "ext/date/php_date.h"
      29              : #ifdef COLOPL_TIMESHIFTER_HAVE_PDO
      30              : # include "ext/pdo/php_pdo.h"
      31              : # include "ext/pdo/php_pdo_driver.h"
      32              : #endif
      33              : 
      34              : #ifdef PHP_WIN32
      35              : # include <win32/time.h>
      36              : #else
      37              : # include <sys/time.h>
      38              : #endif
      39              : 
      40              : typedef struct _format_flags_t {
      41              :         bool y, m, d, h, i, s, us;
      42              : } format_flags_t;
      43              : 
      44              : typedef struct _datetime_parts_t {
      45              :         zend_long y, m, d, h, i, s, us;
      46              : } datetime_parts_t;
      47              : 
      48          128 : static inline void parse_format(const char *format, format_flags_t *flags) {
      49              :         const char *c;
      50          128 :         bool skip_next = false;
      51              : 
      52          128 :         memset(flags, 0, sizeof(format_flags_t));
      53              : 
      54          760 :         for (c = format; *c != '\0'; c++) {
      55          632 :                 if (skip_next) {
      56           20 :                         skip_next = false;
      57           20 :                         continue;
      58              :                 }
      59          612 :                 switch (*c) {
      60           20 :                         case '\\':
      61           20 :                                 skip_next = true;
      62           20 :                                 continue;
      63           52 :                         case 'X':
      64              :                         case 'x':
      65              :                         case 'Y':
      66              :                         case 'y':
      67           52 :                                 flags->y = true;
      68           52 :                                 continue;
      69           96 :                         case 'F':
      70              :                         case 'M':
      71              :                         case 'm':
      72              :                         case 'n':
      73           96 :                                 flags->m = true;
      74           96 :                                 continue;
      75           60 :                         case 'd':
      76              :                         case 'j':
      77              :                         case 'D':
      78              :                         /* case 'l': */
      79              :                         /* case 'S': */
      80              :                         case 'z':
      81           60 :                                 flags->d = true;
      82           60 :                                 continue;
      83              :                         /* case 'a': */
      84              :                         /* case 'A': */
      85           52 :                         case 'g':
      86              :                         case 'h':
      87              :                         case 'G':
      88              :                         case 'H':
      89           52 :                                 flags->h = true;
      90           52 :                                 continue;
      91           56 :                         case 'i':
      92           56 :                                 flags->i = true;
      93           56 :                                 continue;
      94           32 :                         case 's':
      95           32 :                                 flags->s = true;
      96           32 :                                 continue;
      97           44 :                         case 'v':
      98              :                         case 'u':
      99           44 :                                 flags->us = true;
     100           44 :                                 continue;
     101           20 :                         case '!':
     102              :                         case '|':
     103              :                         case 'U':
     104           20 :                                 flags->y = true;
     105           20 :                                 flags->m = true;
     106           20 :                                 flags->d = true;
     107           20 :                                 flags->h = true;
     108           20 :                                 flags->i = true;
     109           20 :                                 flags->s = true;
     110           20 :                                 flags->us = true;
     111           20 :                                 continue;
     112          180 :                         default:
     113          180 :                                 continue;
     114              :                 }
     115              :         }
     116          128 : }
     117              : 
     118         3698 : static inline zend_long sec_from_usec(int64_t usec)
     119              : {
     120         3698 :         if (usec >= 0) {
     121         3666 :                 return (zend_long) (usec / 1000000);
     122              :         }
     123              : 
     124           32 :         return (zend_long) -(((-usec) + 999999) / 1000000);
     125              : }
     126              : 
     127         1562 : static inline zend_long usec_remainder(int64_t usec)
     128              : {
     129         1562 :         zend_long sec = sec_from_usec(usec);
     130         1562 :         return (zend_long) (usec - ((int64_t) sec * 1000000));
     131              : }
     132              : 
     133          810 : static inline int64_t get_real_time_usec(void)
     134              : {
     135              : #if HAVE_GETTIMEOFDAY
     136          810 :         struct timeval tp = {0};
     137              : 
     138          810 :         if (gettimeofday(&tp, NULL)) {
     139            0 :                 ZEND_ASSERT(0 && "gettimeofday() can't fail");
     140              :         }
     141              : 
     142          810 :         return ((int64_t) tp.tv_sec * 1000000) + tp.tv_usec;
     143              : #else
     144              :         return (int64_t) time(NULL) * 1000000;
     145              : #endif
     146              : }
     147              : 
     148         1526 : static inline void format_timestamp_usec(int64_t usec, char *buf, size_t buf_len)
     149              : {
     150              :         zend_long sec, rem;
     151              : 
     152         1526 :         sec = sec_from_usec(usec);
     153         1526 :         rem = usec_remainder(usec);
     154              : 
     155         1526 :         if (rem < 0) {
     156            0 :                 rem = -rem;
     157              :         }
     158              : 
     159         1526 :         slprintf(buf, buf_len, ZEND_LONG_FMT ".%06" ZEND_LONG_FMT_SPEC, sec, rem);
     160         1526 : }
     161              : 
     162         1042 : static bool parse_timestamp_usec(zend_string *value, int64_t *usec)
     163              : {
     164              :         const char *p, *end;
     165         1042 :         int64_t sec = 0, fraction = 0;
     166         1042 :         bool negative = false;
     167         1042 :         int digits = 0;
     168              : 
     169         1042 :         p = ZSTR_VAL(value);
     170         1042 :         end = p + ZSTR_LEN(value);
     171              : 
     172         1042 :         if (p < end && *p == '-') {
     173           24 :                 negative = true;
     174           24 :                 p++;
     175              :         }
     176              : 
     177         1042 :         if (p >= end || *p < '0' || *p > '9') {
     178            0 :                 return false;
     179              :         }
     180              : 
     181        11450 :         while (p < end && *p >= '0' && *p <= '9') {
     182        10408 :                 int digit = *p - '0';
     183              : 
     184        10408 :                 if (sec > (INT64_MAX - digit) / 10) {
     185            0 :                         return false;
     186              :                 }
     187        10408 :                 sec = sec * 10 + digit;
     188        10408 :                 p++;
     189              :         }
     190              : 
     191         1042 :         if (p < end && *p == '.') {
     192         1042 :                 p++;
     193         7294 :                 while (p < end && *p >= '0' && *p <= '9' && digits < 6) {
     194         6252 :                         fraction = fraction * 10 + (*p - '0');
     195         6252 :                         p++;
     196         6252 :                         digits++;
     197              :                 }
     198              :         }
     199              : 
     200         1042 :         while (digits < 6) {
     201            0 :                 fraction *= 10;
     202            0 :                 digits++;
     203              :         }
     204              : 
     205         1042 :         if (sec > (INT64_MAX - fraction) / 1000000) {
     206            0 :                 return false;
     207              :         }
     208              : 
     209         1042 :         *usec = (sec * 1000000) + fraction;
     210         1042 :         if (negative) {
     211           24 :                 *usec = -*usec;
     212              :         }
     213              : 
     214         1042 :         return true;
     215              : }
     216              : 
     217              : #define CALL_ORIGINAL_FUNCTION_WITH_PARAMS_RET(_name, _retval, _params, _param_count) \
     218              :         do { \
     219              :                 zend_fcall_info fci = { \
     220              :                         .size = sizeof(zend_fcall_info), \
     221              :                         .retval = _retval, \
     222              :                         .param_count = _param_count, \
     223              :                         .params = _params, \
     224              :                 }; \
     225              :                 zend_function fnc = { \
     226              :                         .type = ZEND_INTERNAL_FUNCTION, \
     227              :                 }; \
     228              :                 zend_fcall_info_cache fcc = { \
     229              :                         .function_handler = &fnc, \
     230              :                 }; \
     231              :                 zend_function *src = (zend_function *) zend_hash_str_find_ptr(CG(function_table), #_name, strlen(#_name)); \
     232              :                 ZEND_ASSERT(src); \
     233              :                 memcpy(&fnc.internal_function, &src->internal_function, sizeof(zend_internal_function)); \
     234              :                 fnc.internal_function.handler = COLOPL_TS_G(orig_##_name); \
     235              :                 zend_call_function(&fci, &fcc); \
     236              :         } while (0);
     237              : 
     238              : #define CALL_ORIGINAL_FUNCTION_WITH_PARAMS(_name, _params, _param_count) \
     239              :         CALL_ORIGINAL_FUNCTION_WITH_PARAMS_RET(_name, return_value, _params, _param_count)
     240              : 
     241              : #define CALL_ORIGINAL_FUNCTION(name) \
     242              :         do { \
     243              :                 COLOPL_TS_G(orig_##name)(INTERNAL_FUNCTION_PARAM_PASSTHRU); \
     244              :         } while (0);
     245              : 
     246              : #define CHECK_STATE(name) \
     247              :         do { \
     248              :                 if (!get_is_hooked() || COLOPL_TS_G(in_internal_call)) { \
     249              :                         CALL_ORIGINAL_FUNCTION(name); \
     250              :                         return; \
     251              :                 } \
     252              :         } while (0);
     253              : 
     254              : #define DEFINE_DT_HOOK_CONSTRUCTOR(name) \
     255              :         static void ZEND_FASTCALL hook_##name##_con(INTERNAL_FUNCTION_PARAMETERS) \
     256              :         { \
     257              :                 CHECK_STATE(name##_con); \
     258              :                 \
     259              :                 CALL_ORIGINAL_FUNCTION(name##_con); \
     260              :                 if (EG(exception)) { \
     261              :                         return; \
     262              :                 } \
     263              :                 \
     264              :                 zend_string *datetime = NULL; \
     265              :                 zval *timezone = NULL, object_timezone; \
     266              :                 php_date_obj *date = NULL; \
     267              :                 int64_t timestamp_usec; \
     268              :                 \
     269              :                 ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 0, 2) \
     270              :                         Z_PARAM_OPTIONAL; \
     271              :                         Z_PARAM_STR_OR_NULL(datetime); \
     272              :                         Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone, php_date_get_timezone_ce()); \
     273              :                 ZEND_PARSE_PARAMETERS_END(); \
     274              :                 \
     275              :                 date = Z_PHPDATE_P(ZEND_THIS); \
     276              :                 \
     277              :                 /* Early return if construction failed. */ \
     278              :                 if (!date || !date->time) { \
     279              :                         return; \
     280              :                 } \
     281              :                 \
     282              :                 if (!time_string_depends_on_current(datetime)) { \
     283              :                         return; \
     284              :                 } \
     285              :                 \
     286              :                 ZVAL_UNDEF(&object_timezone); \
     287              :                 if (!get_date_timezone(ZEND_THIS, &object_timezone)) { \
     288              :                         return; \
     289              :                 } \
     290              :                 \
     291              :                 if (!resolve_time_string_to_timestamp_usec(datetime, &object_timezone, &timestamp_usec)) { \
     292              :                         zval_ptr_dtor(&object_timezone); \
     293              :                         return; \
     294              :                 } \
     295              :                 \
     296              :                 initialize_date_from_timestamp_usec(ZEND_THIS, timestamp_usec, &object_timezone); \
     297              :                 zval_ptr_dtor(&object_timezone); \
     298              :         }
     299              : 
     300              : #define DEFINE_CREATE_FROM_FORMAT_EX(fname, name) \
     301              :         static void ZEND_FASTCALL hook_##fname(INTERNAL_FUNCTION_PARAMETERS) { \
     302              :                 CHECK_STATE(name); \
     303              :                 \
     304              :                 zend_string *format, *_datetime; \
     305              :                 zval *_timezone_object; \
     306              :                 format_flags_t flags; \
     307              :                 \
     308              :                 CALL_ORIGINAL_FUNCTION(name); \
     309              :                 if (EG(exception) || !return_value || Z_TYPE_P(return_value) == IS_FALSE || !Z_PHPDATE_P(return_value)->time) { \
     310              :                         return; \
     311              :                 } \
     312              :                 \
     313              :                 ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 2, 3); \
     314              :                         Z_PARAM_STR(format) \
     315              :                         Z_PARAM_STR(_datetime) \
     316              :                         Z_PARAM_OPTIONAL \
     317              :                         Z_PARAM_OBJECT_OF_CLASS_OR_NULL(_timezone_object, php_date_get_timezone_ce()) \
     318              :                 ZEND_PARSE_PARAMETERS_END(); \
     319              :                 \
     320              :                 parse_format(ZSTR_VAL(format), &flags); \
     321              :                 adjust_create_from_format_result(return_value, &flags); \
     322              :         }
     323              : 
     324              : #define DEFINE_CREATE_FROM_FORMAT(name) \
     325              :         DEFINE_CREATE_FROM_FORMAT_EX(name, name);
     326              : 
     327              : #define HOOK_CONSTRUCTOR(ce, name) \
     328              :         do { \
     329              :                 COLOPL_TS_G(orig_##name##_con) = ce->constructor->internal_function.handler; \
     330              :                 ce->constructor->internal_function.handler = hook_##name##_con; \
     331              :         } while (0);
     332              : 
     333              : #define HOOK_METHOD(ce, name, method) \
     334              :         do { \
     335              :                 zend_function *php_function_entry = zend_hash_str_find_ptr(&ce->function_table, #method, strlen(#method)); \
     336              :                 ZEND_ASSERT(php_function_entry); \
     337              :                 COLOPL_TS_G(orig_##name##_##method) = php_function_entry->internal_function.handler; \
     338              :                 php_function_entry->internal_function.handler = hook_##name##_##method; \
     339              :         } while (0);
     340              : 
     341              : #define HOOK_FUNCTION(name) \
     342              :         do { \
     343              :                 zend_function *php_function_entry = zend_hash_str_find_ptr(CG(function_table), #name, strlen(#name)); \
     344              :                 ZEND_ASSERT(php_function_entry); \
     345              :                 COLOPL_TS_G(orig_##name) = php_function_entry->internal_function.handler; \
     346              :                 php_function_entry->internal_function.handler = hook_##name; \
     347              :         } while (0);
     348              : 
     349              : #define HOOK_FUNCTION_OPTIONAL(name) \
     350              :         do { \
     351              :                 zend_function *php_function_entry = zend_hash_str_find_ptr(CG(function_table), #name, strlen(#name)); \
     352              :                 if (php_function_entry) { \
     353              :                         COLOPL_TS_G(orig_##name) = php_function_entry->internal_function.handler; \
     354              :                         php_function_entry->internal_function.handler = hook_##name; \
     355              :                 } \
     356              :         } while (0);
     357              : 
     358              : #define RESTORE_CONSTRUCTOR(ce, name) \
     359              :         do { \
     360              :                 ZEND_ASSERT(COLOPL_TS_G(orig_##name##_con)); \
     361              :                 ce->constructor->internal_function.handler = COLOPL_TS_G(orig_##name##_con); \
     362              :                 COLOPL_TS_G(orig_##name##_con) = NULL; \
     363              :         } while (0);
     364              : 
     365              : #define RESTORE_METHOD(ce, name, method) \
     366              :         do { \
     367              :                 zend_function *php_function_entry = zend_hash_str_find_ptr(&ce->function_table, #method, strlen(#method)); \
     368              :                 ZEND_ASSERT(php_function_entry); \
     369              :                 ZEND_ASSERT(COLOPL_TS_G(orig_##name##_##method)); \
     370              :                 php_function_entry->internal_function.handler = COLOPL_TS_G(orig_##name##_##method); \
     371              :                 COLOPL_TS_G(orig_##name##_##method) = NULL; \
     372              :         } while (0);
     373              : 
     374              : #define RESTORE_FUNCTION(name) \
     375              :         do { \
     376              :                 zend_function *php_function_entry = zend_hash_str_find_ptr(CG(function_table), #name, strlen(#name)); \
     377              :                 ZEND_ASSERT(php_function_entry); \
     378              :                 ZEND_ASSERT(COLOPL_TS_G(orig_##name)); \
     379              :                 php_function_entry->internal_function.handler = COLOPL_TS_G(orig_##name); \
     380              :                 COLOPL_TS_G(orig_##name) = NULL; \
     381              :         } while (0);
     382              : 
     383              : #define RESTORE_FUNCTION_OPTIONAL(name) \
     384              :         do { \
     385              :                 if (COLOPL_TS_G(orig_##name)) { \
     386              :                         zend_function *php_function_entry = zend_hash_str_find_ptr(CG(function_table), #name, strlen(#name)); \
     387              :                         if (php_function_entry) { \
     388              :                                 php_function_entry->internal_function.handler = COLOPL_TS_G(orig_##name); \
     389              :                         } \
     390              :                         COLOPL_TS_G(orig_##name) = NULL; \
     391              :                 } \
     392              :         } while (0);
     393              : 
     394         1526 : static inline bool create_datetime_from_timestamp_usec(int64_t timestamp_usec, zval *datetime)
     395              : {
     396              :         char timestamp[64];
     397              : 
     398         1526 :         php_date_instantiate(php_date_get_immutable_ce(), datetime);
     399         1526 :         format_timestamp_usec(timestamp_usec, timestamp, sizeof(timestamp));
     400              : 
     401         1526 :         if (!php_date_initialize(Z_PHPDATE_P(datetime), timestamp, strlen(timestamp), "U.u", NULL, PHP_DATE_INIT_FORMAT)) {
     402            0 :                 zval_ptr_dtor(datetime);
     403            0 :                 ZVAL_UNDEF(datetime);
     404            0 :                 return false;
     405              :         }
     406              : 
     407         1526 :         return true;
     408              : }
     409              : 
     410         1430 : static inline bool call_date_format(zval *datetime, const char *format, zval *formatted)
     411              : {
     412              :         zval format_zv;
     413              : 
     414         1430 :         ZVAL_STRING(&format_zv, format);
     415         1430 :         ZVAL_UNDEF(formatted);
     416         1430 :         zend_call_method_with_1_params(Z_OBJ_P(datetime), Z_OBJCE_P(datetime), NULL, "format", formatted, &format_zv);
     417         1430 :         zval_ptr_dtor(&format_zv);
     418              : 
     419         1430 :         return Z_TYPE_P(formatted) == IS_STRING;
     420              : }
     421              : 
     422          236 : static inline bool get_date_timezone(zval *datetime, zval *timezone)
     423              : {
     424          236 :         ZVAL_UNDEF(timezone);
     425          236 :         zend_call_method_with_0_params(Z_OBJ_P(datetime), Z_OBJCE_P(datetime), NULL, "gettimezone", timezone);
     426              : 
     427          236 :         return Z_TYPE_P(timezone) == IS_OBJECT && instanceof_function(Z_OBJCE_P(timezone), php_date_get_timezone_ce());
     428              : }
     429              : 
     430         1286 : static inline bool localize_datetime(zval *datetime, zval *timezone, zval *localized)
     431              : {
     432         1286 :         ZVAL_UNDEF(localized);
     433              : 
     434         1286 :         if (timezone && Z_TYPE_P(timezone) == IS_OBJECT) {
     435          488 :                 zend_call_method_with_1_params(Z_OBJ_P(datetime), Z_OBJCE_P(datetime), NULL, "settimezone", localized, timezone);
     436          488 :                 return Z_TYPE_P(localized) == IS_OBJECT;
     437              :         }
     438              : 
     439          798 :         ZVAL_COPY(localized, datetime);
     440          798 :         return true;
     441              : }
     442              : 
     443         1042 : static bool create_interval_from_snapshot(const colopl_timeshifter_interval_t *src, zval *interval)
     444              : {
     445              :         php_interval_obj *interval_obj;
     446              : 
     447         1042 :         if (!src->initialized) {
     448            0 :                 return false;
     449              :         }
     450              : 
     451         1042 :         php_date_instantiate(php_date_get_interval_ce(), interval);
     452              : 
     453         1042 :         interval_obj = Z_PHPINTERVAL_P(interval);
     454         1042 :         interval_obj->diff = ecalloc(1, sizeof(timelib_rel_time));
     455         1042 :         if (!interval_obj->diff) {
     456            0 :                 zval_ptr_dtor(interval);
     457            0 :                 ZVAL_UNDEF(interval);
     458            0 :                 return false;
     459              :         }
     460              : 
     461         1042 :         memcpy(interval_obj->diff, &src->diff, sizeof(timelib_rel_time));
     462         1042 :         interval_obj->civil_or_wall = src->civil_or_wall;
     463              : #if PHP_VERSION_ID >= 80200
     464         1042 :         interval_obj->from_string = false;
     465         1042 :         interval_obj->date_string = NULL;
     466              : #endif
     467         1042 :         interval_obj->initialized = true;
     468              : 
     469         1042 :         return true;
     470              : }
     471              : 
     472         1042 : static inline bool get_datetime_timestamp_usec(zval *datetime, int64_t *timestamp_usec)
     473              : {
     474              :         zval formatted;
     475              :         bool result;
     476              : 
     477         1042 :         if (!call_date_format(datetime, "U.u", &formatted)) {
     478            0 :                 return false;
     479              :         }
     480              : 
     481         1042 :         result = parse_timestamp_usec(Z_STR(formatted), timestamp_usec);
     482         1042 :         zval_ptr_dtor(&formatted);
     483              : 
     484         1042 :         return result;
     485              : }
     486              : 
     487          240 : bool validate_shift_interval(zval *interval)
     488              : {
     489              :         zval datetime, shifted;
     490          240 :         bool result = false, previous_internal_call;
     491              : 
     492          240 :         if (!create_datetime_from_timestamp_usec(get_real_time_usec(), &datetime)) {
     493            0 :                 return false;
     494              :         }
     495              : 
     496          240 :         previous_internal_call = COLOPL_TS_G(in_internal_call);
     497          240 :         COLOPL_TS_G(in_internal_call) = true;
     498          240 :         ZVAL_UNDEF(&shifted);
     499          240 :         zend_call_method_with_1_params(Z_OBJ_P(&datetime), Z_OBJCE_P(&datetime), NULL, "sub", &shifted, interval);
     500          240 :         COLOPL_TS_G(in_internal_call) = previous_internal_call;
     501              : 
     502          240 :         if (EG(exception)) {
     503            0 :                 zend_clear_exception();
     504          240 :         } else if (Z_TYPE(shifted) == IS_OBJECT) {
     505          240 :                 result = true;
     506              :         }
     507              : 
     508          240 :         if (!Z_ISUNDEF(shifted)) {
     509          240 :                 zval_ptr_dtor(&shifted);
     510              :         }
     511          240 :         zval_ptr_dtor(&datetime);
     512              : 
     513          240 :         return result;
     514              : }
     515              : 
     516         1042 : static bool calculate_shifted_time_usec(int64_t real_usec, zval *timezone, int64_t *shifted_usec)
     517              : {
     518              :         colopl_timeshifter_interval_t interval_snapshot;
     519              :         zval datetime, localized, interval, shifted;
     520         1042 :         bool result = false, previous_internal_call;
     521              : 
     522         1042 :         if (!get_shift_interval(&interval_snapshot)) {
     523            0 :                 *shifted_usec = real_usec;
     524            0 :                 return true;
     525              :         }
     526              : 
     527         1042 :         if (!create_datetime_from_timestamp_usec(real_usec, &datetime)) {
     528            0 :                 return false;
     529              :         }
     530              : 
     531         1042 :         if (!localize_datetime(&datetime, timezone, &localized)) {
     532            0 :                 zval_ptr_dtor(&datetime);
     533            0 :                 return false;
     534              :         }
     535              : 
     536         1042 :         if (!create_interval_from_snapshot(&interval_snapshot, &interval)) {
     537            0 :                 zval_ptr_dtor(&localized);
     538            0 :                 zval_ptr_dtor(&datetime);
     539            0 :                 return false;
     540              :         }
     541              : 
     542         1042 :         previous_internal_call = COLOPL_TS_G(in_internal_call);
     543         1042 :         COLOPL_TS_G(in_internal_call) = true;
     544         1042 :         ZVAL_UNDEF(&shifted);
     545         1042 :         zend_call_method_with_1_params(Z_OBJ_P(&localized), Z_OBJCE_P(&localized), NULL, "sub", &shifted, &interval);
     546         1042 :         COLOPL_TS_G(in_internal_call) = previous_internal_call;
     547              : 
     548         1042 :         if (EG(exception)) {
     549            0 :                 zend_clear_exception();
     550         1042 :         } else if (Z_TYPE(shifted) == IS_OBJECT) {
     551         1042 :                 result = get_datetime_timestamp_usec(&shifted, shifted_usec);
     552              :         }
     553         1042 :         if (!Z_ISUNDEF(shifted)) {
     554         1042 :                 zval_ptr_dtor(&shifted);
     555              :         }
     556              : 
     557         1042 :         zval_ptr_dtor(&interval);
     558         1042 :         zval_ptr_dtor(&localized);
     559         1042 :         zval_ptr_dtor(&datetime);
     560              : 
     561         1042 :         return result;
     562              : }
     563              : 
     564          570 : static inline int64_t get_shifted_time_usec_for_timezone(zval *timezone)
     565              : {
     566          570 :         int64_t real_usec = get_real_time_usec(), shifted_usec;
     567              : 
     568          570 :         if (!calculate_shifted_time_usec(real_usec, timezone, &shifted_usec)) {
     569            0 :                 return real_usec;
     570              :         }
     571              : 
     572          570 :         return shifted_usec;
     573              : }
     574              : 
     575          326 : static inline int64_t get_shifted_time_usec(void)
     576              : {
     577          326 :         return get_shifted_time_usec_for_timezone(NULL);
     578              : }
     579              : 
     580          290 : static inline zend_long get_shifted_time(void)
     581              : {
     582          290 :         return sec_from_usec(get_shifted_time_usec());
     583              : }
     584              : 
     585          244 : static bool get_timestamp_parts(int64_t timestamp_usec, zval *timezone, datetime_parts_t *parts)
     586              : {
     587              :         zval datetime, localized, formatted;
     588              :         int parsed;
     589              : 
     590          244 :         if (!create_datetime_from_timestamp_usec(timestamp_usec, &datetime)) {
     591            0 :                 return false;
     592              :         }
     593              : 
     594          244 :         if (!localize_datetime(&datetime, timezone, &localized)) {
     595            0 :                 zval_ptr_dtor(&datetime);
     596            0 :                 return false;
     597              :         }
     598              : 
     599          244 :         if (!call_date_format(&localized, "Y n j G i s u", &formatted)) {
     600            0 :                 zval_ptr_dtor(&localized);
     601            0 :                 zval_ptr_dtor(&datetime);
     602            0 :                 return false;
     603              :         }
     604              : 
     605          244 :         parsed = sscanf(
     606          244 :                 Z_STRVAL(formatted),
     607              :                 ZEND_LONG_FMT " " ZEND_LONG_FMT " " ZEND_LONG_FMT " " ZEND_LONG_FMT " " ZEND_LONG_FMT " " ZEND_LONG_FMT " " ZEND_LONG_FMT,
     608              :                 &parts->y,
     609              :                 &parts->m,
     610              :                 &parts->d,
     611              :                 &parts->h,
     612              :                 &parts->i,
     613              :                 &parts->s,
     614              :                 &parts->us
     615              :         );
     616              : 
     617          244 :         zval_ptr_dtor(&formatted);
     618          244 :         zval_ptr_dtor(&localized);
     619          244 :         zval_ptr_dtor(&datetime);
     620              : 
     621          244 :         return parsed == 7;
     622              : }
     623              : 
     624          144 : static bool get_date_parts(zval *datetime, datetime_parts_t *parts)
     625              : {
     626              :         zval formatted;
     627              :         int parsed;
     628              : 
     629          144 :         if (!call_date_format(datetime, "Y n j G i s u", &formatted)) {
     630            0 :                 return false;
     631              :         }
     632              : 
     633          144 :         parsed = sscanf(
     634          144 :                 Z_STRVAL(formatted),
     635              :                 ZEND_LONG_FMT " " ZEND_LONG_FMT " " ZEND_LONG_FMT " " ZEND_LONG_FMT " " ZEND_LONG_FMT " " ZEND_LONG_FMT " " ZEND_LONG_FMT,
     636              :                 &parts->y,
     637              :                 &parts->m,
     638              :                 &parts->d,
     639              :                 &parts->h,
     640              :                 &parts->i,
     641              :                 &parts->s,
     642              :                 &parts->us
     643              :         );
     644              : 
     645          144 :         zval_ptr_dtor(&formatted);
     646              : 
     647          144 :         return parsed == 7;
     648              : }
     649              : 
     650          236 : static bool initialize_date_from_parts(zval *datetime, datetime_parts_t *parts, zval *timezone)
     651              : {
     652              :         char date[128];
     653              : 
     654          236 :         slprintf(
     655              :                 date,
     656              :                 sizeof(date),
     657              :                 ZEND_LONG_FMT "-%02" ZEND_LONG_FMT_SPEC "-%02" ZEND_LONG_FMT_SPEC " %02" ZEND_LONG_FMT_SPEC ":%02" ZEND_LONG_FMT_SPEC ":%02" ZEND_LONG_FMT_SPEC ".%06" ZEND_LONG_FMT_SPEC,
     658              :                 parts->y,
     659              :                 parts->m,
     660              :                 parts->d,
     661              :                 parts->h,
     662              :                 parts->i,
     663              :                 parts->s,
     664              :                 parts->us
     665              :         );
     666              : 
     667          236 :         return php_date_initialize(Z_PHPDATE_P(datetime), date, strlen(date), "Y-m-d H:i:s.u", timezone, PHP_DATE_INIT_FORMAT);
     668              : }
     669              : 
     670          108 : static inline bool initialize_date_from_timestamp_usec(zval *datetime, int64_t timestamp_usec, zval *timezone)
     671              : {
     672              :         datetime_parts_t parts;
     673              : 
     674          108 :         if (!get_timestamp_parts(timestamp_usec, timezone, &parts)) {
     675            0 :                 return false;
     676              :         }
     677              : 
     678          108 :         return initialize_date_from_parts(datetime, &parts, timezone);
     679              : }
     680              : 
     681          320 : static inline bool call_original_strtotime(zend_string *times, zend_long base, zval *retval)
     682              : {
     683              :         zval params[2];
     684              : 
     685          320 :         ZVAL_STR(&params[0], times);
     686          320 :         ZVAL_LONG(&params[1], base);
     687          320 :         ZVAL_UNDEF(retval);
     688          320 :         CALL_ORIGINAL_FUNCTION_WITH_PARAMS_RET(strtotime, retval, params, 2);
     689              : 
     690          320 :         return Z_TYPE_P(retval) == IS_LONG;
     691              : }
     692              : 
     693          156 : static bool call_named_function(const char *name, zval *retval, zval *params, uint32_t param_count)
     694              : {
     695          156 :         zend_fcall_info fci = {
     696              :                 .size = sizeof(zend_fcall_info),
     697              :                 .retval = retval,
     698              :                 .param_count = param_count,
     699              :                 .params = params,
     700              :         };
     701              :         bool result;
     702              : 
     703          156 :         ZVAL_STRING(&fci.function_name, name);
     704          156 :         ZVAL_UNDEF(retval);
     705          156 :         result = zend_call_function(&fci, NULL) == SUCCESS && !Z_ISUNDEF_P(retval);
     706          156 :         zval_ptr_dtor(&fci.function_name);
     707              : 
     708          156 :         return result;
     709              : }
     710              : 
     711           48 : static inline bool get_timezone_name(zval *timezone, zval *name)
     712              : {
     713           48 :         ZVAL_UNDEF(name);
     714           48 :         zend_call_method_with_0_params(Z_OBJ_P(timezone), Z_OBJCE_P(timezone), NULL, "getname", name);
     715              : 
     716           48 :         return Z_TYPE_P(name) == IS_STRING;
     717              : }
     718              : 
     719           52 : static inline bool get_default_timezone_name(zval *name)
     720              : {
     721           52 :         return call_named_function("date_default_timezone_get", name, NULL, 0) && Z_TYPE_P(name) == IS_STRING;
     722              : }
     723              : 
     724            8 : static bool create_timezone_from_name(zval *name, zval *timezone)
     725              : {
     726              :         zval params[1];
     727              :         bool result;
     728              : 
     729            8 :         ZVAL_COPY(&params[0], name);
     730           16 :         result = call_named_function("timezone_open", timezone, params, 1) &&
     731           16 :                 Z_TYPE_P(timezone) == IS_OBJECT &&
     732            8 :                 instanceof_function(Z_OBJCE_P(timezone), php_date_get_timezone_ce());
     733            8 :         zval_ptr_dtor(&params[0]);
     734              : 
     735            8 :         return result;
     736              : }
     737              : 
     738            4 : static bool get_default_timezone(zval *timezone)
     739              : {
     740              :         zval name;
     741              :         bool result;
     742              : 
     743            4 :         ZVAL_UNDEF(&name);
     744            4 :         if (!get_default_timezone_name(&name)) {
     745            0 :                 return false;
     746              :         }
     747              : 
     748            4 :         result = create_timezone_from_name(&name, timezone);
     749            4 :         zval_ptr_dtor(&name);
     750              : 
     751            4 :         return result;
     752              : }
     753              : 
     754            4 : static bool get_utc_timezone(zval *timezone)
     755              : {
     756              :         zval name;
     757              :         bool result;
     758              : 
     759            4 :         ZVAL_STRING(&name, "UTC");
     760            4 :         result = create_timezone_from_name(&name, timezone);
     761            4 :         zval_ptr_dtor(&name);
     762              : 
     763            4 :         return result;
     764              : }
     765              : 
     766           96 : static inline bool set_default_timezone(zval *name)
     767              : {
     768              :         zval retval, params[1];
     769              :         bool result;
     770              : 
     771           96 :         ZVAL_COPY(&params[0], name);
     772           96 :         result = call_named_function("date_default_timezone_set", &retval, params, 1) && Z_TYPE(retval) == IS_TRUE;
     773           96 :         zval_ptr_dtor(&params[0]);
     774           96 :         if (!Z_ISUNDEF(retval)) {
     775           96 :                 zval_ptr_dtor(&retval);
     776              :         }
     777              : 
     778           96 :         return result;
     779              : }
     780              : 
     781           48 : static bool call_original_strtotime_with_timezone(zend_string *times, zend_long base, zval *timezone, zval *retval)
     782              : {
     783              :         zval timezone_name, original_timezone;
     784           48 :         bool switched_timezone = false, result;
     785              : 
     786           48 :         ZVAL_UNDEF(&timezone_name);
     787           48 :         ZVAL_UNDEF(&original_timezone);
     788              : 
     789           96 :         if (timezone && Z_TYPE_P(timezone) == IS_OBJECT &&
     790           96 :                 get_timezone_name(timezone, &timezone_name) &&
     791           96 :                 get_default_timezone_name(&original_timezone) &&
     792           48 :                 set_default_timezone(&timezone_name)) {
     793           48 :                 switched_timezone = true;
     794              :         }
     795              : 
     796           48 :         result = call_original_strtotime(times, base, retval);
     797              : 
     798           48 :         if (switched_timezone) {
     799           48 :                 set_default_timezone(&original_timezone);
     800              :         }
     801              : 
     802           48 :         if (!Z_ISUNDEF(timezone_name)) {
     803           48 :                 zval_ptr_dtor(&timezone_name);
     804              :         }
     805           48 :         if (!Z_ISUNDEF(original_timezone)) {
     806           48 :                 zval_ptr_dtor(&original_timezone);
     807              :         }
     808              : 
     809           48 :         return result;
     810              : }
     811              : 
     812          460 : static inline bool time_string_is_now(zend_string *datetime)
     813              : {
     814              :         zend_string *datetime_lower;
     815              :         bool result;
     816              : 
     817          460 :         if (!datetime || ZSTR_LEN(datetime) == 0) {
     818           44 :                 return true;
     819              :         }
     820              : 
     821          416 :         datetime_lower = zend_string_tolower(datetime);
     822          832 :         result = ZSTR_LEN(datetime_lower) == sizeof("now") - 1
     823          416 :                 && memcmp(ZSTR_VAL(datetime_lower), "now", sizeof("now") - 1) == 0;
     824          416 :         zend_string_release(datetime_lower);
     825              : 
     826          416 :         return result;
     827              : }
     828              : 
     829          396 : static bool time_string_depends_on_current(zend_string *datetime)
     830              : {
     831              :         zval first, second;
     832              :         bool result;
     833              : 
     834          396 :         if (!datetime || ZSTR_LEN(datetime) == 0 || time_string_is_now(datetime)) {
     835           60 :                 return true;
     836              :         }
     837              : 
     838          336 :         if (ZSTR_VAL(datetime)[0] == '@') {
     839          200 :                 return false;
     840              :         }
     841              : 
     842          136 :         if (!call_original_strtotime(datetime, 946684800, &first)) {
     843            0 :                 if (!Z_ISUNDEF(first)) {
     844            0 :                         zval_ptr_dtor(&first);
     845              :                 }
     846              : 
     847            0 :                 return false;
     848              :         }
     849              : 
     850          136 :         if (!call_original_strtotime(datetime, 978307200, &second)) {
     851            0 :                 zval_ptr_dtor(&first);
     852            0 :                 if (!Z_ISUNDEF(second)) {
     853            0 :                         zval_ptr_dtor(&second);
     854              :                 }
     855              : 
     856            0 :                 return false;
     857              :         }
     858              : 
     859          136 :         result = Z_LVAL(first) != Z_LVAL(second);
     860          136 :         zval_ptr_dtor(&first);
     861          136 :         zval_ptr_dtor(&second);
     862              : 
     863          136 :         return result;
     864              : }
     865              : 
     866          108 : static bool resolve_time_string_to_timestamp_usec(zend_string *datetime, zval *timezone, int64_t *timestamp_usec)
     867              : {
     868              :         zend_long base;
     869              :         zval parsed;
     870              : 
     871          108 :         if (time_string_is_now(datetime)) {
     872           60 :                 *timestamp_usec = get_shifted_time_usec_for_timezone(timezone);
     873           60 :                 return true;
     874              :         }
     875              : 
     876           48 :         if (!datetime) {
     877            0 :                 *timestamp_usec = get_shifted_time_usec_for_timezone(timezone);
     878            0 :                 return true;
     879              :         }
     880              : 
     881           48 :         base = sec_from_usec(get_shifted_time_usec_for_timezone(timezone));
     882           48 :         if (!call_original_strtotime_with_timezone(datetime, base, timezone, &parsed)) {
     883            0 :                 if (!Z_ISUNDEF(parsed)) {
     884            0 :                         zval_ptr_dtor(&parsed);
     885              :                 }
     886            0 :                 return false;
     887              :         }
     888              : 
     889           48 :         *timestamp_usec = (int64_t) Z_LVAL(parsed) * 1000000;
     890              : 
     891           48 :         zval_ptr_dtor(&parsed);
     892              : 
     893           48 :         return true;
     894              : }
     895              : 
     896          128 : static void adjust_create_from_format_result(zval *datetime, format_flags_t *flags)
     897              : {
     898              :         zval timezone;
     899              :         datetime_parts_t original, shifted, result;
     900          128 :         bool has_time = flags->h || flags->i || flags->s || flags->us;
     901              : 
     902          128 :         ZVAL_UNDEF(&timezone);
     903          128 :         if (!get_date_timezone(datetime, &timezone)) {
     904            0 :                 return;
     905              :         }
     906              : 
     907          128 :         if (!get_date_parts(datetime, &original) ||
     908          128 :                 !get_timestamp_parts(get_shifted_time_usec_for_timezone(&timezone), &timezone, &shifted)) {
     909            0 :                 zval_ptr_dtor(&timezone);
     910              : 
     911            0 :                 return;
     912              :         }
     913          128 :         shifted.us = 0;
     914              : 
     915          128 :         result = shifted;
     916          128 :         if (has_time) {
     917           84 :                 result.h = 0;
     918           84 :                 result.i = 0;
     919           84 :                 result.s = 0;
     920           84 :                 result.us = 0;
     921              :         }
     922              : 
     923          128 :         if (flags->y) { result.y = original.y; }
     924          128 :         if (flags->m) { result.m = original.m; }
     925          128 :         if (flags->d) { result.d = original.d; }
     926          128 :         if (flags->h) { result.h = original.h; }
     927          128 :         if (flags->i) { result.i = original.i; }
     928          128 :         if (flags->s) { result.s = original.s; }
     929          128 :         if (flags->us) { result.us = original.us; }
     930              : 
     931          128 :         initialize_date_from_parts(datetime, &result, &timezone);
     932          128 :         zval_ptr_dtor(&timezone);
     933              : }
     934              : 
     935           16 : static void adjust_date_parse_from_format_result(zval *parsed, zend_string *format, zend_string *datetime)
     936              : {
     937              :         datetime_parts_t parts;
     938              :         format_flags_t flags;
     939              :         zval params[2], created;
     940              : 
     941           16 :         if (Z_TYPE_P(parsed) != IS_ARRAY) {
     942            0 :                 return;
     943              :         }
     944              : 
     945           16 :         parse_format(ZSTR_VAL(format), &flags);
     946              : 
     947           16 :         ZVAL_STR(&params[0], format);
     948           16 :         ZVAL_STR(&params[1], datetime);
     949           16 :         ZVAL_UNDEF(&created);
     950           16 :         CALL_ORIGINAL_FUNCTION_WITH_PARAMS_RET(date_create_immutable_from_format, &created, params, 2);
     951              : 
     952           16 :         if (EG(exception) || Z_TYPE(created) != IS_OBJECT || !Z_PHPDATE_P(&created)->time) {
     953            0 :                 if (!Z_ISUNDEF(created)) {
     954            0 :                         zval_ptr_dtor(&created);
     955              :                 }
     956              : 
     957            0 :                 return;
     958              :         }
     959              : 
     960           16 :         adjust_create_from_format_result(&created, &flags);
     961           16 :         if (get_date_parts(&created, &parts)) {
     962           16 :                 add_assoc_long(parsed, "year", parts.y);
     963           16 :                 add_assoc_long(parsed, "month", parts.m);
     964           16 :                 add_assoc_long(parsed, "day", parts.d);
     965           16 :                 add_assoc_long(parsed, "hour", parts.h);
     966           16 :                 add_assoc_long(parsed, "minute", parts.i);
     967           16 :                 add_assoc_long(parsed, "second", parts.s);
     968           16 :                 add_assoc_double(parsed, "fraction", (double) parts.us / 1000000.0);
     969              :         }
     970              : 
     971           16 :         zval_ptr_dtor(&created);
     972              : }
     973              : 
     974              : #ifdef COLOPL_TIMESHIFTER_HAVE_PDO
     975           64 : static inline bool pdo_time_apply(pdo_dbh_t *dbh)
     976              : {
     977              :         zend_string *sql;
     978              :         char buf[1024];
     979              : 
     980           64 :         if (!COLOPL_TS_G(pdo_mysql_orig_methods) || !COLOPL_TS_G(pdo_mysql_orig_methods)->doer) {
     981            0 :                 return false;
     982              :         }
     983              : 
     984           64 :         slprintf(buf, sizeof(buf), "SET @@session.timestamp = " ZEND_LONG_FMT ";", get_shifted_time());
     985           64 :         sql = zend_string_init_fast(buf, strlen(buf));
     986           64 :         COLOPL_TS_G(pdo_mysql_orig_methods)->doer(dbh, sql);
     987           64 :         zend_string_release(sql);
     988              : 
     989           64 :         return true;
     990              : }
     991              : 
     992           24 : static bool hook_pdo_driver_preparer(pdo_dbh_t *dbh, zend_string *sql, pdo_stmt_t *stmt, zval *driver_options)
     993              : {
     994              :         bool retval;
     995              : 
     996           24 :         if (get_is_hooked()) {
     997           24 :                 pdo_time_apply(dbh);
     998              :         }
     999              : 
    1000           24 :         retval = COLOPL_TS_G(pdo_mysql_orig_methods)->preparer(dbh, sql, stmt, driver_options);
    1001              : 
    1002           24 :         return retval;
    1003              : }
    1004              : 
    1005           40 : static zend_long hook_pdo_driver_doer(pdo_dbh_t *dbh, const zend_string *sql)
    1006              : {
    1007           40 :         if (get_is_hooked()) {
    1008           40 :                 pdo_time_apply(dbh);
    1009              :         }
    1010              : 
    1011           40 :         return COLOPL_TS_G(pdo_mysql_orig_methods)->doer(dbh, sql);
    1012              : }
    1013              : 
    1014           20 : static void ZEND_FASTCALL hook_pdo_con(INTERNAL_FUNCTION_PARAMETERS)
    1015              : {
    1016           20 :         CHECK_STATE(pdo_con);
    1017              : 
    1018           20 :         pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
    1019              : 
    1020           20 :         CALL_ORIGINAL_FUNCTION(pdo_con);
    1021           20 :         if (EG(exception)) {
    1022            0 :                 return;
    1023              :         }
    1024              : 
    1025           20 :         if (!dbh->driver || strcmp(dbh->driver->driver_name, "mysql") != 0 ||
    1026           16 :                 dbh->methods == &COLOPL_TS_G(hooked_mysql_driver_methods)) {
    1027            4 :                 return;
    1028              :         }
    1029              : 
    1030           16 :         if (!COLOPL_TS_G(pdo_mysql_orig_methods)) {
    1031           16 :                 if (!dbh->methods || !dbh->methods->preparer || !dbh->methods->doer) {
    1032            0 :                         return;
    1033              :                 }
    1034              : 
    1035           16 :                 COLOPL_TS_G(pdo_mysql_orig_methods) = dbh->methods;
    1036           16 :                 memcpy(&COLOPL_TS_G(hooked_mysql_driver_methods), dbh->methods, sizeof(struct pdo_dbh_methods));
    1037              : 
    1038           16 :                 COLOPL_TS_G(hooked_mysql_driver_methods).preparer = hook_pdo_driver_preparer;
    1039           16 :                 COLOPL_TS_G(hooked_mysql_driver_methods).doer = hook_pdo_driver_doer;
    1040              :         }
    1041              : 
    1042           16 :         dbh->methods = &COLOPL_TS_G(hooked_mysql_driver_methods);
    1043              : }
    1044              : #endif
    1045              : 
    1046           20 : static inline void date_common(INTERNAL_FUNCTION_PARAMETERS, int localtime)
    1047              : {
    1048              :         zend_string *format;
    1049              :         zend_long ts;
    1050           20 :         bool ts_is_null = true;
    1051              : 
    1052           20 :         ZEND_PARSE_PARAMETERS_START(1, 2)
    1053           20 :                 Z_PARAM_STR(format)
    1054           12 :                 Z_PARAM_OPTIONAL;
    1055           12 :                 Z_PARAM_LONG_OR_NULL(ts, ts_is_null)
    1056           20 :         ZEND_PARSE_PARAMETERS_END();
    1057              : 
    1058           12 :         if (ts_is_null) {
    1059            8 :                 ts = get_shifted_time();
    1060              :         }
    1061              : 
    1062           12 :         RETVAL_STR(php_format_date(ZSTR_VAL(format), ZSTR_LEN(format), ts, localtime));
    1063              : }
    1064              : 
    1065           16 : static void adjust_date_create_result(zval *datetime, zend_string *time_str)
    1066              : {
    1067              :         zval timezone;
    1068              :         int64_t timestamp_usec;
    1069              : 
    1070           16 :         if (Z_TYPE_P(datetime) != IS_OBJECT || !Z_PHPDATE_P(datetime)->time) {
    1071            8 :                 return;
    1072              :         }
    1073              : 
    1074           16 :         if (!time_string_depends_on_current(time_str)) {
    1075            8 :                 return;
    1076              :         }
    1077              : 
    1078            8 :         ZVAL_UNDEF(&timezone);
    1079            8 :         if (!get_date_timezone(datetime, &timezone)) {
    1080            0 :                 return;
    1081              :         }
    1082              : 
    1083            8 :         if (!resolve_time_string_to_timestamp_usec(time_str, &timezone, &timestamp_usec)) {
    1084            0 :                 zval_ptr_dtor(&timezone);
    1085              : 
    1086            0 :                 return;
    1087              :         }
    1088              : 
    1089            8 :         initialize_date_from_timestamp_usec(datetime, timestamp_usec, &timezone);
    1090            8 :         zval_ptr_dtor(&timezone);
    1091              : }
    1092              : 
    1093          220 : DEFINE_DT_HOOK_CONSTRUCTOR(dt);
    1094              : 
    1095         1216 : DEFINE_DT_HOOK_CONSTRUCTOR(dti);
    1096              : 
    1097          146 : static void ZEND_FASTCALL hook_time(INTERNAL_FUNCTION_PARAMETERS)
    1098              : {
    1099          146 :         CHECK_STATE(time);
    1100              : 
    1101          110 :         CALL_ORIGINAL_FUNCTION(time);
    1102          110 :         RETURN_LONG(get_shifted_time());
    1103              : }
    1104              : 
    1105            8 : static bool get_shifted_mktime_parts(bool localtime, datetime_parts_t *parts)
    1106              : {
    1107              :         zval timezone;
    1108              :         bool result;
    1109              : 
    1110            8 :         ZVAL_UNDEF(&timezone);
    1111            8 :         if (localtime) {
    1112            4 :                 if (!get_default_timezone(&timezone)) {
    1113            0 :                         return false;
    1114              :                 }
    1115            4 :         } else if (!get_utc_timezone(&timezone)) {
    1116            0 :                 return false;
    1117              :         }
    1118              : 
    1119            8 :         result = get_timestamp_parts(get_shifted_time_usec_for_timezone(&timezone), &timezone, parts);
    1120            8 :         zval_ptr_dtor(&timezone);
    1121              : 
    1122            8 :         return result;
    1123              : }
    1124              : 
    1125           16 : static void mktime_common(INTERNAL_FUNCTION_PARAMETERS, bool localtime)
    1126              : {
    1127              :         datetime_parts_t current;
    1128           16 :         zend_long hour = 0, minute = 0, second = 0, month = 0, day = 0, year = 0;
    1129              :         zval params[6];
    1130           16 :         bool hour_is_null = true, minute_is_null = true, second_is_null = true,
    1131           16 :                 month_is_null = true, day_is_null = true, year_is_null = true;
    1132              : 
    1133           16 :         if (localtime) {
    1134            8 :                 CALL_ORIGINAL_FUNCTION(mktime);
    1135              :         } else {
    1136            8 :                 CALL_ORIGINAL_FUNCTION(gmmktime);
    1137              :         }
    1138              : 
    1139           16 :         if (EG(exception)) {
    1140            8 :                 return;
    1141              :         }
    1142              : 
    1143           16 :         ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 1, 6)
    1144           16 :                 Z_PARAM_LONG_OR_NULL(hour, hour_is_null)
    1145           16 :                 Z_PARAM_OPTIONAL
    1146           16 :                 Z_PARAM_LONG_OR_NULL(minute, minute_is_null)
    1147            8 :                 Z_PARAM_LONG_OR_NULL(second, second_is_null)
    1148            8 :                 Z_PARAM_LONG_OR_NULL(month, month_is_null)
    1149            8 :                 Z_PARAM_LONG_OR_NULL(day, day_is_null)
    1150            8 :                 Z_PARAM_LONG_OR_NULL(year, year_is_null)
    1151           16 :         ZEND_PARSE_PARAMETERS_END();
    1152              : 
    1153           16 :         if (!hour_is_null && !minute_is_null && !second_is_null &&
    1154            8 :                 !month_is_null && !day_is_null && !year_is_null) {
    1155            8 :                 return;
    1156              :         }
    1157              : 
    1158            8 :         if (!get_shifted_mktime_parts(localtime, &current)) {
    1159            0 :                 return;
    1160              :         }
    1161              : 
    1162            8 :         ZVAL_LONG(&params[0], hour_is_null ? current.h : hour);
    1163            8 :         ZVAL_LONG(&params[1], minute_is_null ? current.i : minute);
    1164            8 :         ZVAL_LONG(&params[2], second_is_null ? current.s : second);
    1165            8 :         ZVAL_LONG(&params[3], month_is_null ? current.m : month);
    1166            8 :         ZVAL_LONG(&params[4], day_is_null ? current.d : day);
    1167            8 :         ZVAL_LONG(&params[5], year_is_null ? current.y : year);
    1168              : 
    1169            8 :         if (localtime) {
    1170            4 :                 CALL_ORIGINAL_FUNCTION_WITH_PARAMS(mktime, params, 6);
    1171              :         } else {
    1172            4 :                 CALL_ORIGINAL_FUNCTION_WITH_PARAMS(gmmktime, params, 6);
    1173              :         }
    1174              : }
    1175              : 
    1176          496 : static void ZEND_FASTCALL hook_mktime(INTERNAL_FUNCTION_PARAMETERS)
    1177              : {
    1178          496 :         CHECK_STATE(mktime);
    1179              : 
    1180            8 :         mktime_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
    1181              : }
    1182              : 
    1183          496 : static void ZEND_FASTCALL hook_gmmktime(INTERNAL_FUNCTION_PARAMETERS)
    1184              : {
    1185          496 :         CHECK_STATE(gmmktime);
    1186              : 
    1187            8 :         mktime_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
    1188              : }
    1189              : 
    1190           40 : static void ZEND_FASTCALL hook_date_create(INTERNAL_FUNCTION_PARAMETERS)
    1191              : {
    1192           40 :         CHECK_STATE(date_create);
    1193              : 
    1194            8 :         zend_string *time_str = NULL;
    1195            8 :         zval *timezone_object = NULL;
    1196              : 
    1197            8 :         CALL_ORIGINAL_FUNCTION(date_create);
    1198            8 :         if (EG(exception) || !return_value || Z_TYPE_P(return_value) == IS_FALSE) {
    1199            0 :                 return;
    1200              :         }
    1201              : 
    1202            8 :         ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 0, 2)
    1203            8 :                 Z_PARAM_OPTIONAL
    1204            8 :                 Z_PARAM_STR_OR_NULL(time_str)
    1205            4 :                 Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, php_date_get_timezone_ce())
    1206            8 :         ZEND_PARSE_PARAMETERS_END();
    1207              : 
    1208            8 :         adjust_date_create_result(return_value, time_str);
    1209              : }
    1210              : 
    1211           16 : static void ZEND_FASTCALL hook_date_create_immutable(INTERNAL_FUNCTION_PARAMETERS)
    1212              : {
    1213           16 :         CHECK_STATE(date_create_immutable);
    1214              : 
    1215            8 :         zend_string *time_str = NULL;
    1216            8 :         zval *timezone_object = NULL;
    1217              : 
    1218            8 :         CALL_ORIGINAL_FUNCTION(date_create_immutable);
    1219            8 :         if (EG(exception) || !return_value || Z_TYPE_P(return_value) == IS_FALSE) {
    1220            0 :                 return;
    1221              :         }
    1222              : 
    1223            8 :         ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 0, 2)
    1224            8 :                 Z_PARAM_OPTIONAL
    1225            8 :                 Z_PARAM_STR_OR_NULL(time_str)
    1226            4 :                 Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, php_date_get_timezone_ce())
    1227            8 :         ZEND_PARSE_PARAMETERS_END();
    1228              : 
    1229            8 :         adjust_date_create_result(return_value, time_str);
    1230              : }
    1231              : 
    1232          104 : DEFINE_CREATE_FROM_FORMAT(date_create_from_format);
    1233              : 
    1234           20 : DEFINE_CREATE_FROM_FORMAT(date_create_immutable_from_format);
    1235              : 
    1236           36 : DEFINE_CREATE_FROM_FORMAT_EX(dt_createfromformat, date_create_from_format);
    1237              : 
    1238           48 : DEFINE_CREATE_FROM_FORMAT_EX(dti_createfromformat, date_create_immutable_from_format);
    1239              : 
    1240           20 : static void ZEND_FASTCALL hook_date_parse_from_format(INTERNAL_FUNCTION_PARAMETERS)
    1241              : {
    1242           24 :         CHECK_STATE(date_parse_from_format);
    1243              : 
    1244              :         zend_string *format, *datetime;
    1245              : 
    1246           20 :         CALL_ORIGINAL_FUNCTION(date_parse_from_format);
    1247           20 :         if (EG(exception) || !return_value || Z_TYPE_P(return_value) != IS_ARRAY) {
    1248            4 :                 return;
    1249              :         }
    1250              : 
    1251           16 :         ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 2, 2)
    1252           16 :                 Z_PARAM_STR(format)
    1253           16 :                 Z_PARAM_STR(datetime)
    1254           16 :         ZEND_PARSE_PARAMETERS_END();
    1255              : 
    1256           16 :         adjust_date_parse_from_format_result(return_value, format, datetime);
    1257              : }
    1258              : 
    1259           20 : static void ZEND_FASTCALL hook_date(INTERNAL_FUNCTION_PARAMETERS)
    1260              : {
    1261           20 :         CHECK_STATE(date);
    1262              : 
    1263           12 :         date_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
    1264              : }
    1265              : 
    1266           12 : static void ZEND_FASTCALL hook_gmdate(INTERNAL_FUNCTION_PARAMETERS)
    1267              : {
    1268           12 :         CHECK_STATE(gmdate);
    1269              : 
    1270            8 :         date_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
    1271              : }
    1272              : 
    1273           16 : static void ZEND_FASTCALL hook_idate(INTERNAL_FUNCTION_PARAMETERS)
    1274              : {
    1275           16 :         CHECK_STATE(idate);
    1276              : 
    1277              :         zend_long ts;
    1278              :         zend_string *format;
    1279           12 :         bool ts_is_null = 1;
    1280              : 
    1281           12 :         ZEND_PARSE_PARAMETERS_START(1, 2)
    1282           12 :                 Z_PARAM_STR(format)
    1283           12 :                 Z_PARAM_OPTIONAL
    1284           12 :                 Z_PARAM_LONG_OR_NULL(ts, ts_is_null)
    1285           12 :         ZEND_PARSE_PARAMETERS_END();
    1286              : 
    1287           12 :         if (!ts_is_null || ZSTR_LEN(format) != 1) {
    1288            8 :                 CALL_ORIGINAL_FUNCTION(idate);
    1289            8 :                 return;
    1290              :         }
    1291              : 
    1292            4 :         if (ts_is_null) {
    1293            4 :                 ts = get_shifted_time();
    1294              :         }
    1295              : 
    1296            4 :         RETURN_LONG(php_idate(ZSTR_VAL(format)[0], ts, 0));
    1297              : }
    1298              : 
    1299           12 : static void ZEND_FASTCALL hook_getdate(INTERNAL_FUNCTION_PARAMETERS)
    1300              : {
    1301           16 :         CHECK_STATE(getdate);
    1302              : 
    1303              :         zend_long timestamp;
    1304              :         zval params[1];
    1305            8 :         bool timestamp_is_null = true;
    1306              : 
    1307            8 :         ZEND_PARSE_PARAMETERS_START(0, 1)
    1308            8 :                 Z_PARAM_OPTIONAL
    1309            8 :                 Z_PARAM_LONG_OR_NULL(timestamp, timestamp_is_null)
    1310            8 :         ZEND_PARSE_PARAMETERS_END();
    1311              : 
    1312            4 :         if (!timestamp_is_null) {
    1313            0 :                 CALL_ORIGINAL_FUNCTION(getdate);
    1314            0 :                 return;
    1315              :         }
    1316              : 
    1317              :         /* Call original function with timestamp params. */
    1318            4 :         ZVAL_LONG(&params[0], get_shifted_time());
    1319            4 :         CALL_ORIGINAL_FUNCTION_WITH_PARAMS(getdate, params, 1);
    1320              : }
    1321              : 
    1322           24 : static void ZEND_FASTCALL hook_localtime(INTERNAL_FUNCTION_PARAMETERS)
    1323              : {
    1324           28 :         CHECK_STATE(localtime);
    1325              : 
    1326              :         zend_long timestamp;
    1327              :         zval params[2];
    1328           16 :         bool timestamp_is_null = true, associative = false;
    1329              : 
    1330           16 :         ZEND_PARSE_PARAMETERS_START(0, 2)
    1331           16 :                 Z_PARAM_OPTIONAL;
    1332           16 :                 Z_PARAM_LONG_OR_NULL(timestamp, timestamp_is_null);
    1333            8 :                 Z_PARAM_BOOL(associative);
    1334           16 :         ZEND_PARSE_PARAMETERS_END();
    1335              : 
    1336           12 :         if (!timestamp_is_null) {
    1337            0 :                 CALL_ORIGINAL_FUNCTION(localtime);
    1338            0 :                 return;
    1339              :         }
    1340              : 
    1341              :         /* Call original function with params. */
    1342           12 :         ZVAL_LONG(&params[0], get_shifted_time());
    1343           12 :         ZVAL_BOOL(&params[1], associative);
    1344           12 :         CALL_ORIGINAL_FUNCTION_WITH_PARAMS(localtime, params, 2);
    1345              : }
    1346              : 
    1347          160 : static void ZEND_FASTCALL hook_strtotime(INTERNAL_FUNCTION_PARAMETERS)
    1348              : {
    1349          228 :         CHECK_STATE(strtotime);
    1350              : 
    1351              :         zend_long preset_ts;
    1352              :         zend_string *times;
    1353              :         zval params[2];
    1354          140 :         bool preset_ts_is_null = true;
    1355              : 
    1356          140 :         ZEND_PARSE_PARAMETERS_START(1, 2)
    1357          140 :                 Z_PARAM_STR(times);
    1358          136 :                 Z_PARAM_OPTIONAL;
    1359          136 :                 Z_PARAM_LONG_OR_NULL(preset_ts, preset_ts_is_null);
    1360          140 :         ZEND_PARSE_PARAMETERS_END();
    1361              : 
    1362          136 :         if (!preset_ts_is_null) {
    1363           64 :                 CALL_ORIGINAL_FUNCTION(strtotime);
    1364           64 :                 return;
    1365              :         }
    1366              : 
    1367              :         /* Call original function based on shifted time */
    1368           72 :         ZVAL_STR(&params[0], times);
    1369           72 :         ZVAL_LONG(&params[1], get_shifted_time());
    1370           72 :         CALL_ORIGINAL_FUNCTION_WITH_PARAMS(strtotime, params, 2);
    1371              : }
    1372              : 
    1373           16 : static void ZEND_FASTCALL hook_strftime(INTERNAL_FUNCTION_PARAMETERS)
    1374              : {
    1375           20 :         CHECK_STATE(strftime);
    1376              : 
    1377              :         zend_long timestamp;
    1378              :         zend_string *format;
    1379              :         zval params[2];
    1380            8 :         bool timestamp_is_null = true;
    1381              : 
    1382            8 :         ZEND_PARSE_PARAMETERS_START(1, 2)
    1383            8 :                 Z_PARAM_STR(format)
    1384            8 :                 Z_PARAM_OPTIONAL
    1385            8 :                 Z_PARAM_LONG_OR_NULL(timestamp, timestamp_is_null)
    1386            8 :         ZEND_PARSE_PARAMETERS_END();
    1387              : 
    1388            8 :         if (!timestamp_is_null) {
    1389            4 :                 CALL_ORIGINAL_FUNCTION(strftime);
    1390            4 :                 return;
    1391              :         }
    1392              : 
    1393            4 :         ZVAL_STR(&params[0], format);
    1394            4 :         ZVAL_LONG(&params[1], get_shifted_time());
    1395            4 :         CALL_ORIGINAL_FUNCTION_WITH_PARAMS(strftime, params, 2);
    1396              : }
    1397              : 
    1398           16 : static void ZEND_FASTCALL hook_gmstrftime(INTERNAL_FUNCTION_PARAMETERS)
    1399              : {
    1400           20 :         CHECK_STATE(gmstrftime);
    1401              : 
    1402              :         zend_long timestamp;
    1403              :         zend_string *format;
    1404              :         zval params[2];
    1405            8 :         bool timestamp_is_null = true;
    1406              : 
    1407            8 :         ZEND_PARSE_PARAMETERS_START(1, 2)
    1408            8 :                 Z_PARAM_STR(format)
    1409            8 :                 Z_PARAM_OPTIONAL
    1410            8 :                 Z_PARAM_LONG_OR_NULL(timestamp, timestamp_is_null)
    1411            8 :         ZEND_PARSE_PARAMETERS_END();
    1412              : 
    1413            8 :         if (!timestamp_is_null) {
    1414            4 :                 CALL_ORIGINAL_FUNCTION(gmstrftime);
    1415            4 :                 return;
    1416              :         }
    1417              : 
    1418            4 :         ZVAL_STR(&params[0], format);
    1419            4 :         ZVAL_LONG(&params[1], get_shifted_time());
    1420            4 :         CALL_ORIGINAL_FUNCTION_WITH_PARAMS(gmstrftime, params, 2);
    1421              : }
    1422              : 
    1423            8 : static void ZEND_FASTCALL hook_uniqid(INTERNAL_FUNCTION_PARAMETERS)
    1424              : {
    1425            8 :         CHECK_STATE(uniqid);
    1426              : 
    1427            8 :         zend_string *prefix = NULL, *original, *replacement;
    1428              :         zend_long sec, usec;
    1429              :         int64_t shifted_usec;
    1430            8 :         zval *more_entropy = NULL;
    1431              :         char id[32], *suffix_start;
    1432              :         size_t prefix_len, timestamp_len, suffix_len;
    1433              :         int fraction_width;
    1434              : 
    1435            8 :         CALL_ORIGINAL_FUNCTION(uniqid);
    1436            8 :         if (EG(exception) || !return_value || Z_TYPE_P(return_value) != IS_STRING) {
    1437            0 :                 return;
    1438              :         }
    1439              : 
    1440            8 :         ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 0, 2)
    1441            8 :                 Z_PARAM_OPTIONAL
    1442            8 :                 Z_PARAM_STR_OR_NULL(prefix)
    1443            4 :                 Z_PARAM_ZVAL(more_entropy)
    1444            8 :         ZEND_PARSE_PARAMETERS_END();
    1445              :         (void) more_entropy;
    1446              : 
    1447            8 :         original = Z_STR_P(return_value);
    1448            8 :         prefix_len = prefix ? ZSTR_LEN(prefix) : 0;
    1449            8 :         if (ZSTR_LEN(original) <= prefix_len + 8) {
    1450            0 :                 return;
    1451              :         }
    1452              : 
    1453            8 :         suffix_start = memchr(
    1454            8 :                 ZSTR_VAL(original) + prefix_len,
    1455              :                 '.',
    1456            8 :                 ZSTR_LEN(original) - prefix_len
    1457              :         );
    1458            8 :         timestamp_len = suffix_start
    1459            4 :                 ? (size_t) (suffix_start - ZSTR_VAL(original) - prefix_len)
    1460            8 :                 : ZSTR_LEN(original) - prefix_len
    1461              :         ;
    1462              : 
    1463            8 :         if (timestamp_len <= 8 || timestamp_len >= sizeof(id)) {
    1464            0 :                 return;
    1465              :         }
    1466              : 
    1467            8 :         shifted_usec = get_shifted_time_usec();
    1468            8 :         sec = sec_from_usec(shifted_usec);
    1469            8 :         usec = usec_remainder(shifted_usec);
    1470            8 :         fraction_width = (int) timestamp_len - 8;
    1471            8 :         slprintf(id, sizeof(id), "%08x%0*x", (unsigned int) sec, fraction_width, (unsigned int) usec);
    1472              : 
    1473            8 :         suffix_len = ZSTR_LEN(original) - prefix_len - timestamp_len;
    1474            8 :         replacement = zend_string_alloc(prefix_len + timestamp_len + suffix_len, 0);
    1475            8 :         if (prefix_len > 0) {
    1476            4 :                 memcpy(ZSTR_VAL(replacement), ZSTR_VAL(prefix), prefix_len);
    1477              :         }
    1478              : 
    1479            8 :         memcpy(ZSTR_VAL(replacement) + prefix_len, id, timestamp_len);
    1480              : 
    1481            8 :         if (suffix_len > 0) {
    1482            4 :                 memcpy(
    1483            4 :                         ZSTR_VAL(replacement) + prefix_len + timestamp_len,
    1484            4 :                         ZSTR_VAL(original) + prefix_len + timestamp_len,
    1485              :                         suffix_len
    1486              :                 );
    1487              :         }
    1488              : 
    1489            8 :         ZSTR_VAL(replacement)[prefix_len + timestamp_len + suffix_len] = '\0';
    1490              : 
    1491            8 :         zval_ptr_dtor(return_value);
    1492            8 :         ZVAL_STR(return_value, replacement);
    1493              : }
    1494              : 
    1495            8 : static bool get_shifted_local_year(zend_long *year)
    1496              : {
    1497              :         zval params[2], parts, *tm_year;
    1498            8 :         bool result = false;
    1499              : 
    1500            8 :         ZVAL_LONG(&params[0], get_shifted_time());
    1501            8 :         ZVAL_TRUE(&params[1]);
    1502            8 :         ZVAL_UNDEF(&parts);
    1503            8 :         CALL_ORIGINAL_FUNCTION_WITH_PARAMS_RET(localtime, &parts, params, 2);
    1504              : 
    1505           16 :         if (Z_TYPE(parts) == IS_ARRAY &&
    1506           16 :                 (tm_year = zend_hash_str_find(Z_ARRVAL(parts), "tm_year", strlen("tm_year"))) &&
    1507            8 :                 Z_TYPE_P(tm_year) == IS_LONG) {
    1508            8 :                 *year = Z_LVAL_P(tm_year) + 1900;
    1509            8 :                 result = true;
    1510              :         }
    1511              : 
    1512            8 :         if (!Z_ISUNDEF(parts)) {
    1513            8 :                 zval_ptr_dtor(&parts);
    1514              :         }
    1515              : 
    1516            8 :         return result;
    1517              : }
    1518              : 
    1519           24 : static void easter_common(INTERNAL_FUNCTION_PARAMETERS, bool easter_date)
    1520              : {
    1521           24 :         zend_long year = 0, mode = 0, shifted_year;
    1522              :         zval params[2];
    1523           24 :         bool year_is_null = true;
    1524              : 
    1525           24 :         if (easter_date) {
    1526           12 :                 CALL_ORIGINAL_FUNCTION(easter_date);
    1527              :         } else {
    1528           12 :                 CALL_ORIGINAL_FUNCTION(easter_days);
    1529              :         }
    1530              : 
    1531           24 :         if (EG(exception)) {
    1532           16 :                 return;
    1533              :         }
    1534              : 
    1535           24 :         ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 0, 2)
    1536           24 :                 Z_PARAM_OPTIONAL
    1537           24 :                 Z_PARAM_LONG_OR_NULL(year, year_is_null)
    1538           16 :                 Z_PARAM_LONG(mode)
    1539           24 :         ZEND_PARSE_PARAMETERS_END();
    1540              : 
    1541           24 :         if (!year_is_null) {
    1542           16 :                 return;
    1543              :         }
    1544              : 
    1545            8 :         if (!get_shifted_local_year(&shifted_year)) {
    1546            0 :                 return;
    1547              :         }
    1548              : 
    1549            8 :         ZVAL_LONG(&params[0], shifted_year);
    1550            8 :         ZVAL_LONG(&params[1], mode);
    1551            8 :         if (easter_date) {
    1552            4 :                 CALL_ORIGINAL_FUNCTION_WITH_PARAMS(easter_date, params, 2);
    1553              :         } else {
    1554            4 :                 CALL_ORIGINAL_FUNCTION_WITH_PARAMS(easter_days, params, 2);
    1555              :         }
    1556              : }
    1557              : 
    1558           16 : static void ZEND_FASTCALL hook_easter_date(INTERNAL_FUNCTION_PARAMETERS)
    1559              : {
    1560           16 :         CHECK_STATE(easter_date);
    1561              : 
    1562           12 :         easter_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
    1563              : }
    1564              : 
    1565           16 : static void ZEND_FASTCALL hook_easter_days(INTERNAL_FUNCTION_PARAMETERS)
    1566              : {
    1567           16 :         CHECK_STATE(easter_days);
    1568              : 
    1569           12 :         easter_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
    1570              : }
    1571              : 
    1572              : #if HAVE_GETTIMEOFDAY
    1573           36 : static inline void gettimeofday_common(INTERNAL_FUNCTION_PARAMETERS, int mode)
    1574              : {
    1575              :         zend_long sec, usec, offset, is_dst;
    1576              :         zend_string *timezone_info;
    1577              :         int64_t shifted_usec;
    1578           36 :         bool get_as_float = false;
    1579              : 
    1580           36 :         ZEND_PARSE_PARAMETERS_START(0, 1)
    1581           36 :                 Z_PARAM_OPTIONAL;
    1582           36 :                 Z_PARAM_BOOL(get_as_float);
    1583           36 :         ZEND_PARSE_PARAMETERS_END();
    1584              : 
    1585           28 :         shifted_usec = get_shifted_time_usec();
    1586           28 :         sec = sec_from_usec(shifted_usec);
    1587           28 :         usec = usec_remainder(shifted_usec);
    1588              : 
    1589           28 :         if (get_as_float) {
    1590           12 :                 RETVAL_DOUBLE((double) sec + ((double) usec / 1000000.00));
    1591              :         } else {
    1592           16 :                 if (mode) {
    1593            8 :                         offset = 0;
    1594            8 :                         is_dst = 0;
    1595              : 
    1596            8 :                         array_init(return_value);
    1597            8 :                         add_assoc_long(return_value, "sec", sec);
    1598            8 :                         add_assoc_long(return_value, "usec", usec);
    1599              : 
    1600            8 :                         timezone_info = php_format_date("Z I", strlen("Z I"), sec, 1);
    1601            8 :                         sscanf(ZSTR_VAL(timezone_info), ZEND_LONG_FMT " " ZEND_LONG_FMT, &offset, &is_dst);
    1602            8 :                         zend_string_release(timezone_info);
    1603              : 
    1604            8 :                         add_assoc_long(return_value, "minuteswest", -offset / 60);
    1605            8 :                         add_assoc_long(return_value, "dsttime", is_dst);
    1606              :                 } else {
    1607            8 :                         RETVAL_NEW_STR(zend_strpprintf(0, "%.8F %ld", usec / 1000000.00, (long) sec));
    1608              :                 }
    1609              :         }
    1610              : }
    1611              : 
    1612           55 : static void ZEND_FASTCALL hook_microtime(INTERNAL_FUNCTION_PARAMETERS)
    1613              : {
    1614           55 :         CHECK_STATE(microtime);
    1615              : 
    1616           20 :         gettimeofday_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
    1617              : }
    1618              : 
    1619           24 : static void ZEND_FASTCALL hook_gettimeofday(INTERNAL_FUNCTION_PARAMETERS)
    1620              : {
    1621           24 :         CHECK_STATE(gettimeofday);
    1622              : 
    1623           16 :         gettimeofday_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
    1624              : }
    1625              : #endif
    1626              : 
    1627          274 : bool register_hooks()
    1628              : {
    1629              :         /* \DateTime::__construct */
    1630          274 :         HOOK_CONSTRUCTOR(php_date_get_date_ce(), dt);
    1631              : 
    1632              :         /* \DateTimeImmutabel::__construct */
    1633          274 :         HOOK_CONSTRUCTOR(php_date_get_immutable_ce(), dti);
    1634              : 
    1635              :         /* \DateTime::createFromFormat */
    1636          274 :         HOOK_METHOD(php_date_get_date_ce(), dt, createfromformat);
    1637              : 
    1638              :         /* \DateTimeImmutable::createFromFormat */
    1639          274 :         HOOK_METHOD(php_date_get_immutable_ce(), dti, createfromformat);
    1640              : 
    1641          274 :         HOOK_FUNCTION(time);
    1642          274 :         HOOK_FUNCTION(mktime);
    1643          274 :         HOOK_FUNCTION(gmmktime);
    1644          274 :         HOOK_FUNCTION(date_create);
    1645          274 :         HOOK_FUNCTION(date_create_immutable);
    1646          274 :         HOOK_FUNCTION(date_create_from_format);
    1647          274 :         HOOK_FUNCTION(date_create_immutable_from_format);
    1648          274 :         HOOK_FUNCTION(date_parse_from_format);
    1649          274 :         HOOK_FUNCTION(date);
    1650          274 :         HOOK_FUNCTION(gmdate);
    1651          274 :         HOOK_FUNCTION(idate);
    1652          274 :         HOOK_FUNCTION(getdate);
    1653          274 :         HOOK_FUNCTION(localtime);
    1654          274 :         HOOK_FUNCTION(strtotime);
    1655          274 :         HOOK_FUNCTION_OPTIONAL(strftime);
    1656          274 :         HOOK_FUNCTION_OPTIONAL(gmstrftime);
    1657          274 :         HOOK_FUNCTION(uniqid);
    1658          274 :         HOOK_FUNCTION_OPTIONAL(easter_date);
    1659          274 :         HOOK_FUNCTION_OPTIONAL(easter_days);
    1660              : 
    1661              : #if HAVE_GETTIMEOFDAY
    1662          274 :         HOOK_FUNCTION(microtime);
    1663          274 :         HOOK_FUNCTION(gettimeofday);
    1664              : #endif
    1665              : 
    1666          274 :         return true;
    1667              : }
    1668              : 
    1669          270 : void register_pdo_hook()
    1670              : {
    1671              : #ifdef COLOPL_TIMESHIFTER_HAVE_PDO
    1672              :         zend_class_entry *pdo_ce;
    1673              : 
    1674              :         /* PDO may be missing at runtime (disabled build, or shared pdo.so not
    1675              :          * loaded), so resolve the class dynamically instead of linking against
    1676              :          * PDO symbols such as php_pdo_get_dbh_ce(). */
    1677          270 :         if (!zend_hash_str_exists(&module_registry, "pdo", sizeof("pdo") - 1)) {
    1678            0 :                 return;
    1679              :         }
    1680              : 
    1681          270 :         pdo_ce = zend_hash_str_find_ptr(CG(class_table), "pdo", sizeof("pdo") - 1);
    1682          270 :         if (!pdo_ce || pdo_ce->type != ZEND_INTERNAL_CLASS || !pdo_ce->constructor) {
    1683            0 :                 return;
    1684              :         }
    1685              : 
    1686              :         /* \PDO::__construct */
    1687          270 :         HOOK_CONSTRUCTOR(pdo_ce, pdo);
    1688              : #endif
    1689              : }
    1690              : 
    1691          274 : bool unregister_hooks()
    1692              : {
    1693              :         /* \DateTime::__construct */
    1694          274 :         RESTORE_CONSTRUCTOR(php_date_get_date_ce(), dt);
    1695              : 
    1696              :         /* \DateTimeImmutabel::__construct */
    1697          274 :         RESTORE_CONSTRUCTOR(php_date_get_immutable_ce(), dti);
    1698              : 
    1699              :         /* \DateTime::createFromFormat */
    1700          274 :         RESTORE_METHOD(php_date_get_date_ce(), dt, createfromformat);
    1701              : 
    1702              :         /* \DateTimeImmutable::createFromFormat */
    1703          274 :         RESTORE_METHOD(php_date_get_immutable_ce(), dti, createfromformat);
    1704              : 
    1705          274 :         RESTORE_FUNCTION(time);
    1706          274 :         RESTORE_FUNCTION(mktime);
    1707          274 :         RESTORE_FUNCTION(gmmktime);
    1708          274 :         RESTORE_FUNCTION(date_create);
    1709          274 :         RESTORE_FUNCTION(date_create_immutable);
    1710          274 :         RESTORE_FUNCTION(date_create_from_format);
    1711          274 :         RESTORE_FUNCTION(date_create_immutable_from_format);
    1712          274 :         RESTORE_FUNCTION(date_parse_from_format);
    1713          274 :         RESTORE_FUNCTION(date);
    1714          274 :         RESTORE_FUNCTION(gmdate);
    1715          274 :         RESTORE_FUNCTION(idate);
    1716          274 :         RESTORE_FUNCTION(getdate);
    1717          274 :         RESTORE_FUNCTION(localtime);
    1718          274 :         RESTORE_FUNCTION(strtotime);
    1719          274 :         RESTORE_FUNCTION_OPTIONAL(strftime);
    1720          274 :         RESTORE_FUNCTION_OPTIONAL(gmstrftime);
    1721          274 :         RESTORE_FUNCTION(uniqid);
    1722          274 :         RESTORE_FUNCTION_OPTIONAL(easter_date);
    1723          274 :         RESTORE_FUNCTION_OPTIONAL(easter_days);
    1724              : 
    1725              : #if HAVE_GETTIMEOFDAY
    1726          274 :         RESTORE_FUNCTION(microtime);
    1727          274 :         RESTORE_FUNCTION(gettimeofday);
    1728              : #endif
    1729              : 
    1730          274 :         return true;
    1731              : }
    1732              : 
    1733          236 : void apply_request_time_hook()
    1734              : {
    1735              :         zval *globals_server, *request_time, *request_time_float;
    1736              :         int64_t request_usec, shifted_request_usec;
    1737              :         double request_time_value;
    1738              : 
    1739          236 :         globals_server = zend_hash_str_find(&EG(symbol_table), "_SERVER", strlen("_SERVER"));
    1740              : 
    1741          236 :         if (!globals_server || Z_TYPE_P(globals_server) != IS_ARRAY) {
    1742              :                 /* $_SERVER not defined */
    1743            0 :                 return;
    1744              :         }
    1745              : 
    1746          236 :         request_time = zend_hash_str_find(Z_ARR_P(globals_server), "REQUEST_TIME", strlen("REQUEST_TIME"));
    1747          236 :         request_time_float = zend_hash_str_find(Z_ARR_P(globals_server), "REQUEST_TIME_FLOAT", strlen("REQUEST_TIME_FLOAT"));
    1748              : 
    1749              :         /* Get original request time at once */
    1750          236 :         if (COLOPL_TS_G(orig_request_time) == 0 && COLOPL_TS_G(orig_request_time_float) == 0) {
    1751          186 :                 if (request_time_float) {
    1752          186 :                         COLOPL_TS_G(orig_request_time_float) = Z_DVAL_P(request_time_float);
    1753            0 :                 } else if (request_time) {
    1754            0 :                         COLOPL_TS_G(orig_request_time) = Z_LVAL_P(request_time);
    1755              :                 } else {
    1756              :                         /* Missing REQUEST_TIME or REQUEST_TIME_FLOAT */
    1757            0 :                         return;
    1758              :                 }
    1759              :         }
    1760              : 
    1761          236 :         if (request_time) {
    1762          236 :                 if (COLOPL_TS_G(orig_request_time_float) != 0) {
    1763          236 :                         request_usec = (int64_t) (COLOPL_TS_G(orig_request_time_float) * 1000000.0);
    1764            0 :                 } else if (COLOPL_TS_G(orig_request_time) != 0) {
    1765            0 :                         request_usec = (int64_t) COLOPL_TS_G(orig_request_time) * 1000000;
    1766              :                 } else {
    1767            0 :                         return;
    1768              :                 }
    1769              : 
    1770          236 :                 if (!calculate_shifted_time_usec(request_usec, NULL, &shifted_request_usec)) {
    1771            0 :                         return;
    1772              :                 }
    1773              : 
    1774          236 :                 ZVAL_LONG(request_time, sec_from_usec(shifted_request_usec));
    1775              :         }
    1776              : 
    1777          236 :         if (request_time_float) {
    1778          236 :                 if (COLOPL_TS_G(orig_request_time_float) != 0) {
    1779          236 :                         request_time_value = COLOPL_TS_G(orig_request_time_float);
    1780            0 :                 } else if (COLOPL_TS_G(orig_request_time) != 0) {
    1781            0 :                         request_time_value = (double) COLOPL_TS_G(orig_request_time);
    1782              :                 } else {
    1783            0 :                         return;
    1784              :                 }
    1785              : 
    1786          236 :                 if (!calculate_shifted_time_usec((int64_t) (request_time_value * 1000000.0), NULL, &shifted_request_usec)) {
    1787            0 :                         return;
    1788              :                 }
    1789              : 
    1790          236 :                 ZVAL_DOUBLE(request_time_float, (double) shifted_request_usec / 1000000.0);
    1791              :         }
    1792              : }
        

Generated by: LCOV version 2.0-1