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, ×tamp_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(¶ms[0], times);
686 320 : ZVAL_LONG(¶ms[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(¶ms[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(¶ms[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(¶ms[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(¶ms[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(¶ms[0], format);
948 16 : ZVAL_STR(¶ms[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, ×tamp_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, ¤t)) {
1159 0 : return;
1160 : }
1161 :
1162 8 : ZVAL_LONG(¶ms[0], hour_is_null ? current.h : hour);
1163 8 : ZVAL_LONG(¶ms[1], minute_is_null ? current.i : minute);
1164 8 : ZVAL_LONG(¶ms[2], second_is_null ? current.s : second);
1165 8 : ZVAL_LONG(¶ms[3], month_is_null ? current.m : month);
1166 8 : ZVAL_LONG(¶ms[4], day_is_null ? current.d : day);
1167 8 : ZVAL_LONG(¶ms[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(¶ms[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(¶ms[0], get_shifted_time());
1343 12 : ZVAL_BOOL(¶ms[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(¶ms[0], times);
1369 72 : ZVAL_LONG(¶ms[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(¶ms[0], format);
1394 4 : ZVAL_LONG(¶ms[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(¶ms[0], format);
1419 4 : ZVAL_LONG(¶ms[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(¶ms[0], get_shifted_time());
1501 8 : ZVAL_TRUE(¶ms[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(¶ms[0], shifted_year);
1550 8 : ZVAL_LONG(¶ms[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 : }
|