Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a2fc8fe
ext/openssl: openssl: use zend_string_init() instead of manual alloca…
arshidkv12 Mar 14, 2026
31962aa
ext/pgsql: Enable lo_tell64/lo_truncate64 by removing dead VE_PG_LO64…
KentarouTakeda Mar 14, 2026
a5ffcf3
Use zend_string_init_fast() for single-character string creation (#21…
arshidkv12 Mar 14, 2026
006f141
ext/phar: Use zend_string_concat2 instead of manual zend_string_alloc…
arshidkv12 Mar 14, 2026
605c075
ext/zip: add ZipArchive::openString() method
tstarling Apr 29, 2024
b95f0eb
ext/zip: Fix const-generic compile warning
ndossche Mar 14, 2026
e0ac74f
Merge branch 'PHP-8.4' into PHP-8.5
ndossche Mar 14, 2026
c197fad
Merge branch 'PHP-8.5'
ndossche Mar 14, 2026
91134db
ReflectionFiber::getCallable(): remove stray backslash (#21439)
DanielEScherzer Mar 14, 2026
e257c08
ext/gd: phpinfo() to be able to display libjpeg 10.0 support.
devnexen Mar 13, 2026
277a016
Merge branch 'PHP-8.4' into PHP-8.5
devnexen Mar 14, 2026
7e16d4e
Merge branch 'PHP-8.5'
devnexen Mar 14, 2026
4561e92
Fix ReflectionMethod::invoke() for first class callables (#21389)
iliaal Mar 15, 2026
3927630
Remove unused config.h inclusion in SAPIs (#21377)
petk Mar 15, 2026
45157d2
ext/zip: Remove unreachable break after RETURN_STRING (#21458)
arshidkv12 Mar 16, 2026
f44609c
ext/standard: Remove unreachable break after RETURN_STRING (#21459)
arshidkv12 Mar 16, 2026
0039af0
Propagate bind error for stream_socket_server()
iluuu1994 Mar 3, 2026
a8543df
Convert remaining zend_parse_parameters_none() to ZPP (#21343)
alexandre-daubois Mar 16, 2026
b627757
Fix GH-20042: SEGV in array.c when error handler clobbers IAP object
iliaal Mar 16, 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
7 changes: 7 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ PHP NEWS
. Clear session-local state disconnect-equivalent processing.
(KentarouTakeda)

- PGSQL:
. Enabled 64 bits support for pg_lo_truncate()/pg_lo_tell()
if the server supports it. (KentarouTakeda)

- Phar:
. Support reference values in Phar::mungServer(). (ndossche)
. Invalid values now throw in Phar::mungServer() instead of being silently
Expand Down Expand Up @@ -141,11 +145,14 @@ PHP NEWS
. Allowed filtered streams to be casted as fd for select. (Jakub Zelenka)
. Fixed bug GH-21221 (Prevent closing of innerstream of php://temp stream).
(ilutov)
. Improved stream_socket_server() bind failure error reporting. (ilutov)

- Zip:
. Fixed ZipArchive callback being called after executor has shut down.
(ilutov)
. Support minimum version for libzip dependency updated to 1.0.0.
(David Carlier)
. Added ZipArchive::openString() method.
(Tim Starling, Soner Sayakci, Ghaith Olabi)

<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>
3 changes: 3 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ PHP 8.6 UPGRADE NOTES
bound.
RFC: https://wiki.php.net/rfc/clamp_v2

- Zip:
. Added ZipArchive::openString() method.

========================================
7. New Classes and Interfaces
========================================
Expand Down
2 changes: 1 addition & 1 deletion Zend/Optimizer/sccp.c
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ static inline zend_result ct_eval_fetch_dim(zval *result, zval *op1, zval *op2,
return FAILURE;
}
if (index >= 0 && index < Z_STRLEN_P(op1)) {
ZVAL_STR(result, zend_string_init(&Z_STRVAL_P(op1)[index], 1, 0));
ZVAL_CHAR(result, Z_STRVAL_P(op1)[index]);
return SUCCESS;
}
}
Expand Down
4 changes: 4 additions & 0 deletions ext/gd/libgd/gd_jpeg.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ const char * gdJpegGetVersionString()
return "9 compatible";
break;

case 100:
return "10 compatible";
break;

default:
return "unknown";
}
Expand Down
5 changes: 1 addition & 4 deletions ext/openssl/openssl_backend_v3.c
Original file line number Diff line number Diff line change
Expand Up @@ -567,10 +567,7 @@ static zend_string *php_openssl_get_utf8_param(
char buf[64];
size_t len;
if (EVP_PKEY_get_utf8_string_param(pkey, param, buf, sizeof(buf), &len) > 0) {
zend_string *str = zend_string_alloc(len, 0);
memcpy(ZSTR_VAL(str), buf, len);
ZSTR_VAL(str)[len] = '\0';
return str;
return zend_string_init(buf, len, 0);
}
return NULL;
}
Expand Down
4 changes: 1 addition & 3 deletions ext/pdo/pdo_stmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -1916,9 +1916,7 @@ PHP_METHOD(PDOStatement, debugDumpParams)

PHP_METHOD(PDOStatement, getIterator)
{
if (zend_parse_parameters_none() == FAILURE) {
return;
}
ZEND_PARSE_PARAMETERS_NONE();

zend_create_internal_iterator_zval(return_value, ZEND_THIS);
}
Expand Down
8 changes: 0 additions & 8 deletions ext/pgsql/pgsql.c
Original file line number Diff line number Diff line change
Expand Up @@ -3083,15 +3083,11 @@ PHP_FUNCTION(pg_lo_tell)
pgsql = Z_PGSQL_LOB_P(pgsql_id);
CHECK_PGSQL_LOB(pgsql);

#ifdef VE_PG_LO64
if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
offset = lo_tell64((PGconn *)pgsql->conn, pgsql->lofd);
} else {
offset = lo_tell((PGconn *)pgsql->conn, pgsql->lofd);
}
#else
offset = lo_tell((PGconn *)pgsql->conn, pgsql->lofd);
#endif
RETURN_LONG(offset);
}
/* }}} */
Expand All @@ -3112,15 +3108,11 @@ PHP_FUNCTION(pg_lo_truncate)
pgsql = Z_PGSQL_LOB_P(pgsql_id);
CHECK_PGSQL_LOB(pgsql);

#ifdef VE_PG_LO64
if (PQserverVersion((PGconn *)pgsql->conn) >= 90300) {
result = lo_truncate64((PGconn *)pgsql->conn, pgsql->lofd, size);
} else {
result = lo_truncate((PGconn *)pgsql->conn, pgsql->lofd, size);
}
#else
result = lo_truncate((PGconn *)pgsql->conn, pgsql->lofd, size);
#endif
if (!result) {
RETURN_TRUE;
} else {
Expand Down
24 changes: 12 additions & 12 deletions ext/phar/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -887,10 +887,10 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from
memcmp(ZSTR_VAL(str_key), ZSTR_VAL(resource_from->path)+1, from_len) == 0 &&
IS_SLASH(ZSTR_VAL(str_key)[from_len])) {

new_str_key = zend_string_alloc(ZSTR_LEN(str_key) + to_len - from_len, 0);
memcpy(ZSTR_VAL(new_str_key), ZSTR_VAL(resource_to->path) + 1, to_len);
memcpy(ZSTR_VAL(new_str_key) + to_len, ZSTR_VAL(str_key) + from_len, ZSTR_LEN(str_key) - from_len);
ZSTR_VAL(new_str_key)[ZSTR_LEN(new_str_key)] = 0;
new_str_key = zend_string_concat2(
ZSTR_VAL(resource_to->path) + 1, to_len,
ZSTR_VAL(str_key) + from_len, ZSTR_LEN(str_key) - from_len
);

is_modified = true;
entry->is_modified = true;
Expand All @@ -909,10 +909,10 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from
if (zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource_from->path)+1, from_len) &&
(ZSTR_LEN(str_key) == from_len || IS_SLASH(ZSTR_VAL(str_key)[from_len]))) {

new_str_key = zend_string_alloc(ZSTR_LEN(str_key) + to_len - from_len, 0);
memcpy(ZSTR_VAL(new_str_key), ZSTR_VAL(resource_to->path) + 1, to_len);
memcpy(ZSTR_VAL(new_str_key) + to_len, ZSTR_VAL(str_key) + from_len, ZSTR_LEN(str_key) - from_len);
ZSTR_VAL(new_str_key)[ZSTR_LEN(new_str_key)] = 0;
new_str_key = zend_string_concat2(
ZSTR_VAL(resource_to->path) + 1, to_len,
ZSTR_VAL(str_key) + from_len, ZSTR_LEN(str_key) - from_len
);

zend_string_release_ex(str_key, 0);
b->h = zend_string_hash_val(new_str_key);
Expand All @@ -926,10 +926,10 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from
if (zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource_from->path)+1, from_len) &&
(ZSTR_LEN(str_key) == from_len || IS_SLASH(ZSTR_VAL(str_key)[from_len]))) {

new_str_key = zend_string_alloc(ZSTR_LEN(str_key) + to_len - from_len, 0);
memcpy(ZSTR_VAL(new_str_key), ZSTR_VAL(resource_to->path) + 1, to_len);
memcpy(ZSTR_VAL(new_str_key) + to_len, ZSTR_VAL(str_key) + from_len, ZSTR_LEN(str_key) - from_len);
ZSTR_VAL(new_str_key)[ZSTR_LEN(new_str_key)] = 0;
new_str_key = zend_string_concat2(
ZSTR_VAL(resource_to->path) + 1, to_len,
ZSTR_VAL(str_key) + from_len, ZSTR_LEN(str_key) - from_len
);

zend_string_release_ex(str_key, 0);
b->h = zend_string_hash_val(new_str_key);
Expand Down
23 changes: 18 additions & 5 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -3441,14 +3441,27 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
}

/* For Closure::__invoke(), closures from different source locations have
* different signatures, so we must reject those. However, closures created
* from the same source (e.g. in a loop) share the same op_array and should
* be allowed. Compare the underlying function pointer via op_array. */
* different signatures, so we must reject those. */
if (obj_ce == zend_ce_closure && !Z_ISUNDEF(intern->obj)
&& Z_OBJ_P(object) != Z_OBJ(intern->obj)) {
const zend_function *orig_func = zend_get_closure_method_def(Z_OBJ(intern->obj));
const zend_function *given_func = zend_get_closure_method_def(Z_OBJ_P(object));
if (orig_func->op_array.opcodes != given_func->op_array.opcodes) {

bool same_closure = false;
/* Check if they are either both fake closures or they both are not. */
if ((orig_func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) == (given_func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE)) {
if (orig_func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) {
/* For fake closures, scope and name must match. */
same_closure = orig_func->common.scope == given_func->common.scope
&& orig_func->common.function_name == given_func->common.function_name;
} else {
/* Otherwise the opcode structure must be identical. */
ZEND_ASSERT(orig_func->type == ZEND_USER_FUNCTION);
same_closure = orig_func->op_array.opcodes == given_func->op_array.opcodes;
}
}

if (!same_closure) {
if (!variadic) {
efree(params);
}
Expand Down Expand Up @@ -7890,7 +7903,7 @@ ZEND_METHOD(ReflectionFiber, getCallable)
ZEND_PARSE_PARAMETERS_NONE();

if (fiber == NULL || fiber->context.status == ZEND_FIBER_STATUS_DEAD) {
zend_throw_error(NULL, "Cannot fetch the callable from a fiber that has terminated"); \
zend_throw_error(NULL, "Cannot fetch the callable from a fiber that has terminated");
RETURN_THROWS();
}

Expand Down
62 changes: 62 additions & 0 deletions ext/reflection/tests/gh21362.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,60 @@ $m2 = new ReflectionMethod($closures[0], '__invoke');
foreach ($closures as $closure) {
var_dump($m2->invoke($closure));
}

// First-class callable of a userland function
function my_func($x) { return "my_func: $x"; }
function other_func($x) { return "other_func: $x"; }

$mf = my_func(...);
$mf2 = my_func(...);
$of = other_func(...);

$m3 = new ReflectionMethod($mf, '__invoke');
var_dump($m3->invoke($mf, 'test'));
var_dump($m3->invoke($mf2, 'test'));

try {
$m3->invoke($of, 'test');
echo "No exception thrown\n";
} catch (ReflectionException $e) {
echo $e->getMessage() . "\n";
}

// Internal closures (first-class callable syntax) should also be validated
$vd = var_dump(...);
$pr = print_r(...);

$m4 = new ReflectionMethod($vd, '__invoke');
$m4->invoke($vd, 'internal closure OK');

// Cloned internal closure is a different object but same function - should work
$vd2 = clone $vd;
$m4->invoke($vd2, 'cloned internal closure OK');

// Different internal closure should throw
try {
$m4->invoke($pr, 'should not print');
echo "No exception thrown\n";
} catch (ReflectionException $e) {
echo $e->getMessage() . "\n";
}

// Cross-type: userland Closure to internal Closure's invoke should throw
try {
$m4->invoke($c1, 'should not print');
echo "No exception thrown\n";
} catch (ReflectionException $e) {
echo $e->getMessage() . "\n";
}

// Cross-type: internal Closure to userland Closure's invoke should throw
try {
$m->invoke($vd, 'should not print');
echo "No exception thrown\n";
} catch (ReflectionException $e) {
echo $e->getMessage() . "\n";
}
?>
--EXPECT--
c1: foo=FOO, bar=BAR
Expand All @@ -51,3 +105,11 @@ Given Closure is not the same as the reflected Closure
int(0)
int(1)
int(2)
string(13) "my_func: test"
string(13) "my_func: test"
Given Closure is not the same as the reflected Closure
string(19) "internal closure OK"
string(26) "cloned internal closure OK"
Given Closure is not the same as the reflected Closure
Given Closure is not the same as the reflected Closure
Given Closure is not the same as the reflected Closure
40 changes: 10 additions & 30 deletions ext/simplexml/simplexml.c
Original file line number Diff line number Diff line change
Expand Up @@ -1614,9 +1614,7 @@ PHP_METHOD(SimpleXMLElement, getName)
xmlNodePtr node;
int namelen;

if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_NONE();

sxe = Z_SXEOBJ_P(ZEND_THIS);

Expand Down Expand Up @@ -1900,9 +1898,7 @@ static zend_result sxe_object_cast(zend_object *readobj, zval *writeobj, int typ
/* {{{ Returns the string content */
PHP_METHOD(SimpleXMLElement, __toString)
{
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_NONE();

zend_result rv = sxe_object_cast_ex(Z_OBJ_P(ZEND_THIS), return_value, IS_STRING);
ZEND_IGNORE_VALUE(rv);
Expand Down Expand Up @@ -1950,9 +1946,7 @@ PHP_METHOD(SimpleXMLElement, count)
{
php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);

if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_NONE();

RETURN_LONG(php_sxe_count_elements_helper(sxe));
}
Expand All @@ -1962,9 +1956,7 @@ PHP_METHOD(SimpleXMLElement, count)
/* {{{ Rewind to first element */
PHP_METHOD(SimpleXMLElement, rewind)
{
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_NONE();

php_sxe_rewind_iterator(Z_SXEOBJ_P(ZEND_THIS));
}
Expand All @@ -1975,9 +1967,7 @@ PHP_METHOD(SimpleXMLElement, valid)
{
php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);

if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_NONE();

RETURN_BOOL(!Z_ISUNDEF(sxe->iter.data));
}
Expand All @@ -1988,9 +1978,7 @@ PHP_METHOD(SimpleXMLElement, current)
{
php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);

if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_NONE();

if (Z_ISUNDEF(sxe->iter.data)) {
zend_throw_error(NULL, "Iterator not initialized or already consumed");
Expand All @@ -2008,9 +1996,7 @@ PHP_METHOD(SimpleXMLElement, key)
php_sxe_object *intern;
php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);

if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_NONE();

if (Z_ISUNDEF(sxe->iter.data)) {
zend_throw_error(NULL, "Iterator not initialized or already consumed");
Expand All @@ -2031,9 +2017,7 @@ PHP_METHOD(SimpleXMLElement, key)
/* {{{ Move to next element */
PHP_METHOD(SimpleXMLElement, next)
{
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_NONE();

php_sxe_move_forward_iterator(Z_SXEOBJ_P(ZEND_THIS));
}
Expand All @@ -2046,9 +2030,7 @@ PHP_METHOD(SimpleXMLElement, hasChildren)
php_sxe_object *child;
xmlNodePtr node;

if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_NONE();

if (Z_ISUNDEF(sxe->iter.data) || sxe->iter.type == SXE_ITER_ATTRLIST) {
RETURN_FALSE;
Expand All @@ -2071,9 +2053,7 @@ PHP_METHOD(SimpleXMLElement, getChildren)
{
php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);

if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
ZEND_PARSE_PARAMETERS_NONE();

if (Z_ISUNDEF(sxe->iter.data) || sxe->iter.type == SXE_ITER_ATTRLIST) {
return; /* return NULL */
Expand Down
Loading
Loading