Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e7eebb9
sapi/phpdbg: remove dead str_type assignments before goto (#22600)
jorgsowa Jul 8, 2026
0b96441
ext/iconv/tests/bug76249.phpt: fallback for non-GNU iconv() (#22552)
orlitzky Jul 8, 2026
215ddb7
ext/iconv/tests/bug52211.phpt: use per-iconv charset names (#22543)
orlitzky Jul 8, 2026
8328868
Merge branch 'PHP-8.4' into PHP-8.5
Girgias Jul 8, 2026
476da42
Merge branch 'PHP-8.5'
Girgias Jul 8, 2026
4daefbf
bz2: use C23 enum and pack filter data struct (#22641)
Girgias Jul 8, 2026
e790541
[skip ci] Add various missing UPGRADING entries for 8.6 BC breaks
LamentXU123 Jul 8, 2026
9b1cdc5
ext/standard: Add test for headers_sent() by ref args
Girgias Sep 28, 2024
66966e6
ext/standard/head.c: no need to rely on ZEND_NUM_ARGS()
Girgias Sep 28, 2024
6f1beed
[skip ci] Fix CXXFLAGS for type verification build
iluuu1994 Jul 8, 2026
5df5863
Merge branch 'PHP-8.2' into PHP-8.3
iluuu1994 Jul 8, 2026
db8cabd
Merge branch 'PHP-8.3' into PHP-8.4
iluuu1994 Jul 8, 2026
c3966f7
Merge branch 'PHP-8.4' into PHP-8.5
iluuu1994 Jul 8, 2026
39675b6
Merge branch 'PHP-8.5'
iluuu1994 Jul 8, 2026
ebbfb3c
ext/iconv/tests/eucjp2iso2022jp.phpt: skip for "unknown" iconv
orlitzky Nov 17, 2024
863263a
ext/iconv/tests/iconv_mime_encode.phpt: skip for "unknown" iconv
orlitzky Nov 17, 2024
a8a2eb6
Merge branch 'PHP-8.4' into PHP-8.5
Girgias Jul 8, 2026
d4f4f4f
Merge branch 'PHP-8.5'
Girgias Jul 8, 2026
8d1d4e3
Zend: Add new zend APIs for `bin2hex` to remove `ext/hash` usage in v…
LamentXU123 Jul 8, 2026
d0ce868
iconv: fix test with duplicate SKIPIF from merge up
Girgias Jul 8, 2026
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
4 changes: 2 additions & 2 deletions .github/workflows/test-suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,10 @@ jobs:
with:
configurationParameters: >-
${{ matrix.asan && 'CFLAGS="-fsanitize=undefined,address -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address"' || '' }}
${{ matrix.variation && 'CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1 -DZEND_VERIFY_TYPE_INFERENCE" CXXFLAGS="-DZEND_VERIFY_TYPE_INFERENCE"' || '' }}
${{ matrix.variation && 'CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1 -DZEND_VERIFY_TYPE_INFERENCE"' || '' }}
${{ (matrix.variation && fromJson(inputs.branch).jobs.LINUX_X64.config.variation_enable_zend_max_execution_timers) && '--enable-zend-max-execution-timers' || '' }}
--${{ matrix.debug && 'enable' || 'disable' }}-debug
${{ matrix.debug && 'CXXFLAGS="-D_GLIBCXX_ASSERTIONS"' || '' }}
${{ (matrix.variation || matrix.debug) && format('CXXFLAGS="{0} {1}"', matrix.variation && '-DZEND_VERIFY_TYPE_INFERENCE' || '', matrix.debug && '-D_GLIBCXX_ASSERTIONS' || '') || '' }}
--${{ matrix.zts && 'enable' || 'disable' }}-zts
asan: ${{ matrix.asan && 'true' || 'false' }}
skipSlow: ${{ (matrix.asan && !inputs.all_variations) && 'true' || 'false' }}
Expand Down
10 changes: 10 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ PHP 8.6 UPGRADE NOTES
from global scope" instead of the prior readonly modification error.
ReflectionProperty::isWritable() also reports these properties
accurately.
. Array access on Dom\DtdNamedNodeMap objects now returns null for negative
integer indexes instead of returning the first node.
. Array access on Dom\DtdNamedNodeMap objects now raises a ValueError when
the integer index is greater than INT_MAX instead of overflowing to a
smaller index.

- GD:
. imagesetstyle(), imagefilter() and imagecrop() filter their array arguments
Expand Down Expand Up @@ -188,6 +193,11 @@ PHP 8.6 UPGRADE NOTES
value is passed.
. number_format() now raises a ValueError when $decimals is outside the
integer range instead of silently clamping very large positive values.
. sleep() now raises a ValueError when $seconds is greater than the platform
limit (UINT_MAX seconds, or UINT_MAX / 1000 seconds on Windows) instead of
allowing the value to overflow.
. usleep() now raises a ValueError when $microseconds is greater than
UINT_MAX instead of allowing the value to overflow.
. proc_open() now raises a ValueError when the $cwd argument contains NUL
bytes.

Expand Down
2 changes: 2 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ PHP 8.6 INTERNALS UPGRADE NOTES
. Added Z_PARAM_ENUM().
. Added zend_enum_fetch_case_id().
. Added zend_enum_get_case_by_id().
. Added zend_bin2hex() and zend_bin2hex_str() as helper functions to remove
dependencies on /ext/hash in various extensions.
. ZEND_INI_GET_ADDR() is now a void* pointer instead of a char* pointer. This
more correctly represents the generic nature of the returned pointer and
allows to remove explicit casts, but possibly breaks pointer arithmetic
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/weakrefs/gh17442_1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ headers_sent($obj,$generator);
Fatal error: Uncaught Exception: Test in %s:%d
Stack trace:
#0 [internal function]: class@anonymous->__destruct()
#1 %s(%d): headers_sent(NULL, 0)
#1 %s(%d): headers_sent(NULL, NULL)
#2 {main}
thrown in %s on line %d
26 changes: 26 additions & 0 deletions Zend/zend_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ ZEND_API zend_string *zend_empty_string = NULL;
ZEND_API zend_string *zend_one_char_string[256];
ZEND_API zend_string **zend_known_strings = NULL;

/* this is read-only, so it's ok */
ZEND_SET_ALIGNED(16, static const char zend_hexconvtab[]) = "0123456789abcdef";

static zend_always_inline void zend_bin2hex_impl(char *out, const unsigned char *in, size_t in_len, const char *hexconvtab)
{
for (size_t i = 0; i < in_len; i++) {
out[i * 2] = hexconvtab[in[i] >> 4];
out[i * 2 + 1] = hexconvtab[in[i] & 0x0f];
}
}

ZEND_API zend_ulong ZEND_FASTCALL zend_string_hash_func(zend_string *str)
{
return ZSTR_H(str) = zend_hash_func(ZSTR_VAL(str), ZSTR_LEN(str));
Expand All @@ -62,6 +73,21 @@ ZEND_API zend_ulong ZEND_FASTCALL zend_hash_func(const char *str, size_t len)
return zend_inline_hash_func(str, len);
}

ZEND_API void ZEND_FASTCALL zend_bin2hex(char *out, const unsigned char *in, size_t in_len)
{
zend_bin2hex_impl(out, in, in_len, zend_hexconvtab);
}

ZEND_API zend_string *zend_bin2hex_str(const unsigned char *in, size_t in_len)
{
zend_string *result = zend_string_safe_alloc(in_len, 2 * sizeof(char), 0, 0);

zend_bin2hex(ZSTR_VAL(result), in, in_len);
ZSTR_VAL(result)[in_len * 2] = '\0';

return result;
}

static void _str_dtor(zval *zv)
{
zend_string *str = Z_STR_P(zv);
Expand Down
2 changes: 2 additions & 0 deletions Zend/zend_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ ZEND_API extern zend_string_init_existing_interned_func_t zend_string_init_exist
ZEND_API zend_ulong ZEND_FASTCALL zend_string_hash_func(zend_string *str);
ZEND_API zend_ulong ZEND_FASTCALL zend_hash_func(const char *str, size_t len);
ZEND_API zend_string* ZEND_FASTCALL zend_interned_string_find_permanent(zend_string *str);
ZEND_API void ZEND_FASTCALL zend_bin2hex(char *out, const unsigned char *in, size_t in_len);
ZEND_API zend_string *zend_bin2hex_str(const unsigned char *in, size_t in_len);

ZEND_API zend_string *zend_string_concat2(
const char *str1, size_t str1_len,
Expand Down
13 changes: 6 additions & 7 deletions ext/bz2/bz2_filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

/* {{{ data structure */

enum strm_status {
C23_ENUM(strm_status, uint8_t) {
PHP_BZ2_UNINITIALIZED,
PHP_BZ2_RUNNING,
PHP_BZ2_FINISHED
Expand All @@ -34,12 +34,11 @@ typedef struct _php_bz2_filter_data {
size_t inbuf_len;
size_t outbuf_len;

enum strm_status status; /* Decompress option */
unsigned int small_footprint : 1; /* Decompress option */
unsigned int expect_concatenated : 1; /* Decompress option */
unsigned int is_flushed : 1; /* only for compression */

int persistent;
bool persistent;
bool expect_concatenated : 1; /* Decompress option */
bool small_footprint : 1; /* Decompress option */
bool is_flushed : 1; /* only for compression */
strm_status status; /* Decompress option */

/* Configuration for reset - immutable */
int blockSize100k; /* compress only */
Expand Down
16 changes: 15 additions & 1 deletion ext/iconv/tests/bug52211.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,22 @@ if (PHP_OS_FAMILY === 'Solaris') {
--FILE--
<?php

// According to POSIX 2024, the to/from charset names are
// implementation-defined. To keep this test true to its original
// purpose, we retain the charsets used in bug 52211, but only when
// the implementation is known to support them. Otherwise we default
// both to ASCII, which should be supported everywhere (in particular
// on musl) yet still considers the input invalid.
$from_charset = "ASCII";
$to_charset = "ASCII";

if (ICONV_IMPL == "libiconv" || ICONV_IMPL == "glibc") {
$from_charset = "CP850";
$to_charset = "ISO-8859-1";
}

$str = "PATHOLOGIES MÉDICO-CHIRUR. ADUL. PL";
$str_iconv = iconv('CP850', 'ISO-8859-1', $str );
$str_iconv = iconv($from_charset, $to_charset, $str );
var_dump($str_iconv);

?>
Expand Down
21 changes: 16 additions & 5 deletions ext/iconv/tests/bug76249.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,27 @@ Bug #76249 (stream filter convert.iconv leads to infinite loop on invalid sequen
iconv
--FILE--
<?php
$ignore = "";
if (ICONV_IMPL == "libiconv" || ICONV_IMPL == "glibc") {
// The original bug report uses "//IGNORE", and the bug itself
// involves the return value and errno from iconv(), so in the
// interest of fidelity we include the suffix on systems like the
// one where the bug was reported. On other systems however, the
// "//IGNORE" suffix may not be supported, and this is allowed by
// POSIX (musl in particular does not support it).
$ignore = "//IGNORE";
}

$fh = fopen('php://memory', 'rw');
fwrite($fh, "abc");
rewind($fh);
if (false === @stream_filter_append($fh, 'convert.iconv.ucs-2/utf8//IGNORE', STREAM_FILTER_READ, [])) {
stream_filter_append($fh, 'convert.iconv.ucs-2/utf-8//IGNORE', STREAM_FILTER_READ, []);
if (false === @stream_filter_append($fh, "convert.iconv.ucs-2/utf8{$ignore}", STREAM_FILTER_READ, [])) {
stream_filter_append($fh, "convert.iconv.ucs-2/utf-8{$ignore}", STREAM_FILTER_READ, []);
}
var_dump(stream_get_contents($fh));
?>
DONE
--EXPECTF--
Warning: stream_get_contents(): iconv stream filter ("ucs-2"=>"utf%A8//IGNORE"): invalid multibyte sequence in %sbug76249.php on line %d
string(0) ""
--EXPECTREGEX--
Warning: stream_get_contents\(\): iconv stream filter \("ucs-2"=>"utf-?8(\/\/IGNORE)?"\): invalid multibyte sequence in .*bug76249\.php on line \d+
string\(0\) ""
DONE
11 changes: 9 additions & 2 deletions ext/iconv/tests/eucjp2iso2022jp.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ iconv
if (PHP_OS_FAMILY === 'Solaris') {
die("skip Solaris iconv behaves differently");
}
// ISO-2022-JP is a stateful encoding, so the right answer is not
// unique. In particular, musl (type "unknown") is known to have an
// inefficient encoding for it that does not agree with the expected
// output below.
if (ICONV_IMPL == "unknown") {
die("skip byte-comparison of stateful encoding with unknown iconv");
}
?>
--INI--
error_reporting=2039
Expand All @@ -22,8 +29,8 @@ function hexdump($str) {
print "\n";
}

$str = str_repeat("���ܸ�ƥ����Ȥ� English text", 30);
$str .= "���ܸ�";
$str = str_repeat("���ܸ�ƥ����Ȥ� English text", 30);
$str .= "���ܸ�";

echo hexdump(iconv("EUC-JP", "ISO-2022-JP", $str));
?>
Expand Down
9 changes: 8 additions & 1 deletion ext/iconv/tests/iconv_mime_encode.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ iconv
iconv.internal_charset=iso-8859-1
--SKIPIF--
<?php
// ISO-2022-JP is a stateful encoding, so the right answer is not
// unique. In particular, musl (type "unknown") is known to have an
// inefficient encoding for it that does not agree with the expected
// output below.
if (ICONV_IMPL == "unknown") {
die("skip byte-comparison of stateful encoding with unknown iconv");
}
if (PHP_OS_FAMILY === 'Solaris') {
die("skip Solaris native iconv does not support MIME encoding");
}
Expand All @@ -26,7 +33,7 @@ $preference = array(
for ($line_len= 0; $line_len < 80; ++$line_len) {
print "-------- line length=$line_len\n";
$preference["line-length"] = $line_len;
$result = iconv_mime_encode("From", "����ץ�ʸ���󥵥�ץ�ʸ�������ܸ�ƥ�����", $preference);
$result = iconv_mime_encode("From", "����ץ�ʸ���󥵥�ץ�ʸ�������ܸ�ƥ�����", $preference);
var_dump($result);
if ($result !== false) {
$max = max(array_map("strlen", explode("\n", $result)));
Expand Down
25 changes: 12 additions & 13 deletions ext/standard/head.c
Original file line number Diff line number Diff line change
Expand Up @@ -314,32 +314,31 @@ PHP_FUNCTION(setrawcookie)
/* {{{ Returns true if headers have already been sent, false otherwise */
PHP_FUNCTION(headers_sent)
{
zval *arg1 = NULL, *arg2 = NULL;
const char *file="";
int line=0;
zval *by_ref_filename = NULL;
zval *by_ref_line = NULL;
const char *file = "";
int line = 0;

ZEND_PARSE_PARAMETERS_START(0, 2)
Z_PARAM_OPTIONAL
Z_PARAM_ZVAL(arg1)
Z_PARAM_ZVAL(arg2)
Z_PARAM_ZVAL(by_ref_filename)
Z_PARAM_ZVAL(by_ref_line)
ZEND_PARSE_PARAMETERS_END();

if (SG(headers_sent)) {
line = php_output_get_start_lineno();
file = php_output_get_start_filename();
}

switch(ZEND_NUM_ARGS()) {
case 2:
ZEND_TRY_ASSIGN_REF_LONG(arg2, line);
ZEND_FALLTHROUGH;
case 1:
if (by_ref_filename) {
if (file) {
ZEND_TRY_ASSIGN_REF_STRING(arg1, file);
ZEND_TRY_ASSIGN_REF_STRING(by_ref_filename, file);
} else {
ZEND_TRY_ASSIGN_REF_EMPTY_STRING(arg1);
ZEND_TRY_ASSIGN_REF_EMPTY_STRING(by_ref_filename);
}
break;
}
if (by_ref_line) {
ZEND_TRY_ASSIGN_REF_LONG(by_ref_line, line);
}

RETURN_BOOL(SG(headers_sent));
Expand Down
119 changes: 119 additions & 0 deletions ext/standard/tests/general_functions/headers_sent_by_ref_args.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
--TEST--
headers_sent() by-ref argument with named arguments
--FILE--
<?php

ob_start();

echo 'headers_sent(line: $line) prior to sending headers', PHP_EOL;
$file = null;
$line = null;

$v1 = headers_sent(line: $line);
$v2 = headers_list();

echo 'headers_sent():', PHP_EOL;
var_dump($v1);
echo 'headers_list():', PHP_EOL;
var_dump($v2);
echo '$file:', PHP_EOL;
var_dump($file);
echo '$line:', PHP_EOL;
var_dump($line);

echo 'headers_sent(filename: $file) prior to sending headers', PHP_EOL;
$file = null;
$line = null;
$v1 = headers_sent(filename: $file);
$v2 = headers_list();

echo 'headers_sent():', PHP_EOL;
var_dump($v1);
echo 'headers_list():', PHP_EOL;
var_dump($v2);
echo '$file:', PHP_EOL;
var_dump($file);
echo '$line:', PHP_EOL;
var_dump($line);

echo 'header():', PHP_EOL;
var_dump(header("HTTP 1.0", true, 200));

ob_end_flush();

echo 'headers_sent(line: $line) after sending headers', PHP_EOL;
$file = null;
$line = null;
$v1 = headers_sent(line: $line);
$v2 = headers_list();

echo 'headers_sent():', PHP_EOL;
var_dump($v1);
echo 'headers_list():', PHP_EOL;
var_dump($v2);
echo '$file:', PHP_EOL;
var_dump($file);
echo '$line:', PHP_EOL;
var_dump($line);

echo 'headers_sent(filename: $file) after sending headers', PHP_EOL;
$file = null;
$line = null;
$v1 = headers_sent(filename: $file);
$v2 = headers_list();

echo 'headers_sent():', PHP_EOL;
var_dump($v1);
echo 'headers_list():', PHP_EOL;
var_dump($v2);
echo '$file:', PHP_EOL;
var_dump($file);
echo '$line:', PHP_EOL;
var_dump($line);

echo "Done\n";
?>
--EXPECTF--
headers_sent(line: $line) prior to sending headers
headers_sent():
bool(false)
headers_list():
array(0) {
}
$file:
NULL
$line:
int(0)
headers_sent(filename: $file) prior to sending headers
headers_sent():
bool(false)
headers_list():
array(0) {
}
$file:
string(0) ""
$line:
NULL
header():
NULL
headers_sent(line: $line) after sending headers
headers_sent():
bool(true)
headers_list():
array(0) {
}
$file:
NULL
$line:
int(39)
headers_sent(filename: $file) after sending headers
headers_sent():
bool(true)
headers_list():
array(0) {
}
$file:
string(%d) "%s"
$line:
NULL
Done
Loading