Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ PHP NEWS
an error). (Derick)
. Fixed Unix timestamps in February of the year 0 are misparsed with
@-notation. (LukasGelbmann)
. Fixed bug GH-11368 (idate() doesn't work for the year -1). (Derick)
. Fixed bug GH-11310 (__debugInfo does nothing on userland classes extending
Date classes). (Derick)

- DBA:
. Fixed OOB read on malformed length field in dba flatfile handler. (alhudz)
Expand Down Expand Up @@ -83,11 +86,17 @@ PHP NEWS
and a proxy set). (CVE-2026-12184) (ndossche)

- Zip:
. Fixed bug GH-22649 (ZipArchive::setCommentName() and setCommentIndex()
could crash after overwriting an entry and resetting its inherited
unchanged comment). (Weilin Du)
. Fixed bug GH-21705 (ZipArchive::getFromIndex() ignores
ZipArchive::FL_UNCHANGED for deleted entries). (Weilin Du)
. Fixed bug GH-22176 (memory leak with ZipArchive::registerCancelBack()
is used with reference returning function during shutdown).
(David Carlier)
. ZipArchive::addGlob() and ZipArchive::addPattern() now raise a TypeError
for invalid "remove_all_path", "comp_method", "comp_flags", and
"enc_method" options instead of emitting a warning. (David Carlier)

02 Jul 2026, PHP 8.6.0alpha1

Expand Down
4 changes: 4 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES
3. Module changes
========================

- ext/date:
. php_idate() now returns the result state, and moves the return value into an
out parameter.

- ext/mbstring:
. Added GB18030-2022 to default encoding list for zh-CN.

Expand Down
76 changes: 42 additions & 34 deletions ext/curl/interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -2030,42 +2030,42 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
HashTable *ph;
zend_string *val, *tmp_val;
struct curl_slist *slist = NULL;
const char *name = NULL;

if (Z_TYPE_P(zvalue) != IS_ARRAY) {
const char *name = NULL;
switch (option) {
case CURLOPT_HTTPHEADER:
name = "CURLOPT_HTTPHEADER";
break;
case CURLOPT_QUOTE:
name = "CURLOPT_QUOTE";
break;
case CURLOPT_HTTP200ALIASES:
name = "CURLOPT_HTTP200ALIASES";
break;
case CURLOPT_POSTQUOTE:
name = "CURLOPT_POSTQUOTE";
break;
case CURLOPT_PREQUOTE:
name = "CURLOPT_PREQUOTE";
break;
case CURLOPT_TELNETOPTIONS:
name = "CURLOPT_TELNETOPTIONS";
break;
case CURLOPT_MAIL_RCPT:
name = "CURLOPT_MAIL_RCPT";
break;
case CURLOPT_RESOLVE:
name = "CURLOPT_RESOLVE";
break;
case CURLOPT_PROXYHEADER:
name = "CURLOPT_PROXYHEADER";
break;
case CURLOPT_CONNECT_TO:
name = "CURLOPT_CONNECT_TO";
break;
}
switch (option) {
case CURLOPT_HTTPHEADER:
name = "CURLOPT_HTTPHEADER";
break;
case CURLOPT_QUOTE:
name = "CURLOPT_QUOTE";
break;
case CURLOPT_HTTP200ALIASES:
name = "CURLOPT_HTTP200ALIASES";
break;
case CURLOPT_POSTQUOTE:
name = "CURLOPT_POSTQUOTE";
break;
case CURLOPT_PREQUOTE:
name = "CURLOPT_PREQUOTE";
break;
case CURLOPT_TELNETOPTIONS:
name = "CURLOPT_TELNETOPTIONS";
break;
case CURLOPT_MAIL_RCPT:
name = "CURLOPT_MAIL_RCPT";
break;
case CURLOPT_RESOLVE:
name = "CURLOPT_RESOLVE";
break;
case CURLOPT_PROXYHEADER:
name = "CURLOPT_PROXYHEADER";
break;
case CURLOPT_CONNECT_TO:
name = "CURLOPT_CONNECT_TO";
break;
}

if (Z_TYPE_P(zvalue) != IS_ARRAY) {
zend_type_error("%s(): The %s option must have an array value", get_active_function_name(), name);
return FAILURE;
}
Expand All @@ -2074,6 +2074,14 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
ZEND_HASH_FOREACH_VAL(ph, current) {
ZVAL_DEREF(current);
val = zval_get_tmp_string(current, &tmp_val);

if (zend_str_has_nul_byte(val)) {
curl_slist_free_all(slist);
zend_tmp_string_release(tmp_val);
zend_value_error("%s(): cURL option %s must not contain any null bytes", get_active_function_name(), name);
return FAILURE;
}

struct curl_slist *new_slist = curl_slist_append(slist, ZSTR_VAL(val));
zend_tmp_string_release(tmp_val);
if (!new_slist) {
Expand Down
52 changes: 52 additions & 0 deletions ext/curl/tests/curl_setopt_error_nul_byte.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
--TEST--
curl_setopt() throws ValueError for NUL bytes in lists
--EXTENSIONS--
curl
--FILE--
<?php

$ch = curl_init();

$list_options = [
"CURLOPT_HTTP200ALIASES",
"CURLOPT_HTTPHEADER",
"CURLOPT_POSTQUOTE",
"CURLOPT_PREQUOTE",
"CURLOPT_QUOTE",
"CURLOPT_TELNETOPTIONS",
"CURLOPT_MAIL_RCPT",
"CURLOPT_RESOLVE",
"CURLOPT_PROXYHEADER",
"CURLOPT_CONNECT_TO",
];

foreach ($list_options as $option) {
try {
curl_setopt($ch, constant($option), ["Something: foo\0bar"]);
} catch (ValueError $exception) {
echo $option . ": " . $exception->getMessage() . "\n\n";
}
}

$ch = null;
?>
--EXPECT--
CURLOPT_HTTP200ALIASES: curl_setopt(): cURL option CURLOPT_HTTP200ALIASES must not contain any null bytes

CURLOPT_HTTPHEADER: curl_setopt(): cURL option CURLOPT_HTTPHEADER must not contain any null bytes

CURLOPT_POSTQUOTE: curl_setopt(): cURL option CURLOPT_POSTQUOTE must not contain any null bytes

CURLOPT_PREQUOTE: curl_setopt(): cURL option CURLOPT_PREQUOTE must not contain any null bytes

CURLOPT_QUOTE: curl_setopt(): cURL option CURLOPT_QUOTE must not contain any null bytes

CURLOPT_TELNETOPTIONS: curl_setopt(): cURL option CURLOPT_TELNETOPTIONS must not contain any null bytes

CURLOPT_MAIL_RCPT: curl_setopt(): cURL option CURLOPT_MAIL_RCPT must not contain any null bytes

CURLOPT_RESOLVE: curl_setopt(): cURL option CURLOPT_RESOLVE must not contain any null bytes

CURLOPT_PROXYHEADER: curl_setopt(): cURL option CURLOPT_PROXYHEADER must not contain any null bytes

CURLOPT_CONNECT_TO: curl_setopt(): cURL option CURLOPT_CONNECT_TO must not contain any null bytes
83 changes: 53 additions & 30 deletions ext/date/php_date.c
Original file line number Diff line number Diff line change
Expand Up @@ -901,13 +901,13 @@ PHPAPI zend_string *php_format_date(const char *format, size_t format_len, time_
/* }}} */

/* {{{ php_idate */
PHPAPI int php_idate(char format, time_t ts, bool localtime)
PHPAPI bool php_idate(char format, time_t ts, bool localtime, int *result)
{
timelib_time *t;
timelib_tzinfo *tzi;
int retval = -1;
timelib_time_offset *offset = NULL;
timelib_sll isoweek, isoyear;
bool success = true;

t = timelib_time_ctor();

Expand Down Expand Up @@ -947,54 +947,57 @@ PHPAPI int php_idate(char format, time_t ts, bool localtime)

switch (format) {
/* day */
case 'd': case 'j': retval = (int) t->d; break;
case 'd': case 'j': *result = (int) t->d; break;

case 'N': retval = (int) timelib_iso_day_of_week(t->y, t->m, t->d); break;
case 'w': retval = (int) timelib_day_of_week(t->y, t->m, t->d); break;
case 'z': retval = (int) timelib_day_of_year(t->y, t->m, t->d); break;
case 'N': *result = (int) timelib_iso_day_of_week(t->y, t->m, t->d); break;
case 'w': *result = (int) timelib_day_of_week(t->y, t->m, t->d); break;
case 'z': *result = (int) timelib_day_of_year(t->y, t->m, t->d); break;

/* week */
case 'W': retval = (int) isoweek; break; /* iso weeknr */
case 'W': *result = (int) isoweek; break; /* iso weeknr */

/* month */
case 'm': case 'n': retval = (int) t->m; break;
case 't': retval = (int) timelib_days_in_month(t->y, t->m); break;
case 'm': case 'n': *result = (int) t->m; break;
case 't': *result = (int) timelib_days_in_month(t->y, t->m); break;

/* year */
case 'L': retval = (int) timelib_is_leap((int) t->y); break;
case 'y': retval = (int) (t->y % 100); break;
case 'Y': retval = (int) t->y; break;
case 'o': retval = (int) isoyear; break; /* iso year */
case 'L': *result = (int) timelib_is_leap((int) t->y); break;
case 'y': *result = (int) (t->y % 100); break;
case 'Y': *result = (int) t->y; break;
case 'o': *result = (int) isoyear; break; /* iso year */

/* Swatch Beat a.k.a. Internet Time */
case 'B':
retval = ((((long)t->sse)-(((long)t->sse) - ((((long)t->sse) % 86400) + 3600))) * 10);
if (retval < 0) {
retval += 864000;
*result = ((((long)t->sse)-(((long)t->sse) - ((((long)t->sse) % 86400) + 3600))) * 10);
if (*result < 0) {
*result += 864000;
}
/* Make sure to do this on a positive int to avoid rounding errors */
retval = (retval / 864) % 1000;
*result = (*result / 864) % 1000;
break;

/* time */
case 'g': case 'h': retval = (int) ((t->h % 12) ? (int) t->h % 12 : 12); break;
case 'H': case 'G': retval = (int) t->h; break;
case 'i': retval = (int) t->i; break;
case 's': retval = (int) t->s; break;
case 'g': case 'h': *result = (int) ((t->h % 12) ? (int) t->h % 12 : 12); break;
case 'H': case 'G': *result = (int) t->h; break;
case 'i': *result = (int) t->i; break;
case 's': *result = (int) t->s; break;

/* timezone */
case 'I': retval = (int) (!localtime ? offset->is_dst : 0); break;
case 'Z': retval = (int) (!localtime ? offset->offset : 0); break;
case 'I': *result = (int) (!localtime ? offset->is_dst : 0); break;
case 'Z': *result = (int) (!localtime ? offset->offset : 0); break;

case 'U': retval = (int) t->sse; break;
case 'U': *result = (int) t->sse; break;

default:
success = false;
}

if (!localtime) {
timelib_time_offset_dtor(offset);
}
timelib_time_dtor(t);

return retval;
return success;
}
/* }}} */

Expand All @@ -1018,7 +1021,7 @@ PHP_FUNCTION(idate)
zend_string *format;
zend_long ts;
bool ts_is_null = 1;
int ret;
int ret = 0;

ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_STR(format)
Expand All @@ -1035,8 +1038,8 @@ PHP_FUNCTION(idate)
ts = php_time();
}

ret = php_idate(ZSTR_VAL(format)[0], ts, 0);
if (ret == -1) {
bool ok = php_idate(ZSTR_VAL(format)[0], ts, 0, &ret);
if (!ok) {
php_error_docref(NULL, E_WARNING, "Unrecognized date format token");
RETURN_FALSE;
}
Expand Down Expand Up @@ -1955,12 +1958,22 @@ static HashTable *date_object_get_properties_for(zend_object *object, zend_prop_
php_date_obj *dateobj;

switch (purpose) {
case ZEND_PROP_PURPOSE_DEBUG:
case ZEND_PROP_PURPOSE_SERIALIZE:
case ZEND_PROP_PURPOSE_VAR_EXPORT:
case ZEND_PROP_PURPOSE_JSON:
case ZEND_PROP_PURPOSE_ARRAY_CAST:
break;
case ZEND_PROP_PURPOSE_DEBUG: {
if (object->ce->__debugInfo) {
int is_temp = 0;
HashTable *ht = zend_std_get_debug_info(object, &is_temp);
if (ht && !is_temp) {
GC_TRY_ADDREF(ht);
}
return ht;
}
break;
}
default:
return zend_std_get_properties_for(object, purpose);
}
Expand Down Expand Up @@ -2077,12 +2090,22 @@ static HashTable *date_object_get_properties_for_timezone(zend_object *object, z
php_timezone_obj *tzobj;

switch (purpose) {
case ZEND_PROP_PURPOSE_DEBUG:
case ZEND_PROP_PURPOSE_SERIALIZE:
case ZEND_PROP_PURPOSE_VAR_EXPORT:
case ZEND_PROP_PURPOSE_JSON:
case ZEND_PROP_PURPOSE_ARRAY_CAST:
break;
case ZEND_PROP_PURPOSE_DEBUG: {
if (object->ce->__debugInfo) {
int is_temp = 0;
HashTable *ht = zend_std_get_debug_info(object, &is_temp);
if (ht && !is_temp) {
GC_TRY_ADDREF(ht);
}
return ht;
}
break;
}
default:
return zend_std_get_properties_for(object, purpose);
}
Expand Down
2 changes: 1 addition & 1 deletion ext/date/php_date.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ PHPAPI time_t php_time(void);
/* Backwards compatibility wrapper */
PHPAPI zend_long php_parse_date(const char *string, zend_long *now);
PHPAPI void php_mktime(INTERNAL_FUNCTION_PARAMETERS, bool gmt);
PHPAPI int php_idate(char format, time_t ts, bool localtime);
PHPAPI bool php_idate(char format, time_t ts, bool localtime, int *result);

#define _php_strftime php_strftime

Expand Down
37 changes: 37 additions & 0 deletions ext/date/tests/bug-gh11310-1.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
Bug GH-11310: __debugInfo does nothing on userland classes extending Date classes
--FILE--
<?php
class UDateTime extends DateTime { public function __debugInfo(): array { return ['value' => 'zzz']; } }
class UDateTimeImmutable extends DateTimeImmutable { public function __debugInfo(): array { return ['value' => 'zzz']; } }
class UDateTimeZone extends DateTimeZone { public function __debugInfo(): array { return ['value' => 'zzz']; } }
class UDateInterval extends DateInterval { public function __debugInfo(): array { return ['value' => 'zzz']; } }
class UDatePeriod extends DatePeriod { public function __debugInfo(): array { return ['value' => 'zzz']; } }

$d = new UDateTime(); var_dump($d);
$d = new UDateTimeImmutable(); var_dump($d);
$d = new UDateTimeZone("Europe/Kyiv"); var_dump($d);
$d = new UDateInterval("P3D"); var_dump($d);
$d = UDatePeriod::createFromISO8601String("2026-07-09T17:23:06Z/P3D/R5"); var_dump($d);
?>
--EXPECTF--
object(UDateTime)#%d (1) {
["value"]=>
string(3) "zzz"
}
object(UDateTimeImmutable)#%d (1) {
["value"]=>
string(3) "zzz"
}
object(UDateTimeZone)#%d (1) {
["value"]=>
string(3) "zzz"
}
object(UDateInterval)#%d (1) {
["value"]=>
string(3) "zzz"
}
object(UDatePeriod)#%d (1) {
["value"]=>
string(3) "zzz"
}
Loading