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