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