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
62 changes: 58 additions & 4 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.6.0beta1

- Streams:
. Fixed file_put_contents() LOCK_EX early return leaking stream error
operation depth. (iliaal)

- Core:
. Deprecated "namespace" as a class constant name. (NickSdot)
. Changed run-tests.php to run in parallel by default, using up to 10
Expand All @@ -17,6 +13,13 @@ PHP NEWS
-d error_include_args=On). (David Carlier)
. Fixed GH-23121 (is_callable() wrongly accepts objects with no get_closure
handler). (David Carlier)
. Passing a 3rd argument to define() is now deprecated. (Girgias)
. Naming a function readonly is now deprecated. (Girgias)
. Added stateless closure cache. (ilutov)

- BZ2:
. Passing an object for the Bzip2 {de}compression stream filter is now
deprecated. Use get_object_vars() on the object instead. (Girgias)

- Curl:
. Improved cURL option validation errors to include the option name.
Expand Down Expand Up @@ -60,6 +63,12 @@ PHP NEWS
. Fixed IntlNumberRangeFormatter::format() crash when the formatting fails.
(David Carlier)

- MbString:
. Passing objects to mb_convert_variables() is now deprecated. (Girgias)

- MySQLi:
. The mysqli_get_charset() function is now deprecated. (Kamil Tekiela)

- PDO:
. Fixed pdo_raise_impl_error() emitting a warning under ERRMODE_SILENT.
(iliaal)
Expand Down Expand Up @@ -91,6 +100,24 @@ PHP NEWS
. Fixed segfault when comparing uninitialized SimpleXMLElement
instances. (iliaal)

- SPL:
. The spl_classes() function is now deprecated, use
ReflectionExtension::getClassNames() instead. (Girgias)
. The spl_object_hash() function is now deprecated, use spl_object_id()
instead. (Girgias)
. The following ArrayIterator methods are now deprecated:
* ArrayIterator::getFlags()
* ArrayIterator::setFlags()
* ArrayIterator::asort()
* ArrayIterator::ksort()
* ArrayIterator::uasort()
* ArrayIterator::uksort()
* ArrayIterator::natsort()
* ArrayIterator::natcasesort()
* ArrayIterator::unserialize()
* ArrayIterator::serialize()
(Girgias)

- Standard:
. Added the "filter.max_filter_count" stream context option for php://filter
URLs. Using more than 16 filters without configuring this option is now
Expand All @@ -108,11 +135,38 @@ PHP NEWS
shutdown re-registration). (David Carlier)
. Io\Poll\Context::wait() now takes a Time\Duration object as a timeout.
(timwolla)
. Passing an object to array_walk{_recursive} is now deprecated. Use
get_object_vars() on the object instead. (Girgias)
. The is_double() function is now deprecated, use is_float() instead.
(Girgias)
. The is_long() and is_integer() functions are now deprecated, use is_int()
instead. (Girgias)
. The doubleval() function is now deprecated, use floatval() instead.
(Girgias)
. The strcoll() function is now deprecated, use Collator::compare() instead.
(Girgias)
. The SORT_LOCALE_STRING constant for the family of sort functions is now
deprecated, use one of the following functions instead:
* Collator::sort()
* Collator::asort()
* Collator::sort()
* Collator::sortWithSortKeys()
(Girgias)

- Streams:
. Fixed file_put_contents() LOCK_EX early return leaking stream error
operation depth. (iliaal)

- XSL:
. Fixed use-after-free when a DOMDocument subclass __clone() retains the
stylesheet copy made by XSLTProcessor::importStylesheet(). (iliaal)

- Zlib
. Passing an object for the zlib deflate and inflate stream filter is now
deprecated. Use get_object_vars() on the object instead. (Girgias)
. Passing an object to the $option argument to deflate_init and inflate_init
is now deprecated. Use get_object_vars() on the object instead. (Girgias)

30 Jul 2026, PHP 8.6.0alpha3

- Core:
Expand Down
6 changes: 6 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,10 @@ PHP 8.6 UPGRADE NOTES
. Passing objects to mb_convert_variables() is now deprecated
RFC: https://wiki.php.net/rfc/deprecations_php_8_6#passing_objects_for_vars_parameter_of_mb_convert_variables

- MySQLi:
. The mysqli_get_charset() function is now deprecated.
RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_mysqli_get_charset

- SPL:
. The spl_classes() function is now deprecated, use
ReflectionExtension::getClassNames() instead.
Expand Down Expand Up @@ -788,6 +792,8 @@ PHP 8.6 UPGRADE NOTES
. The TAILCALL VM is now enabled on Windows when compiling with Clang >= 19
x86_64.
. The performance of ZTS builds has been improved.
. Added stateless closure cache.
RFC: https://wiki.php.net/rfc/closure-optimizations#stateless_closure_caching

- DOM:
. Made splitText() faster and consume less memory.
Expand Down
1 change: 1 addition & 0 deletions Zend/Optimizer/compact_literals.c
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
}
break;
case ZEND_CALLABLE_CONVERT:
case ZEND_DECLARE_LAMBDA_FUNCTION:
if (opline->extended_value != (uint32_t)-1) {
opline->extended_value = cache_size;
cache_size += sizeof(void *);
Expand Down
16 changes: 8 additions & 8 deletions Zend/tests/named_params/internal_variadics.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,33 @@ Named params on internal functions: Variadic functions that don't support extra
try {
array_merge([1, 2], a: [3, 4]);
} catch (ArgumentCountError $e) {
echo $e->getMessage(), "\n";
echo $e::class, ': ', $e->getMessage(), "\n";
}

try {
array_diff_key([1, 2], [3, 4], a: [5, 6]);
} catch (ArgumentCountError $e) {
echo $e->getMessage(), "\n";
echo $e::class, ': ', $e->getMessage(), "\n";
}

var_dump(array_intersect(array: [1, 2]) === [1, 2]);

try {
array_intersect([1, 2], arrays: [2]);
} catch (ArgumentCountError $e) {
echo $e->getMessage(), "\n";
echo $e::class, ': ', $e->getMessage(), "\n";
}

try {
$array = [1, 2];
array_push($array, ...['values' => 3]);
} catch (ArgumentCountError $e) {
echo $e->getMessage(), "\n";
echo $e::class, ': ', $e->getMessage(), "\n";
}
?>
--EXPECT--
Internal function array_merge() does not accept named variadic arguments
Internal function array_diff_key() does not accept named variadic arguments
ArgumentCountError: Internal function array_merge() does not accept named variadic arguments
ArgumentCountError: Internal function array_diff_key() does not accept named variadic arguments
bool(true)
Internal function array_intersect() does not accept named variadic arguments
Internal function array_push() does not accept named variadic arguments
ArgumentCountError: Internal function array_intersect() does not accept named variadic arguments
ArgumentCountError: Internal function array_push() does not accept named variadic arguments
2 changes: 1 addition & 1 deletion Zend/tests/partial_application/pipe_optimization_004.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ $_main:
; (lines=3, args=0, vars=0, tmps=%d)
; (after optimizer)
; %s:1-10
0000 T0 = DECLARE_LAMBDA_FUNCTION 0
0000 T0 = DECLARE_LAMBDA_FUNCTION %d 0
0001 FREE T0
0002 RETURN int(1)

Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/partial_application/pipe_optimization_007.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ $_main:
; (lines=3, args=0, vars=0, tmps=%d)
; (after optimizer)
; %s:1-10
0000 T0 = DECLARE_LAMBDA_FUNCTION 0
0000 T0 = DECLARE_LAMBDA_FUNCTION %d 0
0001 FREE T0
0002 RETURN int(1)

Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/partial_application/pipe_optimization_008.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ $_main:
; (lines=4, args=0, vars=1, tmps=%d)
; (after optimizer)
; %s:1-10
0000 T1 = DECLARE_LAMBDA_FUNCTION 0
0000 T1 = DECLARE_LAMBDA_FUNCTION %d 0
0001 BIND_LEXICAL T1 CV0($a)
0002 FREE T1
0003 RETURN int(1)
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/partial_application/pipe_optimization_013.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ $_main:
; (lines=4, args=0, vars=1, tmps=%d)
; (after optimizer)
; %s:1-9
0000 T1 = DECLARE_LAMBDA_FUNCTION 0
0000 T1 = DECLARE_LAMBDA_FUNCTION %d 0
0001 BIND_LEXICAL T1 CV0($b)
0002 FREE T1
0003 RETURN int(1)
Expand Down
18 changes: 18 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -8941,6 +8941,7 @@ static zend_string *zend_begin_func_decl(znode *result, zend_op_array *op_array,
if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL);
opline->op2.num = func_ref;
opline->extended_value = (uint32_t)-1;
} else {
opline = get_next_op();
opline->opcode = ZEND_DECLARE_FUNCTION;
Expand Down Expand Up @@ -9130,6 +9131,23 @@ static zend_op_array *zend_compile_func_decl_ex(

zend_compile_stmt(stmt_ast);

if (decl->kind == ZEND_AST_CLOSURE || decl->kind == ZEND_AST_ARROW_FUNC) {
zend_op_array *declaring_op_array = orig_oparray_context.op_array;

if ((op_array->fn_flags & ZEND_ACC_STATIC)
&& !op_array->static_variables
/* Don't cache closures in main, as those would leak without a proper
* cleanup mechanism. */
&& declaring_op_array->function_name
&& declaring_op_array->last) {
zend_op *declare_lambda_op = &declaring_op_array->opcodes[declaring_op_array->last - 1];
if (declare_lambda_op->opcode == ZEND_DECLARE_LAMBDA_FUNCTION) {
declare_lambda_op->extended_value = declaring_op_array->cache_size;
declaring_op_array->cache_size += sizeof(void *);
}
}
}

if (is_method) {
CG(zend_lineno) = decl->start_lineno;
zend_check_magic_method_implementation(
Expand Down
10 changes: 10 additions & 0 deletions Zend/zend_execute_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ void init_executor(void) /* {{{ */

zend_hash_init(&EG(callable_convert_cache), 8, NULL, ZVAL_PTR_DTOR, 0);
zend_hash_init(&EG(partial_function_application_cache), 8, NULL, zend_partial_op_array_dtor, 0);
zend_stack_init(&EG(lambda_cache), sizeof(zend_object *));

EG(active) = 1;
}
Expand Down Expand Up @@ -268,6 +269,14 @@ void shutdown_destructors(void) /* {{{ */
}
/* }}} */

static void lambda_dtor(zend_object **closure_ptr)
{
zend_object *closure = *closure_ptr;
if (GC_DELREF(closure) == 0) {
zend_objects_store_del(closure);
}
}

/* Free values held by the executor. */
ZEND_API void zend_shutdown_executor_values(bool fast_shutdown)
{
Expand Down Expand Up @@ -421,6 +430,7 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown)

zend_hash_clean(&EG(callable_convert_cache));
zend_hash_clean(&EG(partial_function_application_cache));
zend_stack_clean(&EG(lambda_cache), (void (*)(void *)) lambda_dtor, 1);

#if ZEND_DEBUG
if (!CG(unclean_shutdown)) {
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ struct _zend_executor_globals {

HashTable callable_convert_cache;
HashTable partial_function_application_cache;
zend_stack lambda_cache;

void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};
Expand Down
17 changes: 15 additions & 2 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -8409,13 +8409,21 @@ ZEND_VM_HANDLER(210, ZEND_DECLARE_ATTRIBUTED_CONST, CONST, CONST)
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}

ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, NUM)
ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, UNUSED, NUM, NUM|CACHE_SLOT)
{
USE_OPLINE
zend_function *func;
zval *object;
zend_class_entry *called_scope;

if (opline->extended_value != (uint32_t)-1) {
zend_object *closure = CACHED_PTR(opline->extended_value);
if (closure) {
ZVAL_OBJ_COPY(EX_VAR(opline->result.var), closure);
ZEND_VM_NEXT_OPCODE();
}
}

func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
if (Z_TYPE(EX(This)) == IS_OBJECT) {
called_scope = Z_OBJCE(EX(This));
Expand All @@ -8432,7 +8440,12 @@ ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, NUM)
SAVE_OPLINE();
zend_create_closure(EX_VAR(opline->result.var), func,
EX(func)->op_array.scope, called_scope, object);

if (opline->extended_value != (uint32_t)-1) {
zend_object *closure = Z_OBJ_P(EX_VAR(opline->result.var));
GC_ADDREF(closure);
CACHE_PTR(opline->extended_value, closure);
zend_stack_push(&EG(lambda_cache), &closure);
}
ZEND_VM_NEXT_OPCODE();
}

Expand Down
Loading