Skip to content

Commit e4b56b4

Browse files
committed
Refactors internal time retrieval and measurement
Introducing a more consistent and portable approach. The primary goals are to encapsulate platform-specific differences, improve time resolution, and prepare for long-term compatibility (e.g., Y2038 on WIN64).
1 parent 084e409 commit e4b56b4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+634
-904
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ PHP NEWS
1111
. Fix OSS-Fuzz #429429090 (Failed assertion on unset() with uninitialized
1212
container). (ilutov)
1313
. Fixed GH-20564 (Don't call autoloaders with pending exception). (ilutov)
14+
. Improved accuracy of microtime() and gettimeofday(), up-to 10ns. (marc-mabe)
1415

1516
- Date:
1617
. Update timelib to 2022.16. (Derick)

UPGRADING.INTERNALS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ PHP 8.6 INTERNALS UPGRADE NOTES
5656
node.
5757
. The zend_exception_save() and zend_exception_restore() functions were
5858
removed.
59+
. Introduced a new time-retrieval API zend_time_*. Use this API instead of
60+
time(), gettimeofday(), clock_gettime() and timespec_get().
5961

6062
========================
6163
2. Build system changes
@@ -71,6 +73,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES
7173
. Removed the XML_GetCurrentByteCount() libxml compatibility wrapper,
7274
as it was unused and could return the wrong result.
7375

76+
- ext/date
77+
. Deprecated php_time(), Use zend_time_real_get() instead.
78+
7479
- ext/mbstring:
7580
. Added GB18030-2022 to default encoding list for zh-CN.
7681

Zend/Optimizer/zend_func_infos.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -559,10 +559,8 @@ static const func_info_t func_infos[] = {
559559
F1("dechex", MAY_BE_STRING),
560560
F1("base_convert", MAY_BE_STRING),
561561
F1("number_format", MAY_BE_STRING),
562-
#if defined(HAVE_GETTIMEOFDAY)
563562
F1("microtime", MAY_BE_STRING|MAY_BE_DOUBLE),
564563
F1("gettimeofday", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_DOUBLE),
565-
#endif
566564
#if defined(HAVE_GETRUSAGE)
567565
F1("getrusage", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_FALSE),
568566
#endif
@@ -597,9 +595,7 @@ static const func_info_t func_infos[] = {
597595
F1("stream_resolve_include_path", MAY_BE_STRING|MAY_BE_FALSE),
598596
F1("stream_get_wrappers", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING),
599597
F1("stream_get_transports", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING),
600-
#if defined(HAVE_GETTIMEOFDAY)
601598
F1("uniqid", MAY_BE_STRING),
602-
#endif
603599
F1("parse_url", MAY_BE_LONG|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_NULL|MAY_BE_FALSE),
604600
F1("urlencode", MAY_BE_STRING),
605601
F1("urldecode", MAY_BE_STRING),

Zend/zend_time.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| Copyright (c) The PHP Group |
4+
+----------------------------------------------------------------------+
5+
| This source file is subject to version 3.01 of the PHP license, |
6+
| that is bundled with this package in the file LICENSE, and is |
7+
| available through the world-wide-web at the following url: |
8+
| https://www.php.net/license/3_01.txt |
9+
| If you did not receive a copy of the PHP license and are unable to |
10+
| obtain it through the world-wide-web, please send a note to |
11+
| license@php.net so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
| Author: Marc Bennewitz <marc@mabe.berlin> |
14+
+----------------------------------------------------------------------+
15+
*/
16+
17+
#include "zend_time.h"
18+
19+
/* Current real/wall-time in seconds */
20+
ZEND_API time_t zend_time_real_get(void) {
21+
return time(NULL);
22+
}
23+
24+
ZEND_API void zend_time_real_spec(struct timespec *ts) {
25+
#if defined(HAVE_CLOCK_GETTIME)
26+
27+
(void) clock_gettime(CLOCK_REALTIME, ts);
28+
29+
#elif defined(HAVE_TIMESPEC_GET)
30+
31+
(void) timespec_get(ts, TIME_UTC);
32+
33+
#elif defined(HAVE_GETTIMEOFDAY)
34+
35+
struct timeval tv;
36+
(void) gettimeofday(&tv, NULL);
37+
zend_time_val2spec(tv, ts);
38+
39+
#else
40+
41+
ts->tv_sec = zend_time_real_get();
42+
ts->tv_nsec = 0;
43+
44+
#endif
45+
}
46+
47+
ZEND_API uint64_t zend_time_mono_fallback(void) {
48+
#if ZEND_HRTIME_AVAILABLE
49+
return (uint64_t)zend_hrtime();
50+
#else
51+
struct timespec ts;
52+
zend_time_real_spec(&ts);
53+
return ((uint64_t) ts.tv_sec * ZEND_NANO_IN_SEC) + ts.tv_nsec;
54+
#endif
55+
}

Zend/zend_time.h

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| Copyright (c) The PHP Group |
4+
+----------------------------------------------------------------------+
5+
| This source file is subject to version 3.01 of the PHP license, |
6+
| that is bundled with this package in the file LICENSE, and is |
7+
| available through the world-wide-web at the following url: |
8+
| https://www.php.net/license/3_01.txt |
9+
| If you did not receive a copy of the PHP license and are unable to |
10+
| obtain it through the world-wide-web, please send a note to |
11+
| license@php.net so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
| Author: Marc Bennewitz <marc@mabe.berlin> |
14+
+----------------------------------------------------------------------+
15+
*/
16+
17+
#ifndef ZEND_TIME_H
18+
#define ZEND_TIME_H
19+
20+
#include "zend_portability.h"
21+
22+
#ifdef PHP_WIN32
23+
# include "win32/time.h"
24+
#endif
25+
#ifdef HAVE_SYS_TIME_H
26+
# include <sys/time.h>
27+
#endif
28+
#include <time.h>
29+
30+
#include "zend_hrtime.h"
31+
32+
#ifndef PHP_WIN32
33+
# define tv_sec_t time_t
34+
# define tv_usec_t suseconds_t
35+
#else
36+
# define tv_sec_t long
37+
# define tv_usec_t long
38+
#endif
39+
40+
#define ZEND_MILLI_IN_SEC 1000U
41+
#define ZEND_MICRO_IN_SEC 1000000U
42+
43+
BEGIN_EXTERN_C()
44+
45+
/* Assign seconds to timeval */
46+
static zend_always_inline void zend_time_sec2val(time_t s, struct timeval *tv) {
47+
tv->tv_sec = (tv_sec_t) s;
48+
tv->tv_usec = 0;
49+
}
50+
51+
/* Assign microseconds to timeval */
52+
static zend_always_inline void zend_time_usec2val(int64_t usec, struct timeval *tv) {
53+
tv->tv_sec = (tv_sec_t) (usec / ZEND_MICRO_IN_SEC);
54+
tv->tv_usec = (tv_usec_t) (usec % ZEND_MICRO_IN_SEC);
55+
56+
if (UNEXPECTED(tv->tv_usec < 0)) {
57+
tv->tv_usec += ZEND_MICRO_IN_SEC;
58+
tv->tv_sec -= 1;
59+
}
60+
}
61+
62+
/* Assign double (seconds) to timeval */
63+
static zend_always_inline void zend_time_dbl2val(double s, struct timeval *tv) {
64+
tv->tv_sec = (tv_sec_t) s;
65+
tv->tv_usec = (tv_usec_t) ((s - tv->tv_sec) * ZEND_MICRO_IN_SEC);
66+
67+
if (UNEXPECTED(tv->tv_usec < 0)) {
68+
tv->tv_usec += ZEND_MICRO_IN_SEC;
69+
tv->tv_sec -= 1;
70+
} else if (UNEXPECTED(tv->tv_usec >= ZEND_MICRO_IN_SEC)) {
71+
// rare, but protects against rounding up to exactly 1 second
72+
tv->tv_usec -= ZEND_MICRO_IN_SEC;
73+
tv->tv_sec += 1;
74+
}
75+
}
76+
77+
/* Assign timeval to timespec */
78+
static zend_always_inline void zend_time_val2spec(struct timeval tv, struct timespec *ts) {
79+
ts->tv_sec = (time_t) tv.tv_sec;
80+
ts->tv_nsec = (long) (tv.tv_usec * 1000);
81+
}
82+
83+
/* Current real/wall-time in seconds */
84+
ZEND_API time_t zend_time_real_get(void);
85+
86+
/* Current real/wall-time in up-to nano seconds */
87+
ZEND_API void zend_time_real_spec(struct timespec *ts);
88+
89+
/* Monotonic time in nanoseconds with a fallback to real/wall-time
90+
if no monotonic timer is available */
91+
ZEND_API uint64_t zend_time_mono_fallback(void);
92+
93+
END_EXTERN_C()
94+
95+
#endif // ZEND_TIME_H

Zend/zend_virtual_cwd.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
#include <errno.h>
2525
#include <stdlib.h>
2626
#include <fcntl.h>
27-
#include <time.h>
2827

2928
#include "zend.h"
3029
#include "zend_virtual_cwd.h"
30+
#include "zend_time.h"
3131

3232
#ifdef ZEND_WIN32
3333
#include <io.h>
@@ -577,7 +577,7 @@ static size_t tsrm_realpath_r(char *path, size_t start, size_t len, int *ll, tim
577577
if (start && save && CWDG(realpath_cache_size_limit)) {
578578
/* cache lookup for absolute path */
579579
if (!*t) {
580-
*t = time(0);
580+
*t = zend_time_real_get();
581581
}
582582
if ((bucket = realpath_cache_find(path, len, *t)) != NULL) {
583583
if (is_dir && !bucket->is_dir) {

configure.ac

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,7 @@ AC_CHECK_FUNCS(m4_normalize([
540540
asctime_r
541541
asprintf
542542
chroot
543+
clock_gettime
543544
ctime_r
544545
explicit_memset
545546
fdatasync
@@ -591,6 +592,7 @@ AC_CHECK_FUNCS(m4_normalize([
591592
strptime
592593
strtok_r
593594
symlink
595+
timespec_get
594596
tzset
595597
unsetenv
596598
usleep
@@ -1759,6 +1761,7 @@ PHP_ADD_SOURCES([Zend], m4_normalize([
17591761
zend_generators.c
17601762
zend_hash.c
17611763
zend_highlight.c
1764+
zend_time.c
17621765
zend_hrtime.c
17631766
zend_inheritance.c
17641767
zend_ini_parser.c

docs-old/streams.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ PHPAPI php_stream *php_stream_sock_open_from_socket(int socket, int persistent);
9090
/* Convert a socket into a stream. */
9191
9292
PHPAPI php_stream *php_stream_sock_open_host(const char *host, unsigned short port,
93-
int socktype, int timeout, int persistent);
93+
int socktype, struct timeval *timeout, const char *persistent_id);
9494
/* Open a connection to a host and return a stream. */
9595
9696
PHPAPI php_stream *php_stream_sock_open_unix(const char *path, int persistent,

ext/calendar/cal_unix.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
#include "php.h"
2020
#include "php_calendar.h"
2121
#include "sdncal.h"
22-
#include <time.h>
22+
#include "zend_time.h"
2323

2424
#define SECS_PER_DAY (24 * 3600)
2525

@@ -36,7 +36,7 @@ PHP_FUNCTION(unixtojd)
3636
}
3737

3838
if (tl_is_null) {
39-
ts = time(NULL);
39+
ts = zend_time_real_get();
4040
} else if (tl >= 0) {
4141
ts = (time_t) tl;
4242
} else {

ext/calendar/easter.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#include "php.h"
2121
#include "php_calendar.h"
2222
#include "sdncal.h"
23-
#include <time.h>
23+
#include "zend_time.h"
2424

2525
/**
2626
* If `gm` is true this will return the timestamp at midnight on Easter of the given year. If it is false this
@@ -43,9 +43,8 @@ static void _cal_easter(INTERNAL_FUNCTION_PARAMETERS, bool gm)
4343

4444
/* Default to the current year if year parameter is not given */
4545
if (year_is_null) {
46-
time_t a;
46+
time_t a = zend_time_real_get();
4747
struct tm b, *res;
48-
time(&a);
4948
res = php_localtime_r(&a, &b);
5049
if (!res) {
5150
year = 1900;

0 commit comments

Comments
 (0)