diff --git a/NEWS b/NEWS index 7d85ff104dcd..0c2fd04ad469 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.6.0beta1 +- GMP: + . Added optional $definitely_prime output parameter to gmp_prevprime(). + (Weilin Du) + . Added gmp_powm_sec(). (Weilin Du) 30 Jul 2026, PHP 8.6.0alpha3 @@ -23,7 +27,6 @@ PHP NEWS . Made php-cli functionality available in embed builds. (henderkes) - GMP: - . Added gmp_powm_sec(). (Weilin Du) . Added gmp_prevprime(). (Weilin Du, David Carlier) . Fixed GMP power and shift operators to reject GMP right operands outside the unsigned long range instead of silently truncating them. (Weilin Du) diff --git a/UPGRADING b/UPGRADING index 7c9473d07b7d..d8c8a953766a 100644 --- a/UPGRADING +++ b/UPGRADING @@ -275,9 +275,11 @@ PHP 8.6 UPGRADE NOTES . Added gmp_powm_sec() for side-channel quiet modular exponentiation. Requires GNU MP 5.0.0 or later. . Added gmp_prevprime() to get the largest prime smaller than the given - number. A ValueError is thrown if no such prime exists. This function is - available only when PHP is built against GNU MP 6.3.0 or later; it is not - available on official Windows builds using MPIR. + number. The optional $definitely_prime output parameter indicates whether + the returned number is definitely prime, as opposed to probably prime. + A ValueError is thrown if no such prime exists. This function is available + only when PHP is built against GNU MP 6.3.0 or later; it is not available + on official Windows builds using MPIR. - Intl: . Added Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue(), diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index dcb4e8bc8412..8826ca41641f 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -471,7 +471,7 @@ 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 + /* Build a bitset of constant pre-bound PFA args: These are args whose value is always 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++) { diff --git a/Zend/tests/partial_application/const_arg_opt.phpt b/Zend/tests/partial_application/const_arg_opt.phpt index d63456a96a5d..4b3afd1c2d2a 100644 --- a/Zend/tests/partial_application/const_arg_opt.phpt +++ b/Zend/tests/partial_application/const_arg_opt.phpt @@ -46,12 +46,12 @@ $f(2); echo "# Constant pre-bound argument:\n"; $f = f(2, ?); print_lexical_vars($f); -$f(2); +$f(1); echo "# Constant pre-bound argument (inverted):\n"; -$f = f(?, 2); +$f = f(?, 3); print_lexical_vars($f); -$f(1); +$f(4); echo "# Inlined pre-bound argument:\n"; $f = f(g(), ?); @@ -100,15 +100,15 @@ array(2) { [0]=> int(2) [1]=> - int(2) + int(1) } # Constant pre-bound argument (inverted): no lexical vars array(2) { [0]=> - int(1) + int(4) [1]=> - int(2) + int(3) } # Inlined pre-bound argument: no lexical vars diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 2c0b892d1941..622e90da59b2 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -854,12 +854,19 @@ static zend_always_inline zend_result zend_call_function_with_return_value( * If retval_ptr is NULL, the return value is discarded. * If object is NULL, this must be a free function or static call. * called_scope must be provided for instance and static method calls. */ -ZEND_API void zend_call_known_function( +ZEND_API void zend_call_known_function_ex( zend_function *fn, zend_object *object, zend_class_entry *called_scope, zval *retval_ptr, - uint32_t param_count, zval *params, HashTable *named_params); + uint32_t param_count, zval *params, HashTable *named_params, uint32_t consumed_args); -static zend_always_inline void zend_call_known_fcc( - const zend_fcall_info_cache *fcc, zval *retval_ptr, uint32_t param_count, zval *params, HashTable *named_params) +static zend_always_inline void zend_call_known_function( + zend_function *fn, zend_object *object, zend_class_entry *called_scope, zval *retval_ptr, + uint32_t param_count, zval *params, HashTable *named_params) { + zend_call_known_function_ex(fn, object, called_scope, retval_ptr, param_count, params, named_params, 0); +} + +static zend_always_inline void zend_call_known_fcc_ex( + const zend_fcall_info_cache *fcc, zval *retval_ptr, + uint32_t param_count, zval *params, HashTable *named_params, uint32_t consumed_args) { zend_function *func = fcc->function_handler; /* Need to copy trampolines as they get released after they are called */ @@ -868,7 +875,13 @@ static zend_always_inline void zend_call_known_fcc( memcpy(func, fcc->function_handler, sizeof(zend_function)); zend_string_addref(func->op_array.function_name); } - zend_call_known_function(func, fcc->object, fcc->called_scope, retval_ptr, param_count, params, named_params); + zend_call_known_function_ex(func, fcc->object, fcc->called_scope, retval_ptr, param_count, params, named_params, consumed_args); +} + +static zend_always_inline void zend_call_known_fcc( + const zend_fcall_info_cache *fcc, zval *retval_ptr, uint32_t param_count, zval *params, HashTable *named_params) +{ + zend_call_known_fcc_ex(fcc, retval_ptr, param_count, params, named_params, 0); } /* Call the provided zend_function instance method on an object. */ diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index a17cb3d91b91..a91f0d2c933a 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1352,7 +1352,7 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner( 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); + (void**)cache_slot, fcc_ast->name, flags, /* const_args */ 0); if (named_positions) { zend_array_release(named_positions); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 88b0a485750a..83ba5e7490cf 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1100,9 +1100,9 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ } /* }}} */ -ZEND_API void zend_call_known_function( +ZEND_API void zend_call_known_function_ex( zend_function *fn, zend_object *object, zend_class_entry *called_scope, zval *retval_ptr, - uint32_t param_count, zval *params, HashTable *named_params) + uint32_t param_count, zval *params, HashTable *named_params, uint32_t consumed_args) { zval retval; zend_fcall_info fci; @@ -1116,7 +1116,7 @@ ZEND_API void zend_call_known_function( fci.param_count = param_count; fci.params = params; fci.named_params = named_params; - fci.consumed_args = 0; + fci.consumed_args = consumed_args; ZVAL_UNDEF(&fci.function_name); /* Unused */ fcic.function_handler = fn; diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index bf0a77666b6f..4a81026d451b 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -1087,10 +1087,13 @@ GMP_UNARY_OP_FUNCTION(nextprime); ZEND_FUNCTION(gmp_prevprime) { mpz_ptr gmpnum_a, gmpnum_result; + zval *definitely_prime = NULL; int res; - ZEND_PARSE_PARAMETERS_START(1, 1) + ZEND_PARSE_PARAMETERS_START(1, 2) GMP_Z_PARAM_INTO_MPZ_PTR(gmpnum_a) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(definitely_prime) ZEND_PARSE_PARAMETERS_END(); if (mpz_cmp_ui(gmpnum_a, 2) <= 0) { @@ -1106,6 +1109,9 @@ ZEND_FUNCTION(gmp_prevprime) INIT_GMP_RETVAL(gmpnum_result); res = mpz_prevprime(gmpnum_result, gmpnum_a); ZEND_ASSERT(res); + if (definitely_prime) { + ZEND_TRY_ASSIGN_REF_BOOL(definitely_prime, res == 2); + } } /* }}} */ #endif diff --git a/ext/gmp/gmp.stub.php b/ext/gmp/gmp.stub.php index f2ea15481748..e9c3d51ad41d 100644 --- a/ext/gmp/gmp.stub.php +++ b/ext/gmp/gmp.stub.php @@ -188,7 +188,8 @@ function gmp_hamdist(GMP|int|string $num1, GMP|int|string $num2): int {} function gmp_nextprime(GMP|int|string $num): GMP {} #ifdef HAVE___GMPZ_PREVPRIME -function gmp_prevprime(GMP|int|string $num): GMP {} +/** @param bool $definitely_prime */ +function gmp_prevprime(GMP|int|string $num, &$definitely_prime = null): GMP {} #endif function gmp_binomial(GMP|int|string $n, int $k): GMP {} diff --git a/ext/gmp/gmp_arginfo.h b/ext/gmp/gmp_arginfo.h index 60eec94bb5cf..dddaa7e528c9 100644 --- a/ext/gmp/gmp_arginfo.h +++ b/ext/gmp/gmp_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit gmp.stub.php instead. - * Stub hash: 57d016aed930bb41ff4357917e3ae8abd612e973 */ + * Stub hash: 743a4be1078abfa29294336564126ace8c194cbe */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_gmp_init, 0, 1, GMP, 0) ZEND_ARG_TYPE_MASK(0, num, MAY_BE_LONG|MAY_BE_STRING, NULL) @@ -190,6 +190,7 @@ ZEND_END_ARG_INFO() #if defined(HAVE___GMPZ_PREVPRIME) ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_gmp_prevprime, 0, 1, GMP, 0) ZEND_ARG_OBJ_TYPE_MASK(0, num, GMP, MAY_BE_LONG|MAY_BE_STRING, NULL) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, definitely_prime, "null") ZEND_END_ARG_INFO() #endif diff --git a/ext/gmp/tests/gmp_prevprime.phpt b/ext/gmp/tests/gmp_prevprime.phpt index 42c08566c413..541a62f918d5 100644 --- a/ext/gmp/tests/gmp_prevprime.phpt +++ b/ext/gmp/tests/gmp_prevprime.phpt @@ -19,16 +19,42 @@ foreach ([-1, 0, 1, 2] as $value) { } } +$definitelyPrime = null; +try { + var_dump(gmp_prevprime(2, $definitelyPrime)); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} +var_dump($definitelyPrime); + var_dump(gmp_strval(gmp_prevprime(3))); var_dump(gmp_strval(gmp_prevprime(4))); var_dump(gmp_strval(gmp_prevprime(10000))); +$definitelyPrime = null; +var_dump(gmp_strval(gmp_prevprime(3, $definitelyPrime))); +var_dump($definitelyPrime); + +$probablePrime = gmp_nextprime(gmp_pow(10, 80)); +$definitelyPrime = null; +$previousPrime = gmp_prevprime(gmp_add($probablePrime, 1), $definitelyPrime); +var_dump(gmp_cmp($previousPrime, $probablePrime) === 0); +var_dump(is_bool($definitelyPrime)); +var_dump($definitelyPrime === (gmp_prob_prime($previousPrime) === 2)); + ?> --EXPECT-- gmp_prevprime(): Argument #1 ($num) must be greater than 2 gmp_prevprime(): Argument #1 ($num) must be greater than 2 gmp_prevprime(): Argument #1 ($num) must be greater than 2 gmp_prevprime(): Argument #1 ($num) must be greater than 2 +gmp_prevprime(): Argument #1 ($num) must be greater than 2 +NULL string(1) "2" string(1) "3" string(4) "9973" +string(1) "2" +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 33b0dadb900f..06bd6691be13 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -353,10 +353,8 @@ static zend_result php_zip_add_file(ze_zip_object *obj, const char *filename, si typedef struct { zend_long remove_all_path; - char *remove_path; - size_t remove_path_len; - char *add_path; - size_t add_path_len; + const zend_string *remove_path; + const zend_string *add_path; zip_flags_t flags; zip_int32_t comp_method; zip_uint32_t comp_flags; @@ -450,8 +448,8 @@ static zend_result php_zip_parse_options(HashTable *options, zip_options *opts) zend_value_error("Option \"remove_path\" must be less than %d bytes", MAXPATHLEN - 1); return FAILURE; } - opts->remove_path_len = Z_STRLEN_P(option); - opts->remove_path = Z_STRVAL_P(option); + /* No need to copy the string as it's only ever used to check the paths after parsing the options */ + opts->remove_path = Z_STR_P(option); } if ((option = zend_hash_str_find(options, "add_path", sizeof("add_path") - 1)) != NULL) { @@ -470,8 +468,7 @@ static zend_result php_zip_parse_options(HashTable *options, zip_options *opts) zend_value_error("Option \"add_path\" must be less than %d bytes", MAXPATHLEN - 1); return FAILURE; } - opts->add_path_len = Z_STRLEN_P(option); - opts->add_path = Z_STRVAL_P(option); + opts->add_path = Z_STR_P(option); } if ((option = zend_hash_str_find(options, "flags", sizeof("flags") - 1)) != NULL) { @@ -654,7 +651,7 @@ static bool php_zipobj_close(ze_zip_object *obj, zend_string **out_str) /* {{{ * } /* }}} */ -int php_zip_glob(zend_string *spattern, zend_long flags, zval *return_value) /* {{{ */ +static int php_zip_glob(zend_string *spattern, zend_long flags, zval *return_value) /* {{{ */ { int cwd_skip = 0; #ifdef ZTS @@ -758,7 +755,7 @@ int php_zip_glob(zend_string *spattern, zend_long flags, zval *return_value) /* } /* }}} */ -int php_zip_pcre(zend_string *regexp, char *path, int path_len, zval *return_value) /* {{{ */ +static int php_zip_pcre(zend_string *regexp, char *path, int path_len, zval *return_value) /* {{{ */ { #ifdef ZTS char cwd[MAXPATHLEN]; @@ -1826,53 +1823,58 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* ze_zip_object *ze_obj = Z_ZIP_P(self); for (int i = 0; i < found; i++) { - char *file_stripped, *entry_name; - size_t entry_name_len, file_stripped_len; - char entry_name_buf[MAXPATHLEN]; zend_string *basename = NULL; if ((zval_file = zend_hash_index_find(Z_ARRVAL_P(return_value), i)) != NULL) { + const char *file_stripped; + size_t file_stripped_len; if (opts.remove_all_path) { basename = php_basename(Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), NULL, 0); file_stripped = ZSTR_VAL(basename); file_stripped_len = ZSTR_LEN(basename); - } else if (opts.remove_path && Z_STRLEN_P(zval_file) > opts.remove_path_len && !memcmp(Z_STRVAL_P(zval_file), opts.remove_path, opts.remove_path_len)) { - if (IS_SLASH(Z_STRVAL_P(zval_file)[opts.remove_path_len])) { - file_stripped = Z_STRVAL_P(zval_file) + opts.remove_path_len + 1; - file_stripped_len = Z_STRLEN_P(zval_file) - opts.remove_path_len - 1; + } else if (opts.remove_path && zend_string_starts_with(Z_STR_P(zval_file), opts.remove_path)) { + if (IS_SLASH(Z_STRVAL_P(zval_file)[ZSTR_LEN(opts.remove_path)])) { + file_stripped = Z_STRVAL_P(zval_file) + ZSTR_LEN(opts.remove_path) + 1; + file_stripped_len = Z_STRLEN_P(zval_file) - ZSTR_LEN(opts.remove_path) - 1; } else { - file_stripped = Z_STRVAL_P(zval_file) + opts.remove_path_len; - file_stripped_len = Z_STRLEN_P(zval_file) - opts.remove_path_len; + file_stripped = Z_STRVAL_P(zval_file) + ZSTR_LEN(opts.remove_path); + file_stripped_len = Z_STRLEN_P(zval_file) - ZSTR_LEN(opts.remove_path); } } else { file_stripped = Z_STRVAL_P(zval_file); file_stripped_len = Z_STRLEN_P(zval_file); } + zend_string *entry_name; if (opts.add_path) { - if ((opts.add_path_len + file_stripped_len) > MAXPATHLEN) { + if ((ZSTR_LEN(opts.add_path) + file_stripped_len) > MAXPATHLEN) { if (basename) { zend_string_release_ex(basename, false); } - php_error_docref(NULL, E_WARNING, "Entry name too long (max: %d, %zd given)", - MAXPATHLEN - 1, (opts.add_path_len + file_stripped_len)); + php_error_docref(NULL, E_WARNING, "Entry name too long (max: %d, %zu given)", + MAXPATHLEN - 1, (ZSTR_LEN(opts.add_path) + file_stripped_len)); zend_array_destroy(Z_ARR_P(return_value)); RETURN_FALSE; } - snprintf(entry_name_buf, MAXPATHLEN, "%s%s", opts.add_path, file_stripped); + entry_name = zend_string_concat2( + ZSTR_VAL(opts.add_path), ZSTR_LEN(opts.add_path), + file_stripped, file_stripped_len + ); } else { - snprintf(entry_name_buf, MAXPATHLEN, "%s", file_stripped); + entry_name = zend_string_init(file_stripped, file_stripped_len, false); } + ZEND_ASSERT(ZSTR_LEN(entry_name) <= MAXPATHLEN); - entry_name = entry_name_buf; - entry_name_len = strlen(entry_name); if (basename) { zend_string_release_ex(basename, false); basename = NULL; } - if (php_zip_add_file(ze_obj, Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), - entry_name, entry_name_len, 0, 0, -1, opts.flags) == FAILURE) { + const zend_result status = php_zip_add_file(ze_obj, Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), + ZSTR_VAL(entry_name), ZSTR_LEN(entry_name), 0, 0, -1, opts.flags); + + zend_string_release_ex(entry_name, false); + if (status == FAILURE) { zend_array_destroy(Z_ARR_P(return_value)); RETURN_FALSE; }