diff --git a/NEWS b/NEWS index cc2eed87ccfb..7ec4eb4b809e 100644 --- a/NEWS +++ b/NEWS @@ -65,6 +65,12 @@ PHP NEWS TCP_USER_TIMEOUT, and SO_LINGER options. (Weilin Du) . Fixed various memory related issues in ext/sockets. (David Carlier) +- Standard: + . Fixed setlocale() to reject locale names containing NUL bytes instead of + silently truncating them, and to reject arrays passed after the $locales + argument or additional arguments passed after an array $locales argument. + (Weilin Du) + - Streams: . Added a new IO copy API used by php_stream_copy_to_stream_ex() that leverages platform primitives (sendfile, splice, copy_file_range, diff --git a/UPGRADING b/UPGRADING index 34817c10974f..d30a5663274f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -201,6 +201,12 @@ PHP 8.6 UPGRADE NOTES bytes. . parse_str() now raises a ValueError when the $string argument contains NUL bytes. + . setlocale() now raises a ValueError when a locale name contains NUL bytes, + instead of silently truncating it. + Arrays are now accepted only for the $locales argument. Passing an array as + a later variadic locale argument now throws a TypeError. Passing any + additional locale arguments when $locales is an array now throws an + ArgumentCountError. . linkinfo() now raises a ValueError when the $path argument is empty. . pathinfo() now raises a ValueError when an invalid $flag argument value is passed. diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c index a4ecb19c85ef..d603fe159e20 100644 --- a/Zend/Optimizer/compact_literals.c +++ b/Zend/Optimizer/compact_literals.c @@ -26,6 +26,7 @@ #include "zend_execute.h" #include "zend_vm.h" #include "zend_extensions.h" +#include "zend_partial.h" #define DEBUG_COMPACT_LITERALS 0 @@ -747,7 +748,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } break; case ZEND_CALLABLE_CONVERT_PARTIAL: - opline->op1.num = cache_size; + opline->extended_value = cache_size | (opline->extended_value & ZEND_PARTIAL_FLAGS); cache_size += 2 * sizeof(void *); break; } diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index 77dc322fbdec..dcb4e8bc8412 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -469,6 +469,34 @@ static uint32_t zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa) } } } + + if (call_info->caller_call_opline && call_info->caller_call_opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) { + /* Build a bitset of constant pre-bound PFA args: These are args whose value is alway the same for all + * instances of a PFA. */ + uint32_t const_args = 0; + for (uint32_t i = 0, l = MIN(sizeof(const_args)*CHAR_BIT, call_info->num_args); i < l; i++) { + zend_op *send_opline = call_info->arg_info[i].opline; + if (send_opline->op1_type == IS_CONST) { + zval *value = CT_CONSTANT_EX(op_array, send_opline->op1.constant); + if (Z_TYPE_P(value) == IS_CONSTANT_AST) { + /* Const exprs can evaluate to non-const zvals (e.g. objects), and are not idempotent */ + continue; + } + const_args |= (UINT32_C(1) << i); + } + } + + /* Pass the bitset to the ZEND_CALLABLE_CONVERT_PARTIAL opline. */ + zend_op *call_opline = call_info->caller_call_opline; + if (call_opline->op2_type == IS_UNUSED) { + call_opline->op2.num = const_args; + } else { + ZEND_ASSERT(call_opline->op2_type == IS_CONST); + zval *zv = CT_CONSTANT_EX(op_array, call_opline->op2.constant); + Z_EXTRA_P(zv) = const_args; + } + } + call_info = call_info->next_callee; } while (call_info); } diff --git a/Zend/tests/lazy_objects/setRawValueWithoutLazyInitialization_no_dynamic_prop.phpt b/Zend/tests/lazy_objects/setRawValueWithoutLazyInitialization_no_dynamic_prop.phpt index 9151e58f3fc6..3c9686ba04c4 100644 --- a/Zend/tests/lazy_objects/setRawValueWithoutLazyInitialization_no_dynamic_prop.phpt +++ b/Zend/tests/lazy_objects/setRawValueWithoutLazyInitialization_no_dynamic_prop.phpt @@ -38,6 +38,6 @@ test('Proxy', $obj); ?> --EXPECT-- # Ghost -ReflectionException: Can not use setRawValueWithoutLazyInitialization on dynamic property C::$dyn +ReflectionException: Cannot use setRawValueWithoutLazyInitialization() on dynamic property C::$dyn # Proxy -ReflectionException: Can not use setRawValueWithoutLazyInitialization on dynamic property C::$dyn +ReflectionException: Cannot use setRawValueWithoutLazyInitialization() on dynamic property C::$dyn diff --git a/Zend/tests/lazy_objects/skipLazyInitialization.phpt b/Zend/tests/lazy_objects/skipLazyInitialization.phpt index 4fc47b13db67..8a95e684a5a2 100644 --- a/Zend/tests/lazy_objects/skipLazyInitialization.phpt +++ b/Zend/tests/lazy_objects/skipLazyInitialization.phpt @@ -198,10 +198,10 @@ getValue(): string(5) "value" ## Property [ public static $static = 'static' ] skipInitializerForProperty(): -ReflectionException: Can not use skipLazyInitialization on static property A::$static +ReflectionException: Cannot use skipLazyInitialization() on static property A::$static setRawValueWithoutLazyInitialization(): -ReflectionException: Can not use setRawValueWithoutLazyInitialization on static property A::$static +ReflectionException: Cannot use setRawValueWithoutLazyInitialization() on static property A::$static ## Property [ public $noDefault = NULL ] @@ -238,10 +238,10 @@ getValue(): string(5) "value" ## Property [ public virtual $virtual { get; set; } ] skipInitializerForProperty(): -ReflectionException: Can not use skipLazyInitialization on virtual property A::$virtual +ReflectionException: Cannot use skipLazyInitialization() on virtual property A::$virtual setRawValueWithoutLazyInitialization(): -ReflectionException: Can not use setRawValueWithoutLazyInitialization on virtual property A::$virtual +ReflectionException: Cannot use setRawValueWithoutLazyInitialization() on virtual property A::$virtual ## Property [ $dynamicProp ] @@ -295,10 +295,10 @@ getValue(): string(5) "value" ## Property [ public static $static = 'static' ] skipInitializerForProperty(): -ReflectionException: Can not use skipLazyInitialization on static property A::$static +ReflectionException: Cannot use skipLazyInitialization() on static property A::$static setRawValueWithoutLazyInitialization(): -ReflectionException: Can not use setRawValueWithoutLazyInitialization on static property A::$static +ReflectionException: Cannot use setRawValueWithoutLazyInitialization() on static property A::$static ## Property [ public $noDefault = NULL ] @@ -335,10 +335,10 @@ getValue(): string(5) "value" ## Property [ public virtual $virtual { get; set; } ] skipInitializerForProperty(): -ReflectionException: Can not use skipLazyInitialization on virtual property A::$virtual +ReflectionException: Cannot use skipLazyInitialization() on virtual property A::$virtual setRawValueWithoutLazyInitialization(): -ReflectionException: Can not use setRawValueWithoutLazyInitialization on virtual property A::$virtual +ReflectionException: Cannot use setRawValueWithoutLazyInitialization() on virtual property A::$virtual ## Property [ $dynamicProp ] diff --git a/Zend/tests/lazy_objects/skipLazyInitialization_no_dynamic_prop.phpt b/Zend/tests/lazy_objects/skipLazyInitialization_no_dynamic_prop.phpt index 74e12cb3629f..dbbe88c52a2c 100644 --- a/Zend/tests/lazy_objects/skipLazyInitialization_no_dynamic_prop.phpt +++ b/Zend/tests/lazy_objects/skipLazyInitialization_no_dynamic_prop.phpt @@ -38,6 +38,6 @@ test('Proxy', $obj); ?> --EXPECT-- # Ghost -ReflectionException: Can not use skipLazyInitialization on dynamic property C::$dyn +ReflectionException: Cannot use skipLazyInitialization() on dynamic property C::$dyn # Proxy -ReflectionException: Can not use skipLazyInitialization on dynamic property C::$dyn +ReflectionException: Cannot use skipLazyInitialization() on dynamic property C::$dyn diff --git a/Zend/tests/partial_application/const_arg_opt.phpt b/Zend/tests/partial_application/const_arg_opt.phpt new file mode 100644 index 000000000000..d63456a96a5d --- /dev/null +++ b/Zend/tests/partial_application/const_arg_opt.phpt @@ -0,0 +1,162 @@ +--TEST-- +Constant argument optimization +--DESCRIPTION-- +Pre-bound arguments that are constant can be burned into the generated +op_array instead of being passed via the Closure's lexical vars. +--ENV-- +A=1 +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--FILE-- +getClosureUsedVariables(); + if ($vars === []) { + echo "no lexical vars\n"; + } else { + $varNames = array_keys($vars); + echo 'lexical vars: ', implode(', ', $varNames), "\n"; + } +} + +echo "# Non-constant pre-bound argument:\n"; +$f = f(getenv('A'), ?); +print_lexical_vars($f); +$f(2); + +echo "# Constant pre-bound argument:\n"; +$f = f(2, ?); +print_lexical_vars($f); +$f(2); + +echo "# Constant pre-bound argument (inverted):\n"; +$f = f(?, 2); +print_lexical_vars($f); +$f(1); + +echo "# Inlined pre-bound argument:\n"; +$f = f(g(), ?); +print_lexical_vars($f); +$f(2); + +echo "# Constexpr pre-bound argument:\n"; +const B = 4; +$f = f(B, ?); +print_lexical_vars($f); +$f(2); + +echo "# Mixed arguments:\n"; +$f = h(5, getenv('A'), ?); +print_lexical_vars($f); +$f(2); + +echo "# Constant array pre-bound argument:\n"; +$f = f(['foo' => 'bar'], ?); +print_lexical_vars($f); +$f(2); + +echo "# Named arguments:\n"; +$f = h(1, c: ?, b: ?); +print_lexical_vars($f); +$f(2, 3); + +echo "# Many arguments (optimization can not be applied for all args) :\n"; +$f = i(?, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33); +print_lexical_vars($f); +$f(33); + +?> +--EXPECT-- +# Non-constant pre-bound argument: +lexical vars: a +array(2) { + [0]=> + string(1) "1" + [1]=> + int(2) +} +# Constant pre-bound argument: +no lexical vars +array(2) { + [0]=> + int(2) + [1]=> + int(2) +} +# Constant pre-bound argument (inverted): +no lexical vars +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} +# Inlined pre-bound argument: +no lexical vars +array(2) { + [0]=> + int(3) + [1]=> + int(2) +} +# Constexpr pre-bound argument: +lexical vars: a +array(2) { + [0]=> + int(4) + [1]=> + int(2) +} +# Mixed arguments: +lexical vars: b +array(3) { + [0]=> + int(5) + [1]=> + string(1) "1" + [2]=> + int(2) +} +# Constant array pre-bound argument: +no lexical vars +array(2) { + [0]=> + array(1) { + ["foo"]=> + string(3) "bar" + } + [1]=> + int(2) +} +# Named arguments: +no lexical vars +array(3) { + [0]=> + int(1) + [1]=> + int(3) + [2]=> + int(2) +} +# Many arguments (optimization can not be applied for all args) : +lexical vars: a33 +int(33) diff --git a/Zend/tests/partial_application/constexpr_001.phpt b/Zend/tests/partial_application/constexpr_001.phpt new file mode 100644 index 000000000000..8ab5fc881af5 --- /dev/null +++ b/Zend/tests/partial_application/constexpr_001.phpt @@ -0,0 +1,54 @@ +--TEST-- +PFA in constexpr 001 +--FILE-- + +--EXPECT-- +array(2) { + [0]=> + string(3) "foo" + [1]=> + int(1) +} +array(2) { + [0]=> + string(3) "foo" + [1]=> + int(1) +} +array(2) { + [0]=> + string(3) "foo" + [1]=> + int(1) +} +array(2) { + [0]=> + string(3) "foo" + [1]=> + int(1) +} diff --git a/Zend/tests/partial_application/constexpr_002.phpt b/Zend/tests/partial_application/constexpr_002.phpt new file mode 100644 index 000000000000..05f48b5eb67d --- /dev/null +++ b/Zend/tests/partial_application/constexpr_002.phpt @@ -0,0 +1,22 @@ +--TEST-- +PFA in constexpr: non-constexpr arg +--FILE-- +getMessage(), " on line ", $e->getLine(); +} + +?> +--EXPECTF-- +Fatal error: Constant expression contains invalid operations in %s on line %d diff --git a/Zend/tests/partial_application/constexpr_003.phpt b/Zend/tests/partial_application/constexpr_003.phpt new file mode 100644 index 000000000000..4e7b7a17d0b0 --- /dev/null +++ b/Zend/tests/partial_application/constexpr_003.phpt @@ -0,0 +1,25 @@ +--TEST-- +PFA in constexpr: named args +--FILE-- + +--EXPECT-- +array(3) { + [0]=> + string(5) "hello" + [1]=> + int(3) + [2]=> + float(1.5) +} diff --git a/Zend/tests/partial_application/constexpr_004.phpt b/Zend/tests/partial_application/constexpr_004.phpt new file mode 100644 index 000000000000..b5fee160a03e --- /dev/null +++ b/Zend/tests/partial_application/constexpr_004.phpt @@ -0,0 +1,28 @@ +--TEST-- +PFA in constexpr: extra named args +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + array(3) { + [0]=> + int(1) + ["foo"]=> + string(3) "bar" + ["bar"]=> + string(3) "baz" + } +} diff --git a/Zend/tests/partial_application/constexpr_005.phpt b/Zend/tests/partial_application/constexpr_005.phpt new file mode 100644 index 000000000000..48c830a0b830 --- /dev/null +++ b/Zend/tests/partial_application/constexpr_005.phpt @@ -0,0 +1,13 @@ +--TEST-- +PFA in constexpr: variadic placeholder must be after positional params +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder must be last in %s on line %d diff --git a/Zend/tests/partial_application/constexpr_006.phpt b/Zend/tests/partial_application/constexpr_006.phpt new file mode 100644 index 000000000000..873c0b155fe1 --- /dev/null +++ b/Zend/tests/partial_application/constexpr_006.phpt @@ -0,0 +1,13 @@ +--TEST-- +PFA in constexpr: variadic placeholder must be after named params +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder must be last in %s on line %d diff --git a/Zend/tests/partial_application/constexpr_007.phpt b/Zend/tests/partial_application/constexpr_007.phpt new file mode 100644 index 000000000000..e932812ddf70 --- /dev/null +++ b/Zend/tests/partial_application/constexpr_007.phpt @@ -0,0 +1,13 @@ +--TEST-- +PFA in constexpr: variadic placeholder may only appear once +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder may only appear once in %s on line %d diff --git a/Zend/tests/partial_application/constexpr_008.phpt b/Zend/tests/partial_application/constexpr_008.phpt new file mode 100644 index 000000000000..c8f5b36ccb63 --- /dev/null +++ b/Zend/tests/partial_application/constexpr_008.phpt @@ -0,0 +1,13 @@ +--TEST-- +PFA in constexpr: variadic placeholder not allowed to be named +--FILE-- + +--EXPECTF-- +Parse error: syntax error, unexpected token "..." in %s on line %d diff --git a/Zend/tests/partial_application/constexpr_010.phpt b/Zend/tests/partial_application/constexpr_010.phpt new file mode 100644 index 000000000000..e4e05cc30a8e --- /dev/null +++ b/Zend/tests/partial_application/constexpr_010.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA in constexpr: error during partial creation +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +ArgumentCountError: Partial application of g() expects exactly 2 arguments, 1 given diff --git a/Zend/tests/partial_application/constexpr_011.phpt b/Zend/tests/partial_application/constexpr_011.phpt new file mode 100644 index 000000000000..a6480f214797 --- /dev/null +++ b/Zend/tests/partial_application/constexpr_011.phpt @@ -0,0 +1,43 @@ +--TEST-- +PFA in constexpr: error during arg list evaluation +--FILE-- +getMessage(), "\n"; +} + +try { + h(); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +try { + j(); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Error: Named parameter $a overwrites previous argument +Error: Call to undefined function invalid() +Error: Undefined constant "MISSING_CONST" diff --git a/Zend/tests/partial_application/constexpr_012.phpt b/Zend/tests/partial_application/constexpr_012.phpt new file mode 100644 index 000000000000..3c941ed6e13e --- /dev/null +++ b/Zend/tests/partial_application/constexpr_012.phpt @@ -0,0 +1,38 @@ +--TEST-- +PFA in constexpr: nested const expr +--FILE-- + +--EXPECTF-- +array(3) { + [0]=> + object(stdClass)#%d (0) { + } + [1]=> + object(Closure)#%d (2) { + ["function"]=> + string(1) "g" + ["parameter"]=> + array(3) { + ["$a"]=> + string(10) "" + ["$b"]=> + string(10) "" + ["$c"]=> + string(10) "" + } + } + [2]=> + int(1) +} diff --git a/Zend/tests/partial_application/constexpr_014.phpt b/Zend/tests/partial_application/constexpr_014.phpt new file mode 100644 index 000000000000..c75fdb60716b --- /dev/null +++ b/Zend/tests/partial_application/constexpr_014.phpt @@ -0,0 +1,45 @@ +--TEST-- +PFA in constexpr: sites +--FILE-- +getAttributes(A::class)[0]->newInstance()->fn)('hello')); +echo "# Method attr\n"; +var_dump((new ReflectionMethod(C::class, 'f')->getAttributes(A::class)[0]->newInstance()->fn)('hello')); +echo "# Method default value\n"; +var_dump((new C()->f())('hello')); + +?> +--EXPECT-- +# Class const +int(5) +# Const +int(5) +# Class attr +int(5) +# Method attr +int(5) +# Method default value +int(5) diff --git a/Zend/tests/partial_application/constexpr_015.inc b/Zend/tests/partial_application/constexpr_015.inc new file mode 100644 index 000000000000..bd9dd1674f89 --- /dev/null +++ b/Zend/tests/partial_application/constexpr_015.inc @@ -0,0 +1,20 @@ + diff --git a/Zend/tests/partial_application/constexpr_015.phpt b/Zend/tests/partial_application/constexpr_015.phpt new file mode 100644 index 000000000000..2449adb9e4b7 --- /dev/null +++ b/Zend/tests/partial_application/constexpr_015.phpt @@ -0,0 +1,33 @@ +--TEST-- +PFA in constexpr: preloading +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.preload={PWD}/constexpr_015.inc +--SKIPIF-- + +--FILE-- +getAttributes(A::class)[0]->newInstance()->fn)('hello')); +echo "# Method attr\n"; +var_dump((new ReflectionMethod(C::class, 'f')->getAttributes(A::class)[0]->newInstance()->fn)('hello')); +echo "# Method default value\n"; +var_dump((new C()->f())('hello')); + +?> +--EXPECT-- +# Class const +int(5) +# Class attr +int(5) +# Method attr +int(5) +# Method default value +int(5) diff --git a/Zend/tests/partial_application/pipe_optimization_004.phpt b/Zend/tests/partial_application/pipe_optimization_004.phpt index addee498d810..194f08a9c143 100644 --- a/Zend/tests/partial_application/pipe_optimization_004.phpt +++ b/Zend/tests/partial_application/pipe_optimization_004.phpt @@ -37,7 +37,7 @@ $_main: 0005 INIT_FCALL_BY_NAME 2 string("foo") 0006 SEND_PLACEHOLDER 1 0007 SEND_PLACEHOLDER 2 -0008 T1 = CALLABLE_CONVERT_PARTIAL %d +0008 T1 = CALLABLE_CONVERT_PARTIAL %d string("{closure:%s}") 0009 INIT_DYNAMIC_CALL 1 T1 0010 SEND_VAL_EX int(2) 1 0011 DO_FCALL diff --git a/Zend/tests/partial_application/pipe_optimization_007.phpt b/Zend/tests/partial_application/pipe_optimization_007.phpt index 09c3c765d237..be8d773c44ce 100644 --- a/Zend/tests/partial_application/pipe_optimization_007.phpt +++ b/Zend/tests/partial_application/pipe_optimization_007.phpt @@ -37,7 +37,7 @@ $_main: 0005 INIT_FCALL_BY_NAME 0 string("foo") 0006 SEND_PLACEHOLDER string("a") 0007 SEND_PLACEHOLDER string("b") -0008 T1 = CALLABLE_CONVERT_PARTIAL %d array(...) +0008 T1 = CALLABLE_CONVERT_PARTIAL %d string("{closure:%s}") array(...) 0009 INIT_DYNAMIC_CALL 1 T1 0010 SEND_VAL_EX int(2) 1 0011 DO_FCALL diff --git a/Zend/tests/partial_application/pipe_optimization_008.phpt b/Zend/tests/partial_application/pipe_optimization_008.phpt index 070074632c77..96f8d88815ca 100644 --- a/Zend/tests/partial_application/pipe_optimization_008.phpt +++ b/Zend/tests/partial_application/pipe_optimization_008.phpt @@ -36,7 +36,7 @@ $_main: 0004 DECLARE_FUNCTION string("foo") 0 0005 INIT_FCALL_BY_NAME 0 string("foo") 0006 SEND_VAL_EX int(1) string("a") -0007 T1 = CALLABLE_CONVERT_PARTIAL %d +0007 T1 = CALLABLE_CONVERT_PARTIAL %d string("{closure:%s}") 0008 INIT_DYNAMIC_CALL 1 T1 0009 SEND_VAL_EX int(2) 1 0010 DO_FCALL diff --git a/Zend/tests/partial_application/pipe_optimization_013.phpt b/Zend/tests/partial_application/pipe_optimization_013.phpt index 7d1a48b2f2ed..6c2be6728e73 100644 --- a/Zend/tests/partial_application/pipe_optimization_013.phpt +++ b/Zend/tests/partial_application/pipe_optimization_013.phpt @@ -32,7 +32,7 @@ $_main: 0004 DECLARE_FUNCTION string("foo") 0 0005 INIT_FCALL_BY_NAME 0 string("foo") 0006 SEND_VAL_EX int(2) string("b") -0007 T0 = CALLABLE_CONVERT_PARTIAL 3 +0007 T0 = CALLABLE_CONVERT_PARTIAL %d string("{closure:%s}") 0008 INIT_DYNAMIC_CALL 1 T0 0009 SEND_VAL_EX int(1) 1 0010 DO_FCALL diff --git a/Zend/tests/partial_application/references_004.phpt b/Zend/tests/partial_application/references_004.phpt index e5163c710da2..8d56d7dceaf4 100644 --- a/Zend/tests/partial_application/references_004.phpt +++ b/Zend/tests/partial_application/references_004.phpt @@ -1,5 +1,10 @@ --TEST-- PFA receives variadic param by ref if the actual function does +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.file_update_protection=0 --FILE-- static function {closure:%s:%d} ] { @@ %sreferences_004.php 13 - 13 - - Bound Variables [2] { - Variable #0 [ $a ] - Variable #1 [ $args2 ] + - Bound Variables [1] { + Variable #0 [ $args2 ] } - Parameters [2] { diff --git a/Zend/tests/partial_application/rfc_examples_const_expr.phpt b/Zend/tests/partial_application/rfc_examples_const_expr.phpt index e2fc57440256..23350f6116cf 100644 --- a/Zend/tests/partial_application/rfc_examples_const_expr.phpt +++ b/Zend/tests/partial_application/rfc_examples_const_expr.phpt @@ -1,7 +1,5 @@ --TEST-- PFA RFC examples: "Constant expressions" section ---XFAIL-- -PFA in constant expressions not implemented yet --FILE-- --EXPECTF-- object(Closure)#%d (5) { diff --git a/Zend/tests/partial_application/variation_debug_002.phpt b/Zend/tests/partial_application/variation_debug_002.phpt index a7c4c2d76e49..a4df317bbae9 100644 --- a/Zend/tests/partial_application/variation_debug_002.phpt +++ b/Zend/tests/partial_application/variation_debug_002.phpt @@ -1,8 +1,20 @@ --TEST-- PFA variation: var_dump(), internal function +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.file_update_protection=0 +--ENV-- +A=1 --FILE-- --EXPECTF-- object(Closure)#%d (5) { @@ -11,7 +23,7 @@ object(Closure)#%d (5) { ["file"]=> string(%d) "%svariation_debug_002.php" ["line"]=> - int(2) + int(7) ["static"]=> array(3) { ["array"]=> diff --git a/Zend/tests/partial_application/variation_variadics_001.phpt b/Zend/tests/partial_application/variation_variadics_001.phpt index 850f0eda149d..43b4ff23e29f 100644 --- a/Zend/tests/partial_application/variation_variadics_001.phpt +++ b/Zend/tests/partial_application/variation_variadics_001.phpt @@ -1,5 +1,12 @@ --TEST-- PFA variation: variadics, user function +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.file_update_protection=0 +--ENV-- +A=1 --FILE-- static function {closure:%s:%d} ] { @@ %s 6 - 6 - - Bound Variables [2] { - Variable #0 [ $a ] - Variable #1 [ $b2 ] - } - - Parameters [1] { Parameter #0 [ ...$b ] } diff --git a/Zend/tests/partial_application/variation_variadics_002.phpt b/Zend/tests/partial_application/variation_variadics_002.phpt index 21d8169fc42c..33330723f31d 100644 --- a/Zend/tests/partial_application/variation_variadics_002.phpt +++ b/Zend/tests/partial_application/variation_variadics_002.phpt @@ -1,5 +1,10 @@ --TEST-- PFA variation: variadics, internal function +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.file_update_protection=0 --FILE-- static function {closure:%s:%d} ] { @@ %svariation_variadics_002.php 2 - 2 - - Bound Variables [2] { - Variable #0 [ $format ] - Variable #1 [ $values2 ] - } - - Parameters [1] { Parameter #0 [ mixed ...$values ] } diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 1b293c832c91..a17cb3d91b91 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -25,6 +25,7 @@ #include "zend_closures.h" #include "zend_constants.h" #include "zend_enum.h" +#include "zend_partial.h" ZEND_API zend_ast_process_t zend_ast_process = NULL; @@ -60,6 +61,8 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(zend_ast *args) { ast->kind = ZEND_AST_CALLABLE_CONVERT; ast->attr = 0; ast->lineno = CG(zend_lineno); + ast->filename = NULL; + ast->name = NULL; ast->args = args; ZEND_MAP_PTR_INIT(ast->fptr, NULL); @@ -671,6 +674,82 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex( return r; } +static zend_execute_data *zend_ast_evaluate_arg_list( + zend_function *func, + zend_ast_list *args_ast, + zend_class_entry *scope, + void *object_or_called_scope, + bool *short_circuited_ptr, + zend_ast_evaluate_ctx *ctx, + zend_array **named_positions_ptr, + bool *uses_variadic_placeholder +) { + zend_execute_data *frame = zend_vm_stack_push_call_frame_ex( + zend_vm_calc_used_stack(args_ast->children, func), + 0, func, 0, object_or_called_scope); + + for (uint32_t i = 0; i < args_ast->children; i++) { + zend_ast *arg_ast = args_ast->child[i]; + uint32_t arg_num = i + 1; + zval *arg; + zend_string *arg_name = NULL; + + if (arg_ast->kind == ZEND_AST_NAMED_ARG) { + void *cache_slot[2] = {0}; + arg_name = zend_ast_get_str(arg_ast->child[0]); + arg = zend_handle_named_arg(&frame, arg_name, &arg_num, (void**)&cache_slot); + if (!arg) { + goto fail; + } + if (named_positions_ptr) { + if (!*named_positions_ptr) { + *named_positions_ptr = zend_new_array(0); + } + zval tmp; + ZVAL_LONG(&tmp, zend_hash_num_elements(*named_positions_ptr)); + zend_hash_add(*named_positions_ptr, arg_name, &tmp); + } + arg_ast = arg_ast->child[1]; + } else { + arg = ZEND_CALL_VAR_NUM(frame, ZEND_CALL_NUM_ARGS(frame)); + } + + if (arg_ast->kind == ZEND_AST_PLACEHOLDER_ARG) { + if (arg_ast->attr == ZEND_PLACEHOLDER_VARIADIC) { + if (uses_variadic_placeholder) { + *uses_variadic_placeholder = true; + } + continue; + } else { + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + } + } else { + if (zend_ast_evaluate_ex(arg, arg_ast, scope, short_circuited_ptr, ctx) == FAILURE) { + ZVAL_UNDEF(arg); + goto fail; + } + } + if (!arg_name) { + ZEND_CALL_NUM_ARGS(frame)++; + } + } + + return frame; + +fail: + for (uint32_t i = 0, num_args = ZEND_CALL_NUM_ARGS(frame); i < num_args; i++) { + zval_ptr_dtor(ZEND_CALL_VAR_NUM(frame, i)); + } + if (ZEND_CALL_INFO(frame) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_destroy(frame->extra_named_params); + } + zend_vm_stack_free_call_frame(frame); + if (named_positions_ptr && *named_positions_ptr) { + zend_array_destroy(*named_positions_ptr); + } + return NULL; +} + static zend_result ZEND_FASTCALL zend_ast_evaluate_inner( zval *result, zend_ast *ast, @@ -1155,10 +1234,6 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner( zend_ast_list *args = zend_ast_get_list(fcc_ast->args); ZEND_ASSERT(args->children > 0); - if (args->children != 1 || args->child[0]->attr != ZEND_PLACEHOLDER_VARIADIC) { - /* TODO: PFAs */ - zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations"); - } switch (ast->kind) { case ZEND_AST_CALL: { @@ -1248,9 +1323,46 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner( default: ZEND_UNREACHABLE(); } - zend_create_fake_closure(result, fptr, fptr->common.scope, called_scope, NULL); + bool is_fcc = args->children == 1 && args->child[0]->attr == ZEND_PLACEHOLDER_VARIADIC; - return SUCCESS; + if (is_fcc) { + zend_create_fake_closure(result, fptr, fptr->common.scope, called_scope, NULL); + return SUCCESS; + } + + zend_array *named_positions = NULL; + bool uses_variadic_placeholder = false; + zend_execute_data *frame = zend_ast_evaluate_arg_list( + fptr, args, scope, called_scope, short_circuited_ptr, ctx, + &named_positions, &uses_variadic_placeholder); + if (!frame) { + ZEND_ASSERT(EG(exception)); + return FAILURE; + } + + void *cache_slot[2] = {0}; + zend_array *extra_named_params = ZEND_CALL_INFO(frame) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS + ? frame->extra_named_params + : NULL; + uint32_t flags = (fcc_ast->attr & ZEND_PARTIAL_CACHEABLE_IN_SHM); + if (uses_variadic_placeholder) { + flags |= ZEND_PARTIAL_USES_VARIADIC_PLACEHOLDER; + } + zend_partial_create(result, &frame->This, fptr, + ZEND_CALL_NUM_ARGS(frame), ZEND_CALL_ARG(frame, 1), + extra_named_params, named_positions, + fcc_ast->filename, &ast->lineno, + (void**)cache_slot, fcc_ast->name, flags, 0); + + if (named_positions) { + zend_array_release(named_positions); + } + if (extra_named_params) { + zend_array_release(extra_named_params); + } + zend_vm_stack_free_call_frame(frame); + + return EG(exception) ? FAILURE : SUCCESS; } case ZEND_AST_OP_ARRAY: { @@ -1425,6 +1537,16 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf) new->kind = old->kind; new->attr = old->attr; new->lineno = old->lineno; + if (old->filename) { + new->filename = zend_string_copy(old->filename); + } else { + new->filename = NULL; + } + if (old->name) { + new->name = zend_string_copy(old->name); + } else { + new->name = NULL; + } ZEND_MAP_PTR_INIT(new->fptr, ZEND_MAP_PTR(old->fptr)); buf = (void*)((char*)buf + sizeof(zend_ast_fcc)); new->args = buf; @@ -1525,6 +1647,12 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) } else if (EXPECTED(ast->kind == ZEND_AST_CALLABLE_CONVERT)) { zend_ast_fcc *fcc_ast = (zend_ast_fcc*) ast; + if (fcc_ast->filename) { + zend_string_release_ex(fcc_ast->filename, 0); + } + if (fcc_ast->name) { + zend_string_release_ex(fcc_ast->name, 0); + } ast = fcc_ast->args; goto tail_call; } @@ -1543,6 +1671,9 @@ ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn, void *contex for (i = 0; i < list->children; ++i) { fn(&list->child[i], context); } + } else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) { + zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast; + fn(&fcc_ast->args, context); } else if (zend_ast_is_decl(ast)) { /* Not implemented. */ ZEND_UNREACHABLE(); diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 26f54102a143..5d646f428c1b 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -233,6 +233,8 @@ typedef struct _zend_ast_fcc { zend_ast_kind kind; /* Type of the node (ZEND_AST_* enum constant) */ zend_ast_attr attr; /* Additional attribute, use depending on node type */ uint32_t lineno; /* Line number */ + zend_string *filename; + zend_string *name; zend_ast *args; ZEND_MAP_PTR_DEF(zend_function *, fptr); } zend_ast_fcc; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 2466710f9cee..9c80cccb7cb9 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -40,6 +40,7 @@ #include "zend_call_stack.h" #include "zend_frameless_function.h" #include "zend_property_hooks.h" +#include "zend_partial.h" #define SET_NODE(target, src) do { \ target ## _type = (src)->op_type; \ @@ -4088,7 +4089,50 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, const zend_function *f } /* }}} */ -static void zend_compile_call_partial(znode *result, uint32_t arg_count, +static zend_string *zend_compile_partial_name(const zend_op_array *declaring_op_array, + const uint32_t declaring_lineno) +{ + /* We attempt to generate a name that hints at where the PFA was created, + * similarly to Closures (GH-13550). + * We do not attempt to make the name unique. */ + + zend_string *filename = declaring_op_array->filename; + uint32_t start_lineno = declaring_lineno; + + zend_string *class = zend_empty_string; + zend_string *separator = zend_empty_string; + zend_string *function = filename; + const char *parens = ""; + + if (declaring_op_array->function_name) { + function = declaring_op_array->function_name; + if (declaring_op_array->fn_flags & ZEND_ACC_CLOSURE) { + /* If the parent function is a closure, don't redundantly + * add the classname and parentheses. */ + } else { + parens = "()"; + + if (declaring_op_array->scope && declaring_op_array->scope->name) { + class = declaring_op_array->scope->name; + separator = ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM); + } + } + } + + zend_string *name = zend_strpprintf_unchecked( + 0, + "{closure:pfa:%S%S%S%s:%" PRIu32 "}", + class, + separator, + function, + parens, + start_lineno + ); + + return name; +} + +static void zend_compile_call_partial(znode *result, zend_ast_fcc *fcc_ast, uint32_t arg_count, bool may_have_extra_named_args, bool uses_variadic_placeholder, zval *named_positions, uint32_t opnum_init, const zend_function *fbc) { @@ -4105,18 +4149,21 @@ static void zend_compile_call_partial(znode *result, uint32_t arg_count, zend_op *opline = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT_PARTIAL, NULL, NULL); - opline->op1.num = zend_alloc_cache_slots(2); + opline->extended_value = zend_alloc_cache_slots(2); - if (may_have_extra_named_args) { - opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS; - } if (uses_variadic_placeholder) { - opline->extended_value |= ZEND_FCALL_USES_VARIADIC_PLACEHOLDER; + opline->extended_value |= ZEND_PARTIAL_USES_VARIADIC_PLACEHOLDER; } + zend_string *name = zend_compile_partial_name(CG(active_op_array), opline->lineno); + opline->op1.constant = zend_add_literal_string(&name); + opline->op1_type = IS_CONST; + if (!Z_ISUNDEF_P(named_positions)) { opline->op2.constant = zend_add_literal(named_positions); opline->op2_type = IS_CONST; + } else { + opline->op2.num = 0; } } @@ -4156,7 +4203,8 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const ze return true; } - args_ast = ((zend_ast_fcc*)args_ast)->args; + zend_ast_fcc *fcc_ast = (zend_ast_fcc*)args_ast; + args_ast = fcc_ast->args; bool may_have_extra_named_args; bool uses_variadic_placeholder; @@ -4168,7 +4216,7 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const ze &may_have_extra_named_args, true, &uses_variadic_placeholder, &named_positions); - zend_compile_call_partial(result, arg_count, + zend_compile_call_partial(result, fcc_ast, arg_count, may_have_extra_named_args, uses_variadic_placeholder, &named_positions, opnum_init, fbc); @@ -11856,7 +11904,8 @@ static bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */ || kind == ZEND_AST_NAMED_ARG || kind == ZEND_AST_PROP || kind == ZEND_AST_NULLSAFE_PROP || kind == ZEND_AST_CLOSURE - || kind == ZEND_AST_CALL || kind == ZEND_AST_STATIC_CALL || kind == ZEND_AST_CALLABLE_CONVERT; + || kind == ZEND_AST_CALL || kind == ZEND_AST_STATIC_CALL || kind == ZEND_AST_CALLABLE_CONVERT + || kind == ZEND_AST_PLACEHOLDER_ARG; } /* }}} */ @@ -12021,24 +12070,20 @@ static void zend_compile_const_expr_closure(zend_ast **ast_ptr) static void zend_compile_const_expr_fcc(zend_ast **ast_ptr) { - zend_ast **args_ast; - switch ((*ast_ptr)->kind) { - case ZEND_AST_CALL: - args_ast = &(*ast_ptr)->child[1]; - break; - case ZEND_AST_STATIC_CALL: - args_ast = &(*ast_ptr)->child[2]; - break; - default: ZEND_UNREACHABLE(); - } - if ((*args_ast)->kind != ZEND_AST_CALLABLE_CONVERT) { + zend_ast *args_ast = zend_ast_call_get_args(*ast_ptr); + + if (args_ast->kind != ZEND_AST_CALLABLE_CONVERT) { zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations"); } - zend_ast_list *args = zend_ast_get_list(((zend_ast_fcc*)*args_ast)->args); - if (args->children != 1 || args->child[0]->attr != ZEND_PLACEHOLDER_VARIADIC) { - // TODO: PFAs - zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations"); + zend_ast_fcc *fcc = (zend_ast_fcc*)args_ast; + + zend_ast_list *args = zend_ast_get_list(fcc->args); + bool is_fcc = args->children == 1 && args->child[0]->attr == ZEND_PLACEHOLDER_VARIADIC; + + if (!is_fcc) { + fcc->filename = zend_string_copy(CG(active_op_array)->filename); + fcc->name = zend_compile_partial_name(CG(active_op_array), fcc->lineno); } switch ((*ast_ptr)->kind) { @@ -12080,6 +12125,7 @@ static void zend_compile_const_expr_args(zend_ast **ast_ptr) { zend_ast_list *list = zend_ast_get_list(*ast_ptr); bool uses_named_args = false; + bool uses_variadic_placeholder = false; for (uint32_t i = 0; i < list->children; i++) { const zend_ast *arg = list->child[i]; if (arg->kind == ZEND_AST_UNPACK) { @@ -12088,11 +12134,31 @@ static void zend_compile_const_expr_args(zend_ast **ast_ptr) } if (arg->kind == ZEND_AST_NAMED_ARG) { uses_named_args = true; - } else if (uses_named_args) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot use positional argument after named argument"); + if (uses_variadic_placeholder) { + zend_error_noreturn(E_COMPILE_ERROR, "Variadic placeholder must be last"); + } + } else { + bool is_variadic_placeholder = arg->kind == ZEND_AST_PLACEHOLDER_ARG + && arg->attr == ZEND_PLACEHOLDER_VARIADIC; + + if (uses_named_args && !is_variadic_placeholder) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use positional argument after named argument"); + } + + if (uses_variadic_placeholder) { + if (is_variadic_placeholder) { + zend_error_noreturn(E_COMPILE_ERROR, "Variadic placeholder may only appear once"); + } else { + zend_error_noreturn(E_COMPILE_ERROR, "Variadic placeholder must be last"); + } + } + + if (is_variadic_placeholder) { + uses_variadic_placeholder = true; + } } } + if (uses_named_args) { list->attr = 1; } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 69d7aeb2f373..3d4e6f3c3f9f 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -1121,7 +1121,6 @@ ZEND_API zend_string *zend_type_to_string(zend_type type); #define ZEND_THROW_IS_EXPR 1u #define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS (1<<0) -#define ZEND_FCALL_USES_VARIADIC_PLACEHOLDER (1<<1) /* The send mode, the is_variadic, the is_promoted, and the is_tentative flags are stored as part of zend_type */ #define _ZEND_SEND_MODE_SHIFT _ZEND_TYPE_EXTRA_FLAGS_SHIFT diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c index e7a3e1c92b58..ec60383a2bc8 100644 --- a/Zend/zend_partial.c +++ b/Zend/zend_partial.c @@ -51,6 +51,7 @@ #include "zend_closures.h" #include "zend_attributes.h" #include "zend_exceptions.h" +#include "zend_partial.h" #include "ext/opcache/ZendAccelerator.h" static zend_always_inline bool Z_IS_PLACEHOLDER_P(const zval *p) { @@ -65,6 +66,14 @@ static zend_always_inline bool zp_is_non_static_closure(const zend_function *fun return ((function->common.fn_flags & (ZEND_ACC_STATIC|ZEND_ACC_CLOSURE)) == ZEND_ACC_CLOSURE); } +/* Whether argument at offset 'offset' is const. Such arguments can be burned into the generated op_array */ +static inline bool zp_is_const_arg(uint32_t const_args, uint32_t offset) { + if (offset < sizeof(const_args) * CHAR_BIT) { + return const_args & (UINT32_C(1) << offset); + } + return false; +} + static zend_never_inline ZEND_COLD void zp_args_underflow( const zend_function *function, uint32_t args, uint32_t expected) { @@ -178,7 +187,7 @@ static zend_string *zp_get_func_param_name(const zend_function *function, uint32 * including params and used vars. */ static zp_names *zp_assign_names(uint32_t argc, zval *argv, zend_function *function, bool variadic_partial, - zend_array *extra_named_params) + zend_array *extra_named_params, uint32_t const_args) { zp_names *names = zend_arena_calloc(&CG(ast_arena), 1, zend_safe_address_guarded(argc, sizeof(*names->params), offsetof(zp_names, params))); @@ -225,7 +234,7 @@ static zp_names *zp_assign_names(uint32_t argc, zval *argv, /* Assign names for pre-bound params (lexical vars). * There may be clashes, we ensure to generate unique names. */ for (uint32_t offset = 0; offset < argc; offset++) { - if (Z_IS_PLACEHOLDER_P(&argv[offset]) || Z_ISUNDEF(argv[offset])) { + if (Z_IS_PLACEHOLDER_P(&argv[offset]) || Z_ISUNDEF(argv[offset]) || zp_is_const_arg(const_args, offset)) { continue; } uint32_t n = 2; @@ -502,49 +511,6 @@ static zend_ast *zp_param_attributes_to_ast(zend_function *function, return attributes_ast; } -static zend_string *zp_pfa_name(const zend_op_array *declaring_op_array, - const zend_op *declaring_opline) -{ - /* We attempt to generate a name that hints at where the PFA was created, - * similarly to Closures (GH-13550). - * We do not attempt to make the name unique. */ - - zend_string *filename = declaring_op_array->filename; - uint32_t start_lineno = declaring_opline->lineno; - - zend_string *class = zend_empty_string; - zend_string *separator = zend_empty_string; - zend_string *function = filename; - const char *parens = ""; - - if (declaring_op_array->function_name) { - function = declaring_op_array->function_name; - if (declaring_op_array->fn_flags & ZEND_ACC_CLOSURE) { - /* If the parent function is a closure, don't redundantly - * add the classname and parentheses. */ - } else { - parens = "()"; - - if (declaring_op_array->scope && declaring_op_array->scope->name) { - class = declaring_op_array->scope->name; - separator = ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM); - } - } - } - - zend_string *name = zend_strpprintf_unchecked( - 0, - "{closure:pfa:%S%S%S%s:%" PRIu32 "}", - class, - separator, - function, - parens, - start_lineno - ); - - return name; -} - /* Generate the AST for calling the actual function */ static zend_ast *zp_compile_forwarding_call( zval *this_ptr, zend_function *function, @@ -552,7 +518,7 @@ static zend_ast *zp_compile_forwarding_call( zp_names *var_names, bool uses_variadic_placeholder, uint32_t num_args, zend_class_entry *called_scope, zend_type return_type, bool forward_superfluous_args, - zend_ast *stmts_ast) + zend_ast *stmts_ast, uint32_t const_args) { bool is_assert = zend_string_equals(function->common.function_name, ZSTR_KNOWN(ZEND_STR_ASSERT)); @@ -593,6 +559,9 @@ static zend_ast *zp_compile_forwarding_call( default_value_ast = zend_ast_create_zval(&default_value); } args_ast = zend_ast_list_add(args_ast, default_value_ast); + } else if (zp_is_const_arg(const_args, offset)) { + ZEND_ASSERT(Z_TYPE(argv[offset]) < IS_OBJECT); + args_ast = zend_ast_list_add(args_ast, zend_ast_create_zval(&argv[offset])); } else { args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_VAR, zend_ast_create_zval_from_str(zend_string_copy(var_names->params[offset])))); @@ -701,9 +670,9 @@ static uint32_t zp_compute_num_required(const zend_function *function, static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, uint32_t argc, zval *argv, zend_array *extra_named_params, const zend_array *named_positions, - const zend_op_array *declaring_op_array, - const zend_op *declaring_opline, void **cache_slot, - bool uses_variadic_placeholder) { + zend_string *declaring_filename, + const uint32_t *declaring_lineno_ptr, void **cache_slot, + zend_string *pfa_name, uint32_t flags, uint32_t const_args) { zend_op_array *op_array = NULL; @@ -713,6 +682,8 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, return NULL; } + bool uses_variadic_placeholder = flags & ZEND_PARTIAL_USES_VARIADIC_PLACEHOLDER; + if (UNEXPECTED(zp_args_check(function, argc, argv, extra_named_params, uses_variadic_placeholder) != SUCCESS)) { ZEND_ASSERT(EG(exception)); return NULL; @@ -811,7 +782,7 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, /* Assign variable names */ zp_names *var_names = zp_assign_names(argc, argv, function, - uses_variadic_placeholder, extra_named_params); + uses_variadic_placeholder, extra_named_params, const_args); /* Generate AST */ @@ -865,15 +836,17 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, default_value_ast, attributes_ast, NULL, NULL); } else if (!Z_ISUNDEF(argv[offset])) { - // TODO: If the pre-bound parameter is a literal, it can be a - // literal in the function body instead of a lexical var. - zend_ast *lexical_var_ast = zend_ast_create_zval_from_str( - zend_string_copy(var_names->params[offset])); - if (zp_arg_must_be_sent_by_ref(function, offset+1)) { - lexical_var_ast->attr = ZEND_BIND_REF; + if (zp_is_const_arg(const_args, offset)) { + /* Will be burned into the op_array */ + } else { + zend_ast *lexical_var_ast = zend_ast_create_zval_from_str( + zend_string_copy(var_names->params[offset])); + if (zp_arg_must_be_sent_by_ref(function, offset+1)) { + lexical_var_ast->attr = ZEND_BIND_REF; + } + lexical_vars_ast = zend_ast_list_add( + lexical_vars_ast, lexical_var_ast); } - lexical_vars_ast = zend_ast_list_add( - lexical_vars_ast, lexical_var_ast); } } @@ -934,7 +907,7 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, no_forwarding_ast = zp_compile_forwarding_call(this_ptr, function, argc, argv, extra_named_params, var_names, uses_variadic_placeholder, num_params, - called_scope, return_type, false, no_forwarding_ast); + called_scope, return_type, false, no_forwarding_ast, const_args); if (!no_forwarding_ast) { ZEND_ASSERT(EG(exception)); @@ -944,7 +917,7 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, forwarding_ast = zp_compile_forwarding_call(this_ptr, function, argc, argv, extra_named_params, var_names, uses_variadic_placeholder, num_params, - called_scope, return_type, true, forwarding_ast); + called_scope, return_type, true, forwarding_ast, const_args); if (!forwarding_ast) { ZEND_ASSERT(EG(exception)); @@ -971,7 +944,7 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, stmts_ast = zp_compile_forwarding_call(this_ptr, function, argc, argv, extra_named_params, var_names, uses_variadic_placeholder, num_params, - called_scope, return_type, false, stmts_ast); + called_scope, return_type, false, stmts_ast, const_args); if (!stmts_ast) { ZEND_ASSERT(EG(exception)); @@ -1012,10 +985,8 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, } #endif - zend_string *pfa_name = zp_pfa_name(declaring_op_array, declaring_opline); - - op_array = zend_accel_compile_pfa(closure_ast, declaring_op_array, - declaring_opline, function, pfa_name); + op_array = zend_accel_compile_pfa(closure_ast, declaring_filename, + declaring_lineno_ptr, function, pfa_name, flags & ZEND_PARTIAL_CACHEABLE_IN_SHM); zend_ast_destroy(closure_ast); @@ -1040,9 +1011,9 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, static const zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *function, uint32_t argc, zval *argv, zend_array *extra_named_params, const zend_array *named_positions, - const zend_op_array *declaring_op_array, - const zend_op *declaring_opline, void **cache_slot, - bool uses_variadic_placeholder) { + zend_string *declaring_filename, + const uint32_t *declaring_lineno_ptr, void **cache_slot, + zend_string *pfa_name, uint32_t flags, uint32_t const_args) { if (EXPECTED(function->type == ZEND_INTERNAL_FUNCTION ? cache_slot[0] == function @@ -1051,13 +1022,13 @@ static const zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *funct return cache_slot[1]; } - const zend_op_array *op_array = zend_accel_pfa_cache_get(declaring_op_array, - declaring_opline, function); + const zend_op_array *op_array = zend_accel_pfa_cache_get(declaring_lineno_ptr, function, + flags & ZEND_PARTIAL_CACHEABLE_IN_SHM); if (UNEXPECTED(!op_array)) { op_array = zp_compile(this_ptr, function, argc, argv, - extra_named_params, named_positions, declaring_op_array, declaring_opline, - cache_slot, uses_variadic_placeholder); + extra_named_params, named_positions, declaring_filename, declaring_lineno_ptr, + cache_slot, pfa_name, flags, const_args); } if (EXPECTED(op_array) && !(function->common.fn_flags & ZEND_ACC_NEVER_CACHE)) { @@ -1079,7 +1050,7 @@ static void zp_free_unbound_args(uint32_t start, uint32_t argc, zval *argv) /* Bind pre-bound arguments as lexical vars */ static void zp_bind(zval *result, zend_function *function, uint32_t argc, zval *argv, - zend_array *extra_named_params) { + zend_array *extra_named_params, uint32_t const_args) { zend_arg_info *arg_infos = function->common.arg_info; uint32_t bind_offset = 0; @@ -1094,7 +1065,7 @@ static void zp_bind(zval *result, zend_function *function, uint32_t argc, zval * for (uint32_t offset = 0; offset < argc; offset++) { zval *var = &argv[offset]; - if (Z_IS_PLACEHOLDER_P(var) || Z_ISUNDEF_P(var)) { + if (Z_IS_PLACEHOLDER_P(var) || Z_ISUNDEF_P(var) || zp_is_const_arg(const_args, offset)) { continue; } zend_arg_info *arg_info; @@ -1135,14 +1106,16 @@ static void zp_bind(zval *result, zend_function *function, uint32_t argc, zval * void zend_partial_create(zval *result, zval *this_ptr, zend_function *function, uint32_t argc, zval *argv, zend_array *extra_named_params, const zend_array *named_positions, - const zend_op_array *declaring_op_array, - const zend_op *declaring_opline, void **cache_slot, - bool uses_variadic_placeholder) { + zend_string *declaring_filename, + const uint32_t *declaring_lineno_ptr, void **cache_slot, + zend_string *pfa_name, uint32_t flags, uint32_t const_args) { + + ZEND_ASSERT(pfa_name); const zend_op_array *op_array = zp_get_op_array(this_ptr, function, argc, argv, extra_named_params, named_positions, - declaring_op_array, declaring_opline, - cache_slot, uses_variadic_placeholder); + declaring_filename, declaring_lineno_ptr, + cache_slot, pfa_name, flags, const_args); if (UNEXPECTED(!op_array)) { ZEND_ASSERT(EG(exception)); @@ -1170,7 +1143,7 @@ void zend_partial_create(zval *result, zval *this_ptr, zend_function *function, function->common.scope, called_scope, &object, (function->common.fn_flags & ZEND_ACC_CLOSURE) != 0); - zp_bind(result, function, argc, argv, extra_named_params); + zp_bind(result, function, argc, argv, extra_named_params, const_args); } void zend_partial_op_array_dtor(zval *pDest) diff --git a/Zend/zend_partial.h b/Zend/zend_partial.h index 3c47305ff543..d3fcdae6afc8 100644 --- a/Zend/zend_partial.h +++ b/Zend/zend_partial.h @@ -21,12 +21,22 @@ BEGIN_EXTERN_C() +#define ZEND_PARTIAL_USES_VARIADIC_PLACEHOLDER (1<<0) +/* Whether the PFA is cacheable in SHM. This flag is set only when the op_array that declares the PFA is itself in SHM, + * for two reasons: Avoids SHM churn, and ensures that the PFA cache key is unique. */ +#define ZEND_PARTIAL_CACHEABLE_IN_SHM (1<<1) +#define ZEND_PARTIAL_FLAGS (ZEND_PARTIAL_USES_VARIADIC_PLACEHOLDER|ZEND_PARTIAL_CACHEABLE_IN_SHM) + +/* Create a partial application of 'function' + * 'declaring_lineno_ptr' should be a pointer the zend_op.lineno or + * zend_ast.lineno that declares the PFA. The address is used to build a cache + * key. */ void zend_partial_create(zval *result, zval *this_ptr, zend_function *function, uint32_t argc, zval *argv, zend_array *extra_named_params, const zend_array *named_positions, - const zend_op_array *declaring_op_array, - const zend_op *declaring_opline, void **cache_slot, - bool uses_variadic_placeholder); + zend_string *declaring_filename, + const uint32_t *declaring_lineno_ptr, void **cache_slot, + zend_string *pfa_name, uint32_t flags, uint32_t const_args); void zend_partial_op_array_dtor(zval *pDest); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index cd74145799b3..d14230514b34 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -9866,14 +9866,22 @@ ZEND_VM_HANDLER(202, ZEND_CALLABLE_CONVERT, UNUSED, UNUSED, NUM|CACHE_SLOT) ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HANDLER(212, ZEND_CALLABLE_CONVERT_PARTIAL, CACHE_SLOT, CONST|UNUSED, NUM) +ZEND_VM_HANDLER(212, ZEND_CALLABLE_CONVERT_PARTIAL, CONST, CONST|UNUSED, NUM) { USE_OPLINE SAVE_OPLINE(); zend_execute_data *call = EX(call); - void **cache_slot = CACHE_ADDR(opline->op1.num); + void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_PARTIAL_FLAGS); zval *named_positions = GET_OP2_ZVAL_PTR(); + zend_string *pfa_name = Z_STR_P(GET_OP1_ZVAL_PTR()); + uint32_t const_args; + + if (OP2_TYPE == IS_UNUSED) { + const_args = opline->op2.num; + } else { + const_args = Z_EXTRA_P(named_positions); + } zend_partial_create(EX_VAR(opline->result.var), &call->This, call->func, @@ -9881,8 +9889,8 @@ ZEND_VM_HANDLER(212, ZEND_CALLABLE_CONVERT_PARTIAL, CACHE_SLOT, CONST|UNUSED, NU (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? call->extra_named_params : NULL, OP2_TYPE == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, - &EX(func)->op_array, opline, cache_slot, - opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + EX(func)->op_array.filename, &opline->lineno, cache_slot, + pfa_name, opline->extended_value & ZEND_PARTIAL_FLAGS, const_args); if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { zend_array_release(call->extra_named_params); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 8d2e0670ee3b..53bcdccd9719 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4273,45 +4273,6 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_R ZEND_VM_NEXT_OPCODE(); } -static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - SAVE_OPLINE(); - - zend_execute_data *call = EX(call); - void **cache_slot = CACHE_ADDR(opline->op1.num); - zval *named_positions = RT_CONSTANT(opline, opline->op2); - - zend_partial_create(EX_VAR(opline->result.var), - &call->This, call->func, - ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), - (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? - call->extra_named_params : NULL, - IS_CONST == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, - &EX(func)->op_array, opline, cache_slot, - opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); - - if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { - zend_array_release(call->extra_named_params); - } - - if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - zend_free_trampoline(call->func); - } - - if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { - OBJ_RELEASE(Z_OBJ(call->This)); - } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { - OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); - } - - EX(call) = call->prev_execute_data; - - zend_vm_stack_free_call_frame(call); - - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); -} - static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_DYNAMIC_CALL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -4456,45 +4417,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_RECV_VARIADIC ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } -static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - SAVE_OPLINE(); - - zend_execute_data *call = EX(call); - void **cache_slot = CACHE_ADDR(opline->op1.num); - zval *named_positions = NULL; - - zend_partial_create(EX_VAR(opline->result.var), - &call->This, call->func, - ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), - (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? - call->extra_named_params : NULL, - IS_UNUSED == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, - &EX(func)->op_array, opline, cache_slot, - opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); - - if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { - zend_array_release(call->extra_named_params); - } - - if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - zend_free_trampoline(call->func); - } - - if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { - OBJ_RELEASE(Z_OBJ(call->This)); - } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { - OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); - } - - EX(call) = call->prev_execute_data; - - zend_vm_stack_free_call_frame(call); - - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); -} - static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -8741,6 +8663,53 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ ZEND_VM_SMART_BRANCH(0, 1); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_PARTIAL_FLAGS); + zval *named_positions = RT_CONSTANT(opline, opline->op2); + zend_string *pfa_name = Z_STR_P(RT_CONSTANT(opline, opline->op1)); + uint32_t const_args; + + if (IS_CONST == IS_UNUSED) { + const_args = opline->op2.num; + } else { + const_args = Z_EXTRA_P(named_positions); + } + + zend_partial_create(EX_VAR(opline->result.var), + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? + call->extra_named_params : NULL, + IS_CONST == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + EX(func)->op_array.filename, &opline->lineno, cache_slot, + pfa_name, opline->extended_value & ZEND_PARTIAL_FLAGS, const_args); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ADD_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -12067,6 +12036,53 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FUNC_GET_ARGS } /* Contrary to what its name indicates, ZEND_COPY_TMP may receive and define references. */ +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_PARTIAL_FLAGS); + zval *named_positions = NULL; + zend_string *pfa_name = Z_STR_P(RT_CONSTANT(opline, opline->op1)); + uint32_t const_args; + + if (IS_UNUSED == IS_UNUSED) { + const_args = opline->op2.num; + } else { + const_args = Z_EXTRA_P(named_positions); + } + + zend_partial_create(EX_VAR(opline->result.var), + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? + call->extra_named_params : NULL, + IS_UNUSED == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + EX(func)->op_array.filename, &opline->lineno, cache_slot, + pfa_name, opline->extended_value & ZEND_PARTIAL_FLAGS, const_args); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -57089,45 +57105,6 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_RECV_I ZEND_VM_NEXT_OPCODE(); } -static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - SAVE_OPLINE(); - - zend_execute_data *call = EX(call); - void **cache_slot = CACHE_ADDR(opline->op1.num); - zval *named_positions = RT_CONSTANT(opline, opline->op2); - - zend_partial_create(EX_VAR(opline->result.var), - &call->This, call->func, - ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), - (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? - call->extra_named_params : NULL, - IS_CONST == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, - &EX(func)->op_array, opline, cache_slot, - opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); - - if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { - zend_array_release(call->extra_named_params); - } - - if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - zend_free_trampoline(call->func); - } - - if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { - OBJ_RELEASE(Z_OBJ(call->This)); - } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { - OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); - } - - EX(call) = call->prev_execute_data; - - zend_vm_stack_free_call_frame(call); - - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); -} - static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_DYNAMIC_CALL_SPEC_TMP_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -57272,45 +57249,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_RECV_VARIADIC_SPEC ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } -static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - SAVE_OPLINE(); - - zend_execute_data *call = EX(call); - void **cache_slot = CACHE_ADDR(opline->op1.num); - zval *named_positions = NULL; - - zend_partial_create(EX_VAR(opline->result.var), - &call->This, call->func, - ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), - (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? - call->extra_named_params : NULL, - IS_UNUSED == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, - &EX(func)->op_array, opline, cache_slot, - opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); - - if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { - zend_array_release(call->extra_named_params); - } - - if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - zend_free_trampoline(call->func); - } - - if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { - OBJ_RELEASE(Z_OBJ(call->This)); - } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { - OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); - } - - EX(call) = call->prev_execute_data; - - zend_vm_stack_free_call_frame(call); - - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); -} - static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -61557,6 +61495,53 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_IN_AR ZEND_VM_SMART_BRANCH(0, 1); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_PARTIAL_FLAGS); + zval *named_positions = RT_CONSTANT(opline, opline->op2); + zend_string *pfa_name = Z_STR_P(RT_CONSTANT(opline, opline->op1)); + uint32_t const_args; + + if (IS_CONST == IS_UNUSED) { + const_args = opline->op2.num; + } else { + const_args = Z_EXTRA_P(named_positions); + } + + zend_partial_create(EX_VAR(opline->result.var), + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? + call->extra_named_params : NULL, + IS_CONST == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + EX(func)->op_array.filename, &opline->lineno, cache_slot, + pfa_name, opline->extended_value & ZEND_PARTIAL_FLAGS, const_args); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ADD_SPEC_CONST_TMPVARCV_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -64781,6 +64766,53 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FUNC_GET_ARGS_SPEC } /* Contrary to what its name indicates, ZEND_COPY_TMP may receive and define references. */ +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_PARTIAL_FLAGS); + zval *named_positions = NULL; + zend_string *pfa_name = Z_STR_P(RT_CONSTANT(opline, opline->op1)); + uint32_t const_args; + + if (IS_UNUSED == IS_UNUSED) { + const_args = opline->op2.num; + } else { + const_args = Z_EXTRA_P(named_positions); + } + + zend_partial_create(EX_VAR(opline->result.var), + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ? + call->extra_named_params : NULL, + IS_UNUSED == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + EX(func)->op_array.filename, &opline->lineno, cache_slot, + pfa_name, opline->extended_value & ZEND_PARTIAL_FLAGS, const_args); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -109528,10 +109560,10 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_LABEL, (void*)&&ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_LABEL, (void*)&&ZEND_TYPE_ASSERT_SPEC_CONST_LABEL, - (void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_LABEL, + (void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST_LABEL, (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_NULL_LABEL, - (void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_LABEL, + (void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED_LABEL, (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_LABEL, (void*)&&ZEND_NULL_LABEL, @@ -110942,11 +110974,6 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_RECV_INIT_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_RECV_INIT_SPEC_CONST) HYBRID_BREAK(); - HYBRID_CASE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST): - VM_TRACE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST) - ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); - VM_TRACE_OP_END(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST) - HYBRID_BREAK(); HYBRID_CASE(ZEND_INIT_DYNAMIC_CALL_SPEC_TMP): VM_TRACE(ZEND_INIT_DYNAMIC_CALL_SPEC_TMP) ZEND_INIT_DYNAMIC_CALL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -110962,11 +110989,6 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_RECV_VARIADIC_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_RECV_VARIADIC_SPEC_UNUSED) HYBRID_BREAK(); - HYBRID_CASE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED): - VM_TRACE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED) - ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); - VM_TRACE_OP_END(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED) - HYBRID_BREAK(); HYBRID_CASE(ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED): VM_TRACE(ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED) ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -111566,6 +111588,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_IN_ARRAY_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_IN_ARRAY_SPEC_CONST_CONST) HYBRID_BREAK(); + HYBRID_CASE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST): + VM_TRACE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST) + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST) + HYBRID_BREAK(); HYBRID_CASE(ZEND_ADD_SPEC_CONST_TMPVARCV): VM_TRACE(ZEND_ADD_SPEC_CONST_TMPVARCV) ZEND_ADD_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -111926,6 +111953,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_FUNC_GET_ARGS_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_FUNC_GET_ARGS_SPEC_CONST_UNUSED) HYBRID_BREAK(); + HYBRID_CASE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED): + VM_TRACE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED) + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED) + HYBRID_BREAK(); HYBRID_CASE(ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED): VM_TRACE(ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED) ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -118496,10 +118528,10 @@ void zend_vm_init(void) ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_HANDLER, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_HANDLER, ZEND_TYPE_ASSERT_SPEC_CONST_HANDLER, - ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED_HANDLER, ZEND_NULL_HANDLER, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_HANDLER, ZEND_NULL_HANDLER, @@ -121984,10 +122016,10 @@ void zend_vm_init(void) ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_TAILCALL_HANDLER, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_TAILCALL_HANDLER, ZEND_TYPE_ASSERT_SPEC_CONST_TAILCALL_HANDLER, - ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_TAILCALL_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST_TAILCALL_HANDLER, ZEND_NULL_TAILCALL_HANDLER, ZEND_NULL_TAILCALL_HANDLER, - ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_TAILCALL_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED_TAILCALL_HANDLER, ZEND_NULL_TAILCALL_HANDLER, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_TAILCALL_HANDLER, ZEND_NULL_TAILCALL_HANDLER, diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index 7ffe2c220a02..503a6d25634b 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1087,8 +1087,8 @@ _(2556, ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED) \ _(2557, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST) \ _(2558, ZEND_TYPE_ASSERT_SPEC_CONST) \ - _(2559, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST) \ - _(2562, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED) \ + _(2559, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST) \ + _(2562, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED) \ _(2564, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST) \ _(2567, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED) \ _(2569, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 9846e36037b9..f9b30edb5e9b 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -451,7 +451,7 @@ static uint32_t zend_vm_opcodes_flags[214] = { 0x01001103, 0x00000303, 0x01000003, - 0x010003a0, + 0x01000303, 0x00001301, }; diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 85496ee1ab06..07e53dfe0f9f 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -400,7 +400,6 @@ static zend_object *curl_clone_obj(zend_object *object) { clone_object = curl_create_object(curl_ce); clone_ch = curl_from_obj(clone_object); - init_curl_handle(clone_ch); ch = curl_from_obj(object); cp = curl_easy_duphandle(ch->cp); @@ -409,6 +408,7 @@ static zend_object *curl_clone_obj(zend_object *object) { return &clone_ch->std; } + init_curl_handle(clone_ch); clone_ch->cp = cp; _php_setup_easy_copy_handlers(clone_ch, ch); diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 6f7cc8a6552a..8685fb564150 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2024,7 +2024,7 @@ static char *zend_accel_uintptr_hex(char *dest, uintptr_t n) * a scheme, except file:// and phar://. */ #define PFA_KEY_PREFIX "pfa://" -static zend_string *zend_accel_pfa_key(const zend_op *declaring_opline, +static zend_string *zend_accel_pfa_key(const uint32_t *declaring_lineno_ptr, const zend_function *called_function) { const size_t max_key_len = strlen(PFA_KEY_PREFIX) + (sizeof(uintptr_t)*2) + strlen(":") + (sizeof(uintptr_t)*2); @@ -2032,7 +2032,7 @@ static zend_string *zend_accel_pfa_key(const zend_op *declaring_opline, char *dest = ZSTR_VAL(key); dest = zend_mempcpy(dest, PFA_KEY_PREFIX, strlen(PFA_KEY_PREFIX)); - dest = zend_accel_uintptr_hex(dest, (uintptr_t)declaring_opline); + dest = zend_accel_uintptr_hex(dest, (uintptr_t)declaring_lineno_ptr); *dest++ = ':'; const void *ptr; @@ -2054,17 +2054,17 @@ static zend_string *zend_accel_pfa_key(const zend_op *declaring_opline, return key; } -const zend_op_array *zend_accel_pfa_cache_get(const zend_op_array *declaring_op_array, - const zend_op *declaring_opline, const zend_function *called_function) +const zend_op_array *zend_accel_pfa_cache_get( + const uint32_t *declaring_lineno_ptr, const zend_function *called_function, bool cacheable_in_shm) { - zend_string *key = zend_accel_pfa_key(declaring_opline, called_function); + zend_string *key = zend_accel_pfa_key(declaring_lineno_ptr, called_function); zend_op_array *op_array = NULL; - /* A PFA is SHM-cacheable if the declaring_op_array and called_function are + /* A PFA is SHM-cacheable if the declaring op_array and called_function are * cached. */ if (ZCG(accelerator_enabled) && !file_cache_only - && !declaring_op_array->refcount + && cacheable_in_shm /* declaring op_array is cached */ && (called_function->type != ZEND_USER_FUNCTION || !called_function->op_array.refcount)) { zend_persistent_script *persistent_script = zend_accel_hash_find(&ZCSG(hash), key); if (persistent_script) { @@ -2084,11 +2084,13 @@ const zend_op_array *zend_accel_pfa_cache_get(const zend_op_array *declaring_op_ } zend_op_array *zend_accel_compile_pfa(zend_ast *ast, - const zend_op_array *declaring_op_array, - const zend_op *declaring_opline, + zend_string *declaring_filename, + const uint32_t *declaring_lineno_ptr, const zend_function *called_function, - zend_string *pfa_func_name) + zend_string *pfa_func_name, bool cacheable_in_shm) { + ZEND_ASSERT(zend_accel_in_shm((void*)declaring_lineno_ptr) || !cacheable_in_shm); + zend_begin_record_errors(); zend_op_array *op_array; @@ -2106,7 +2108,7 @@ zend_op_array *zend_accel_compile_pfa(zend_ast *ast, CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES; #endif - op_array = zend_compile_ast(ast, ZEND_USER_FUNCTION, declaring_op_array->filename); + op_array = zend_compile_ast(ast, ZEND_USER_FUNCTION, declaring_filename); CG(compiler_options) = orig_compiler_options; } zend_catch { @@ -2119,21 +2121,21 @@ zend_op_array *zend_accel_compile_pfa(zend_ast *ast, ZEND_ASSERT(op_array->num_dynamic_func_defs == 1); zend_string_release(op_array->dynamic_func_defs[0]->function_name); - op_array->dynamic_func_defs[0]->function_name = pfa_func_name; + op_array->dynamic_func_defs[0]->function_name = zend_string_copy(pfa_func_name); - zend_string *key = zend_accel_pfa_key(declaring_opline, called_function); + zend_string *key = zend_accel_pfa_key(declaring_lineno_ptr, called_function); /* Cache op_array only if the declaring op_array and the called function * are cached */ if (!ZCG(accelerator_enabled) || file_cache_only - || declaring_op_array->refcount + || !cacheable_in_shm /* declaring op_array is not in SHM */ || (called_function->type == ZEND_USER_FUNCTION && called_function->op_array.refcount) || (ZCSG(restart_in_progress) && accel_restart_is_active()) || (!ZCG(counted) && accel_activate_add() == FAILURE)) { zend_op_array *script_op_array = op_array; zend_op_array *op_array = script_op_array->dynamic_func_defs[0]; - GC_ADDREF(op_array->function_name); + GC_TRY_ADDREF(op_array->function_name); (*op_array->refcount)++; destroy_op_array(script_op_array); efree(script_op_array); diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index ef7eea433c09..b76e5d1d4be7 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -334,15 +334,14 @@ zend_string* ZEND_FASTCALL accel_new_interned_string(zend_string *str); uint32_t zend_accel_get_class_name_map_ptr(zend_string *type_name); -const zend_op_array *zend_accel_pfa_cache_get(const zend_op_array *declaring_op_array, - const zend_op *declaring_opline, - const zend_function *called_function); +const zend_op_array *zend_accel_pfa_cache_get( + const uint32_t *declaring_lineno_ptr, const zend_function *called_function, bool cacheable_in_shm); zend_op_array *zend_accel_compile_pfa(zend_ast *ast, - const zend_op_array *declaring_op_array, - const zend_op *declaring_opline, + zend_string *declaring_filename, + const uint32_t *declaring_lineno_ptr, const zend_function *called_function, - zend_string *pfa_func_name); + zend_string *pfa_func_name, bool cacheable_in_shm); END_EXTERN_C() diff --git a/ext/opcache/tests/array_map_foreach_optimization_008.phpt b/ext/opcache/tests/array_map_foreach_optimization_008.phpt index 8a406ef11ca6..36c80b460e88 100644 --- a/ext/opcache/tests/array_map_foreach_optimization_008.phpt +++ b/ext/opcache/tests/array_map_foreach_optimization_008.phpt @@ -33,7 +33,7 @@ $_main: 0005 INIT_FCALL 2 %d string("array_map") 0006 INIT_FCALL 0 %d string("plusn") 0007 SEND_VAL int(2) string("n") -0008 T2 = CALLABLE_CONVERT_PARTIAL 2 +0008 T2 = CALLABLE_CONVERT_PARTIAL %d string("{closure:%s}") 0009 SEND_VAL T2 1 0010 SEND_VAR CV0($array) 2 0011 T2 = DO_ICALL diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 265d5de41476..0b6cabe42015 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -22,6 +22,7 @@ #include "zend_attributes.h" #include "zend_system_id.h" #include "zend_enum.h" +#include "zend_partial.h" #include "php.h" #ifdef ZEND_WIN32 @@ -382,6 +383,10 @@ static void zend_file_cache_serialize_ast(zend_ast *ast, } else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) { zend_ast_fcc *fcc = (zend_ast_fcc*)ast; ZEND_MAP_PTR_INIT(fcc->fptr, NULL); + /* Will be reset if needed during unserialize */ + fcc->attr |= ZEND_PARTIAL_CACHEABLE_IN_SHM; + SERIALIZE_STR(fcc->filename); + SERIALIZE_STR(fcc->name); if (!IS_SERIALIZED(fcc->args)) { SERIALIZE_PTR(fcc->args); tmp = fcc->args; @@ -636,6 +641,12 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra break; } #endif + + if (opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) { + /* Will be reset if needed during unserialize */ + opline->extended_value |= ZEND_PARTIAL_CACHEABLE_IN_SHM; + } + zend_serialize_opcode_handler(opline); opline++; } @@ -1308,9 +1319,13 @@ static void zend_file_cache_unserialize_ast(zend_ast *ast, zend_ast_get_op_array(ast)->op_array = Z_PTR(z); } else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) { zend_ast_fcc *fcc = (zend_ast_fcc*)ast; - if (!script->corrupted) { + if (script->corrupted) { + fcc->attr &= ~ZEND_PARTIAL_CACHEABLE_IN_SHM; + } else { ZEND_MAP_PTR_NEW(fcc->fptr); } + UNSERIALIZE_STR(fcc->filename); + UNSERIALIZE_STR(fcc->name); if (!IS_UNSERIALIZED(fcc->args)) { UNSERIALIZE_PTR(fcc->args); zend_file_cache_unserialize_ast(fcc->args, script, buf); @@ -1541,6 +1556,13 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr zval *literal = RT_CONSTANT(opline, opline->op1); UNSERIALIZE_ATTRIBUTES(Z_PTR_P(literal)); } + + if (opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) { + if (script->corrupted) { + opline->extended_value &= ~ZEND_PARTIAL_CACHEABLE_IN_SHM; + } + } + zend_deserialize_opcode_handler(opline); opline++; } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index d3e719dbed70..134551781ba2 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -27,6 +27,7 @@ #include "zend_operators.h" #include "zend_interfaces.h" #include "zend_attributes.h" +#include "zend_partial.h" #ifdef HAVE_JIT # include "Optimizer/zend_func_info.h" @@ -197,6 +198,13 @@ static zend_ast *zend_persist_ast(zend_ast *ast) zend_ast_fcc *copy = zend_shared_memdup(ast, sizeof(zend_ast_fcc)); if (!ZCG(current_persistent_script)->corrupted) { ZEND_MAP_PTR_NEW(copy->fptr); + copy->attr |= ZEND_PARTIAL_CACHEABLE_IN_SHM; + } + if (copy->filename) { + zend_accel_store_interned_string(copy->filename); + } + if (copy->name) { + zend_accel_store_interned_string(copy->name); } copy->args = zend_persist_ast(copy->args); node = (zend_ast *) copy; @@ -624,6 +632,12 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc attributes = zend_persist_attributes(attributes); ZVAL_PTR(literal, attributes); } + + if (opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) { + if (!ZCG(current_persistent_script)->corrupted) { + opline->extended_value |= ZEND_PARTIAL_CACHEABLE_IN_SHM; + } + } } efree(op_array->opcodes); diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 9ff37079193b..71398024e7c6 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -92,6 +92,12 @@ static void zend_persist_ast_calc(zend_ast *ast) } else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) { zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast; ADD_SIZE(sizeof(zend_ast_fcc)); + if (fcc_ast->filename) { + ADD_INTERNED_STRING(fcc_ast->filename); + } + if (fcc_ast->name) { + ADD_INTERNED_STRING(fcc_ast->name); + } zend_persist_ast_calc(fcc_ast->args); } else if (zend_ast_is_decl(ast)) { /* Not implemented. */ diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c index 6a62d9717e7b..4fde4ed5f9b9 100644 --- a/ext/pcre/php_pcre.c +++ b/ext/pcre/php_pcre.c @@ -723,6 +723,8 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, bo #ifdef PCRE2_UCP coptions |= PCRE2_UCP; #endif + /* The \C escape sequence is unsafe in PCRE2_UTF mode */ + coptions |= PCRE2_NEVER_BACKSLASH_C; break; case 'J': coptions |= PCRE2_DUPNAMES; break; @@ -778,8 +780,13 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, bo if (key != regex) { zend_string_release_ex(key, 0); } - pcre2_get_error_message(errnumber, error, sizeof(error)); - php_error_docref(NULL,E_WARNING, "Compilation failed: %s at offset %zu", error, erroffset); + const char *err_msg = (const char*) error; + if (errnumber == PCRE2_ERROR_BACKSLASH_C_CALLER_DISABLED) { + err_msg = "using \\C is incompatible with the 'u' modifier"; + } else { + pcre2_get_error_message(errnumber, error, sizeof(error)); + } + php_error_docref(NULL,E_WARNING, "Compilation failed: %s at offset %zu", err_msg, erroffset); pcre_handle_exec_error(PCRE2_ERROR_INTERNAL); efree(pattern); return NULL; diff --git a/ext/pcre/tests/gh21134.phpt b/ext/pcre/tests/gh21134.phpt new file mode 100644 index 000000000000..ddcf19a6e7a2 --- /dev/null +++ b/ext/pcre/tests/gh21134.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-21134: ASan negative-size-param in preg_match_all() with \C + UTF-8 multibyte input +--CREDITS-- +vi3tL0u1s +--FILE-- + +--EXPECTF-- +Warning: preg_match_all(): Compilation failed: using \C is incompatible with the 'u' modifier at offset 6 in %s on line %d +bool(false) +NULL diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index a068f9f48ac6..ec060cbbf3e9 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3728,7 +3728,7 @@ ZEND_METHOD(ReflectionMethod, getPrototype) if (!mptr->common.prototype) { zend_throw_exception_ex(reflection_exception_ptr, 0, - "Method %s::%s does not have a prototype", ZSTR_VAL(intern->ce->name), ZSTR_VAL(mptr->common.function_name)); + "Method %s::%s() does not have a prototype", ZSTR_VAL(intern->ce->name), ZSTR_VAL(mptr->common.function_name)); RETURN_THROWS(); } @@ -4195,7 +4195,7 @@ ZEND_METHOD(ReflectionClass, setStaticPropertyValue) if (!variable_ptr) { zend_clear_exception(); zend_throw_exception_ex(reflection_exception_ptr, 0, - "Class %s does not have a property named %s", ZSTR_VAL(ce->name), ZSTR_VAL(name)); + "Property %s::$%s does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(name)); RETURN_THROWS(); } @@ -6039,7 +6039,7 @@ static zend_result reflection_property_check_lazy_compatible( { if (!prop) { zend_throw_exception_ex(reflection_exception_ptr, 0, - "Can not use %s on dynamic property %s::$%s", + "Cannot use %s() on dynamic property %s::$%s", method, ZSTR_VAL(scope->name), ZSTR_VAL(unmangled_name)); return FAILURE; @@ -6047,7 +6047,7 @@ static zend_result reflection_property_check_lazy_compatible( if (prop->flags & ZEND_ACC_STATIC) { zend_throw_exception_ex(reflection_exception_ptr, 0, - "Can not use %s on static property %s::$%s", + "Cannot use %s() on static property %s::$%s", method, ZSTR_VAL(prop->ce->name), ZSTR_VAL(unmangled_name)); return FAILURE; @@ -6055,7 +6055,7 @@ static zend_result reflection_property_check_lazy_compatible( if (prop->flags & ZEND_ACC_VIRTUAL) { zend_throw_exception_ex(reflection_exception_ptr, 0, - "Can not use %s on virtual property %s::$%s", + "Cannot use %s() on virtual property %s::$%s", method, ZSTR_VAL(prop->ce->name), ZSTR_VAL(unmangled_name)); return FAILURE; @@ -6065,7 +6065,7 @@ static zend_result reflection_property_check_lazy_compatible( && !zend_class_can_be_lazy(object->ce) ) { zend_throw_exception_ex(reflection_exception_ptr, 0, - "Can not use %s on internal class %s", + "Cannot use %s() on internal class %s", method, ZSTR_VAL(object->ce->name)); return FAILURE; } diff --git a/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_001.phpt b/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_001.phpt index 2c855a043674..512fb5d6d916 100644 --- a/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_001.phpt +++ b/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_001.phpt @@ -73,5 +73,5 @@ Array ) Set non-existent values from A with no default value: -Class A does not have a property named protectedDoesNotExist -Class A does not have a property named privateDoesNotExist +Property A::$protectedDoesNotExist does not exist +Property A::$privateDoesNotExist does not exist diff --git a/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_002.phpt b/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_002.phpt index 82de2ce0c2a8..2822471a3971 100644 --- a/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_002.phpt +++ b/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_002.phpt @@ -49,6 +49,6 @@ ReflectionClass::setStaticPropertyValue() expects exactly 2 arguments, 0 given ReflectionClass::setStaticPropertyValue() expects exactly 2 arguments, 1 given Deprecated: ReflectionClass::setStaticPropertyValue(): Passing null to parameter #1 ($name) of type string is deprecated in %s on line %d -Class C does not have a property named -Class C does not have a property named 1.5 +Property C::$ does not exist +Property C::$1.5 does not exist ReflectionClass::setStaticPropertyValue(): Argument #1 ($name) must be of type string, array given diff --git a/ext/reflection/tests/ReflectionProperty_lazy_initialization_errors.phpt b/ext/reflection/tests/ReflectionProperty_lazy_initialization_errors.phpt new file mode 100644 index 000000000000..8bd82ff64020 --- /dev/null +++ b/ext/reflection/tests/ReflectionProperty_lazy_initialization_errors.phpt @@ -0,0 +1,51 @@ +--TEST-- +Test ReflectionProperty::setRawValueWithoutLazyInitialization() and skipLazyInitialization() errors +--FILE-- + true; + } +} + +function test(object $obj, string $propertyName) { + $r = new ReflectionProperty($obj, $propertyName); + try { + $r->setRawValueWithoutLazyInitialization($obj, true); + } catch (ReflectionException $e) { + echo $e->getMessage() . "\n"; + } + try { + $r->skipLazyInitialization($obj); + } catch (ReflectionException $e) { + echo $e->getMessage() . "\n\n"; + } +} + +$obj = new Demo(); +test($obj, 'myStatic'); +test($obj, 'myVirtual'); + +$obj->myDynamic = true; +test($obj, 'myDynamic'); + +$obj = new ReflectionClass(Demo::class); +test($obj, 'name'); + +?> +--EXPECT-- +Cannot use setRawValueWithoutLazyInitialization() on static property Demo::$myStatic +Cannot use skipLazyInitialization() on static property Demo::$myStatic + +Cannot use setRawValueWithoutLazyInitialization() on virtual property Demo::$myVirtual +Cannot use skipLazyInitialization() on virtual property Demo::$myVirtual + +Cannot use setRawValueWithoutLazyInitialization() on dynamic property Demo::$myDynamic +Cannot use skipLazyInitialization() on dynamic property Demo::$myDynamic + +Cannot use setRawValueWithoutLazyInitialization() on internal class ReflectionClass +Cannot use skipLazyInitialization() on internal class ReflectionClass diff --git a/ext/reflection/tests/bug74949.phpt b/ext/reflection/tests/bug74949.phpt index 20e0fc00e101..858e766c0144 100644 --- a/ext/reflection/tests/bug74949.phpt +++ b/ext/reflection/tests/bug74949.phpt @@ -21,4 +21,4 @@ try { Method [ public method __invoke ] { } -Method Closure::__invoke does not have a prototype +Method Closure::__invoke() does not have a prototype diff --git a/ext/reflection/tests/property_hooks/gh17713.phpt b/ext/reflection/tests/property_hooks/gh17713.phpt index c6d4d241bc50..edd0e1204658 100644 --- a/ext/reflection/tests/property_hooks/gh17713.phpt +++ b/ext/reflection/tests/property_hooks/gh17713.phpt @@ -157,7 +157,7 @@ int(43) # Accessing Base->virtualProp from scope Base Must not write to virtual property Base::$virtualProp Must not read from virtual property Base::$virtualProp -Can not use setRawValueWithoutLazyInitialization on virtual property Base::$virtualProp +Cannot use setRawValueWithoutLazyInitialization() on virtual property Base::$virtualProp Must not read from virtual property Base::$virtualProp # Accessing Test->dynamicProp from scope Base int(42) @@ -165,5 +165,5 @@ int(43) # Accessing Test->changedProp from scope Base May not use setRawValue on static properties May not use getRawValue on static properties -Can not use setRawValueWithoutLazyInitialization on static property Test::$changedProp +Cannot use setRawValueWithoutLazyInitialization() on static property Test::$changedProp May not use getRawValue on static properties diff --git a/ext/standard/string.c b/ext/standard/string.c index 823c32a8cf1e..d8a7479d2a98 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -4909,6 +4909,11 @@ static zend_string *try_setlocale_zval(zend_long cat, zval *loc_zv) { if (UNEXPECTED(loc_str == NULL)) { return NULL; } + if (zend_str_has_nul_byte(loc_str)) { + zend_argument_value_error(2, "must not contain any null bytes"); + zend_tmp_string_release(tmp_loc_str); + return NULL; + } zend_string *result = try_setlocale_str(cat, loc_str); zend_tmp_string_release(tmp_loc_str); return result; @@ -4930,8 +4935,26 @@ PHP_FUNCTION(setlocale) zend_string **strings = do_alloca(sizeof(zend_string *) * num_args, use_heap); for (uint32_t i = 0; i < num_args; i++) { - if (UNEXPECTED(Z_TYPE(args[i]) != IS_ARRAY && !zend_parse_arg_str(&args[i], &strings[i], true, i + 2))) { - zend_wrong_parameter_type_error(i + 2, Z_EXPECTED_ARRAY_OR_STRING_OR_NULL, &args[i]); + if (Z_TYPE(args[i]) == IS_ARRAY) { + if (UNEXPECTED(i != 0)) { + zend_wrong_parameter_type_error(i + 2, Z_EXPECTED_STRING_OR_NULL, &args[i]); + goto out; + } + if (UNEXPECTED(num_args > 1)) { + zend_argument_count_error( + "setlocale() expects exactly 2 arguments when argument #2 ($locales) is an array, %d given", + ZEND_NUM_ARGS()); + goto out; + } + break; + } + if (UNEXPECTED(!zend_parse_arg_path_str(&args[i], &strings[i], true, i + 2))) { + zend_wrong_parameter_type_error( + i + 2, + Z_TYPE(args[i]) == IS_STRING + ? Z_EXPECTED_PATH + : (i == 0 ? Z_EXPECTED_ARRAY_OR_STRING_OR_NULL : Z_EXPECTED_STRING_OR_NULL), + &args[i]); goto out; } } diff --git a/ext/standard/tests/strings/gh18823_strict.phpt b/ext/standard/tests/strings/gh18823_strict.phpt index 3735eab00670..39ae11ef61bc 100644 --- a/ext/standard/tests/strings/gh18823_strict.phpt +++ b/ext/standard/tests/strings/gh18823_strict.phpt @@ -16,4 +16,4 @@ try { ?> --EXPECT-- setlocale(): Argument #2 ($locales) must be of type array|string|null, int given -setlocale(): Argument #3 must be of type array|string|null, int given +setlocale(): Argument #3 must be of type ?string, int given diff --git a/ext/standard/tests/strings/setlocale_null_byte.phpt b/ext/standard/tests/strings/setlocale_null_byte.phpt new file mode 100644 index 000000000000..85663b98a71a --- /dev/null +++ b/ext/standard/tests/strings/setlocale_null_byte.phpt @@ -0,0 +1,53 @@ +--TEST-- +setlocale() rejects locale names with null bytes +--FILE-- +getMessage(), \PHP_EOL; +} + +try { + var_dump(setlocale(LC_ALL, ["locale\0name", "C"])); +} catch (ValueError $e) { + echo $e::class, ": ", $e->getMessage(), \PHP_EOL; +} + +try { + var_dump(@setlocale(LC_ALL, [str_repeat("x", 255), "C\0locale"])); +} catch (ValueError $e) { + echo $e::class, ": ", $e->getMessage(), \PHP_EOL; +} + +try { + var_dump(setlocale(LC_ALL, "zz_ZZ.nope", ["C\0locale"])); +} catch (TypeError $e) { + echo $e::class, ": ", $e->getMessage(), \PHP_EOL; +} + +try { + var_dump(setlocale(LC_ALL, "zz_ZZ.nope", new NullByteStringable())); +} catch (ValueError $e) { + echo $e::class, ": ", $e->getMessage(), \PHP_EOL; +} + +try { + var_dump(setlocale(LC_ALL, [], new NullByteStringable())); +} catch (ArgumentCountError $e) { + echo $e::class, ": ", $e->getMessage(), \PHP_EOL; +} +?> +--EXPECT-- +ValueError: setlocale(): Argument #2 ($locales) must not contain any null bytes +ValueError: setlocale(): Argument #2 ($locales) must not contain any null bytes +ValueError: setlocale(): Argument #2 ($locales) must not contain any null bytes +TypeError: setlocale(): Argument #3 must be of type ?string, array given +ValueError: setlocale(): Argument #3 must not contain any null bytes +ArgumentCountError: setlocale() expects exactly 2 arguments when argument #2 ($locales) is an array, 3 given diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h index 67112c8932b7..878350f972ac 100644 --- a/main/streams/php_stream_errors.h +++ b/main/streams/php_stream_errors.h @@ -21,18 +21,6 @@ BEGIN_EXTERN_C() -/* Error mode context options (internal C constants) */ -#define PHP_STREAM_ERROR_MODE_ERROR 0 -#define PHP_STREAM_ERROR_MODE_EXCEPTION 1 -#define PHP_STREAM_ERROR_MODE_SILENT 2 - -/* Error store context options (internal C constants) */ -#define PHP_STREAM_ERROR_STORE_AUTO 0 -#define PHP_STREAM_ERROR_STORE_NONE 1 -#define PHP_STREAM_ERROR_STORE_NON_TERM 2 -#define PHP_STREAM_ERROR_STORE_TERMINAL 3 -#define PHP_STREAM_ERROR_STORE_ALL 4 - /* Maximum operation nesting depth */ #define PHP_STREAM_ERROR_MAX_DEPTH 1000 /* Operations pool size to prevent extra allocations */ diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c index b2dfdbb217ad..067a604af787 100644 --- a/main/streams/stream_errors.c +++ b/main/streams/stream_errors.c @@ -69,7 +69,7 @@ static void php_stream_error_create_object(zval *zv, php_stream_error_entry *ent } /* Create array of StreamError objects from error chain */ -PHPAPI void php_stream_error_create_array(zval *zv, php_stream_error_entry *first) +static void php_stream_error_create_array(zval *zv, php_stream_error_entry *first) { array_init(zv); @@ -83,8 +83,23 @@ PHPAPI void php_stream_error_create_array(zval *zv, php_stream_error_entry *firs } /* Context option helpers */ - -static int php_stream_auto_decide_error_store_mode(int error_mode) +/* Error mode context options (internal C constants) */ +C23_ENUM(php_stream_error_mode, uint8_t) { + PHP_STREAM_ERROR_MODE_ERROR = 0, + PHP_STREAM_ERROR_MODE_EXCEPTION = 1, + PHP_STREAM_ERROR_MODE_SILENT = 2 +}; + +/* Error store context options (internal C constants) */ +C23_ENUM(php_stream_error_store, uint8_t) { + PHP_STREAM_ERROR_STORE_AUTO = 0, + PHP_STREAM_ERROR_STORE_NONE = 1, + PHP_STREAM_ERROR_STORE_NON_TERM = 2, + PHP_STREAM_ERROR_STORE_TERMINAL = 3, + PHP_STREAM_ERROR_STORE_ALL = 4 +}; + +static php_stream_error_store php_stream_auto_decide_error_store_mode(php_stream_error_mode error_mode) { switch (error_mode) { case PHP_STREAM_ERROR_MODE_ERROR: @@ -98,7 +113,7 @@ static int php_stream_auto_decide_error_store_mode(int error_mode) } } -static int php_stream_get_error_mode(php_stream_context *context) +static php_stream_error_mode php_stream_get_error_mode(php_stream_context *context) { if (!context) { return PHP_STREAM_ERROR_MODE_ERROR; @@ -127,7 +142,8 @@ static int php_stream_get_error_mode(php_stream_context *context) return PHP_STREAM_ERROR_MODE_ERROR; } -static int php_stream_get_error_store_mode(php_stream_context *context, int error_mode) +static php_stream_error_store php_stream_get_error_store_mode( + php_stream_context *context, php_stream_error_mode error_mode) { if (!context) { return php_stream_auto_decide_error_store_mode(error_mode); @@ -283,7 +299,7 @@ PHPAPI php_stream_error_operation *php_stream_error_operation_begin(void) if (state->operation_depth >= PHP_STREAM_ERROR_MAX_DEPTH) { php_error_docref(NULL, E_WARNING, - "Stream error operation depth exceeded (%u), possible infinite recursion", + "Stream error operation depth exceeded (%"PRIu32"), possible infinite recursion", state->operation_depth); return NULL; } @@ -386,7 +402,7 @@ static void php_stream_throw_exception_with_errors(php_stream_error_operation *o } static void php_stream_report_errors(php_stream_context *context, php_stream_error_operation *op, - int error_mode, bool is_terminating) + php_stream_error_mode error_mode, bool is_terminating) { switch (error_mode) { case PHP_STREAM_ERROR_MODE_ERROR: { @@ -439,8 +455,8 @@ PHPAPI void php_stream_error_operation_end(php_stream_context *context) context = FG(default_context); } - int error_mode = php_stream_get_error_mode(context); - int store_mode = php_stream_get_error_store_mode(context, error_mode); + php_stream_error_mode error_mode = php_stream_get_error_mode(context); + php_stream_error_store store_mode = php_stream_get_error_store_mode(context, error_mode); bool is_terminating = php_stream_has_terminating_error(op);