diff --git a/ext/fileinfo/libmagic.patch b/ext/fileinfo/libmagic.patch index 4eb7ccd336f89..6b61691d2733e 100644 --- a/ext/fileinfo/libmagic.patch +++ b/ext/fileinfo/libmagic.patch @@ -2414,7 +2414,7 @@ diff -u libmagic.orig/funcs.c libmagic/funcs.c + } + + repl = zend_string_init(rep, strlen(rep), 0); -+ res = php_pcre_replace_impl(pce, NULL, ms->o.buf, strlen(ms->o.buf), repl, -1, &rep_cnt); ++ res = php_pcre_replace_impl(pce, NULL, ms->o.buf, strlen(ms->o.buf), repl, -1, &rep_cnt, 0); + + zend_string_release_ex(repl, 0); + if (NULL == res) { diff --git a/ext/fileinfo/libmagic/funcs.c b/ext/fileinfo/libmagic/funcs.c index cebf41309a666..b47d0333241ae 100644 --- a/ext/fileinfo/libmagic/funcs.c +++ b/ext/fileinfo/libmagic/funcs.c @@ -671,7 +671,7 @@ file_replace(struct magic_set *ms, const char *pat, const char *rep) } repl = zend_string_init(rep, strlen(rep), 0); - res = php_pcre_replace_impl(pce, NULL, ms->o.buf, strlen(ms->o.buf), repl, -1, &rep_cnt); + res = php_pcre_replace_impl(pce, NULL, ms->o.buf, strlen(ms->o.buf), repl, -1, &rep_cnt, 0); zend_string_release_ex(repl, 0); if (NULL == res) { diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c index b59bd87f92385..58815ec25d031 100644 --- a/ext/pcre/php_pcre.c +++ b/ext/pcre/php_pcre.c @@ -27,6 +27,8 @@ #define PREG_OFFSET_CAPTURE (1<<8) #define PREG_UNMATCHED_AS_NULL (1<<9) +#define PREG_REPLACE_COUNT_CHANGES (1<<0) + #define PREG_SPLIT_NO_EMPTY (1<<0) #define PREG_SPLIT_DELIM_CAPTURE (1<<1) #define PREG_SPLIT_OFFSET_CAPTURE (1<<2) @@ -1571,7 +1573,8 @@ PHPAPI zend_string *php_pcre_replace(zend_string *regex, zend_string *subject_str, const char *subject, size_t subject_len, zend_string *replace_str, - size_t limit, size_t *replace_count) + size_t limit, size_t *replace_count, + zend_long flags) { pcre_cache_entry *pce; /* Compiled regular expression */ zend_string *result; /* Function result */ @@ -1587,7 +1590,7 @@ PHPAPI zend_string *php_pcre_replace(zend_string *regex, } pce->refcount++; result = php_pcre_replace_impl(pce, subject_str, subject, subject_len, replace_str, - limit, replace_count); + limit, replace_count, flags); pce->refcount--; return result; @@ -1595,7 +1598,7 @@ PHPAPI zend_string *php_pcre_replace(zend_string *regex, /* }}} */ /* {{{ php_pcre_replace_impl() */ -PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *subject_str, const char *subject, size_t subject_len, zend_string *replace_str, size_t limit, size_t *replace_count) +PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *subject_str, const char *subject, size_t subject_len, zend_string *replace_str, size_t limit, size_t *replace_count, zend_long flags) { uint32_t options; /* Execution options */ int count; /* Count of matched subpatterns */ @@ -1638,6 +1641,13 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su } } + const bool count_changes = (flags & PREG_REPLACE_COUNT_CHANGES) != 0; + size_t local_replace_count = 0; + size_t *replace_count_ptr = replace_count; + if (count_changes && replace_count) { + replace_count_ptr = &local_replace_count; + } + options = (pce->compile_options & PCRE2_UTF) ? 0 : PCRE2_NO_UTF_CHECK; /* Array of subpattern offsets */ @@ -1658,6 +1668,9 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su if (count >= 0 && limit > 0) { bool simple_string; + const char *rep_ptr = NULL; + size_t rep_len = 0; + size_t match_len_local = 0; /* Check for too many substrings condition. */ if (UNEXPECTED(count == 0)) { @@ -1675,12 +1688,9 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su break; } - if (replace_count) { - ++*replace_count; - } - /* Set the match location in subject */ match = subject + offsets[0]; + match_len_local = (size_t)(offsets[1] - offsets[0]); new_len = result_len + offsets[0] - last_end_offset; /* part before the match */ @@ -1723,10 +1733,15 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su } if (simple_string) { - /* copy replacement */ - memcpy(&ZSTR_VAL(result)[result_len], ZSTR_VAL(replace_str), ZSTR_LEN(replace_str)+1); - result_len += ZSTR_LEN(replace_str); + rep_ptr = ZSTR_VAL(replace_str); + rep_len = ZSTR_LEN(replace_str); + + /* copy replacement */ + memcpy(&ZSTR_VAL(result)[result_len], ZSTR_VAL(replace_str), ZSTR_LEN(replace_str)+1); + result_len += ZSTR_LEN(replace_str); } else { + char *rep_start = ZSTR_VAL(result) + result_len; + /* copy replacement and backrefs */ walkbuf = ZSTR_VAL(result) + result_len; @@ -1753,10 +1768,26 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su walk_last = walk[-1]; } *walkbuf = '\0'; + + rep_ptr = rep_start; + rep_len = (size_t)(walkbuf - rep_start); + /* increment the result length by how much we've added to the string */ result_len += (walkbuf - (ZSTR_VAL(result) + result_len)); } + if (replace_count_ptr) { + bool count_changes = flags & PREG_REPLACE_COUNT_CHANGES; + if (!count_changes) { + ++*replace_count_ptr; + } else { + if (rep_len != match_len_local || + (match_len_local && memcmp(rep_ptr, match, match_len_local) != 0)) { + ++*replace_count_ptr; + } + } + } + limit--; /* Advance to the next piece. */ @@ -1831,6 +1862,15 @@ PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *su pcre2_match_data_free(match_data); } + if (count_changes && replace_count && result != NULL) { + /* If the final output is identical to the input, no effective changes happened. */ + if (ZSTR_LEN(result) == subject_len + && (subject_len == 0 || memcmp(ZSTR_VAL(result), subject, subject_len) == 0)) { + local_replace_count = 0; + } + *replace_count += local_replace_count; + } + return result; } /* }}} */ @@ -1889,6 +1929,13 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin options = (pce->compile_options & PCRE2_UTF) ? 0 : PCRE2_NO_UTF_CHECK; + const bool count_changes = (flags & PREG_REPLACE_COUNT_CHANGES) != 0; + size_t local_replace_count = 0; + size_t *replace_count_ptr = replace_count; + if (count_changes && replace_count) { + replace_count_ptr = &local_replace_count; + } + /* Array of subpattern offsets */ PCRE2_SIZE *const offsets = pcre2_get_ovector_pointer(match_data); @@ -1922,10 +1969,6 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin break; } - if (replace_count) { - ++*replace_count; - } - /* Set the match location in subject */ match = ZSTR_VAL(subject_str) + offsets[0]; @@ -1940,6 +1983,19 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin if (UNEXPECTED(eval_result == NULL)) { goto error; } + + if (replace_count_ptr) { + if (!count_changes) { + ++*replace_count_ptr; + } else { + size_t match_len = (size_t)(offsets[1] - offsets[0]); + if (ZSTR_LEN(eval_result) != match_len || + (match_len && memcmp(ZSTR_VAL(eval_result), match, match_len) != 0)) { + ++*replace_count_ptr; + } + } + } + new_len = zend_safe_address_guarded(1, ZSTR_LEN(eval_result) + ZSTR_MAX_OVERHEAD, new_len) -ZSTR_MAX_OVERHEAD; if (new_len >= alloc_len) { alloc_len = zend_safe_address_guarded(2, new_len, ZSTR_MAX_OVERHEAD) - ZSTR_MAX_OVERHEAD; @@ -2032,6 +2088,16 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce, zend_strin } mdata_used = old_mdata_used; + if (count_changes && replace_count && result != NULL) { + /* If the final output is identical to the input, no effective changes happened. */ + if (ZSTR_LEN(result) == ZSTR_LEN(subject_str) + && (ZSTR_LEN(subject_str) == 0 + || memcmp(ZSTR_VAL(result), ZSTR_VAL(subject_str), ZSTR_LEN(subject_str)) == 0)) { + local_replace_count = 0; + } + *replace_count += local_replace_count; + } + return result; } @@ -2057,7 +2123,7 @@ static zend_always_inline zend_string *php_pcre_replace_func(zend_string *regex, /* {{{ php_pcre_replace_array */ static zend_string *php_pcre_replace_array(HashTable *regex, zend_string *replace_str, HashTable *replace_ht, - zend_string *subject_str, size_t limit, size_t *replace_count) + zend_string *subject_str, size_t limit, size_t *replace_count, zend_long flags) { zval *regex_entry; zend_string *result; @@ -2093,7 +2159,7 @@ static zend_string *php_pcre_replace_array(HashTable *regex, /* Do the actual replacement and put the result back into subject_str for further replacements. */ result = php_pcre_replace(regex_str, subject_str, ZSTR_VAL(subject_str), - ZSTR_LEN(subject_str), replace_entry_str, limit, replace_count); + ZSTR_LEN(subject_str), replace_entry_str, limit, replace_count, flags); zend_tmp_string_release(tmp_replace_entry_str); zend_tmp_string_release(tmp_regex_str); zend_string_release_ex(subject_str, 0); @@ -2115,7 +2181,7 @@ static zend_string *php_pcre_replace_array(HashTable *regex, /* Do the actual replacement and put the result back into subject_str for further replacements. */ result = php_pcre_replace(regex_str, subject_str, ZSTR_VAL(subject_str), - ZSTR_LEN(subject_str), replace_str, limit, replace_count); + ZSTR_LEN(subject_str), replace_str, limit, replace_count, flags); zend_tmp_string_release(tmp_regex_str); zend_string_release_ex(subject_str, 0); subject_str = result; @@ -2134,18 +2200,18 @@ static zend_string *php_pcre_replace_array(HashTable *regex, static zend_always_inline zend_string *php_replace_in_subject( zend_string *regex_str, HashTable *regex_ht, zend_string *replace_str, HashTable *replace_ht, - zend_string *subject, size_t limit, size_t *replace_count) + zend_string *subject, size_t limit, size_t *replace_count, zend_long flags) { zend_string *result; if (regex_str) { ZEND_ASSERT(replace_str != NULL); result = php_pcre_replace(regex_str, subject, ZSTR_VAL(subject), ZSTR_LEN(subject), - replace_str, limit, replace_count); + replace_str, limit, replace_count, flags); } else { ZEND_ASSERT(regex_ht != NULL); result = php_pcre_replace_array(regex_ht, replace_str, replace_ht, subject, - limit, replace_count); + limit, replace_count, flags); } return result; } @@ -2254,6 +2320,7 @@ static void _preg_replace_common( HashTable *subject_ht, zend_string *subject_str, zend_long limit, zval *zcount, + zend_long flags, bool is_filter ) { size_t replace_count = 0; @@ -2269,7 +2336,7 @@ static void _preg_replace_common( if (subject_str) { old_replace_count = replace_count; result = php_replace_in_subject(regex_str, regex_ht, replace_str, replace_ht, - subject_str, limit, &replace_count); + subject_str, limit, &replace_count, flags); if (result != NULL) { if (!is_filter || replace_count > old_replace_count) { RETVAL_STR(result); @@ -2298,7 +2365,7 @@ static void _preg_replace_common( zend_string *tmp_subject_entry_str; zend_string *subject_entry_str = zval_get_tmp_string(subject_entry, &tmp_subject_entry_str); result = php_replace_in_subject(regex_str, regex_ht, replace_str, replace_ht, - subject_entry_str, limit, &replace_count); + subject_entry_str, limit, &replace_count, flags); if (result != NULL) { if (!is_filter || replace_count > old_replace_count) { @@ -2329,15 +2396,17 @@ static void preg_replace_common(INTERNAL_FUNCTION_PARAMETERS, bool is_filter) HashTable *regex_ht, *replace_ht, *subject_ht; zend_long limit = -1; zval *zcount = NULL; + zend_long flags = 0; /* Get function parameters and do error-checking. */ - ZEND_PARSE_PARAMETERS_START(3, 5) + ZEND_PARSE_PARAMETERS_START(3, 6) Z_PARAM_ARRAY_HT_OR_STR(regex_ht, regex_str) Z_PARAM_ARRAY_HT_OR_STR(replace_ht, replace_str) Z_PARAM_ARRAY_HT_OR_STR(subject_ht, subject_str) Z_PARAM_OPTIONAL Z_PARAM_LONG(limit) Z_PARAM_ZVAL(zcount) + Z_PARAM_LONG(flags) ZEND_PARSE_PARAMETERS_END(); _preg_replace_common( @@ -2345,7 +2414,8 @@ static void preg_replace_common(INTERNAL_FUNCTION_PARAMETERS, bool is_filter) regex_ht, regex_str, replace_ht, replace_str, subject_ht, subject_str, - limit, zcount, is_filter); + limit, zcount, + flags, is_filter); } /* }}} */ @@ -2371,7 +2441,7 @@ ZEND_FRAMELESS_FUNCTION(preg_replace, 3) regex_ht, regex_str, replace_ht, replace_str, subject_ht, subject_str, - /* limit */ -1, /* zcount */ NULL, /* is_filter */ false); + /* limit */ -1, /* zcount */ NULL, /* flags */ 0, /* is_filter */ false); flf_clean:; Z_FLF_PARAM_FREE_STR(1, regex_tmp); diff --git a/ext/pcre/php_pcre.h b/ext/pcre/php_pcre.h index e180d68a3d486..710579bad07bb 100644 --- a/ext/pcre/php_pcre.h +++ b/ext/pcre/php_pcre.h @@ -25,7 +25,7 @@ #include -PHPAPI zend_string *php_pcre_replace(zend_string *regex, zend_string *subject_str, const char *subject, size_t subject_len, zend_string *replace_str, size_t limit, size_t *replace_count); +PHPAPI zend_string *php_pcre_replace(zend_string *regex, zend_string *subject_str, const char *subject, size_t subject_len, zend_string *replace_str, size_t limit, size_t *replace_count, zend_long flags); PHPAPI pcre2_code* pcre_get_compiled_regex(zend_string *regex, uint32_t *capture_count); extern zend_module_entry pcre_module_entry; @@ -53,7 +53,7 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str zval *subpats, bool global, zend_long flags, zend_off_t start_offset); PHPAPI zend_string *php_pcre_replace_impl(pcre_cache_entry *pce, zend_string *subject_str, const char *subject, size_t subject_len, zend_string *replace_str, - size_t limit, size_t *replace_count); + size_t limit, size_t *replace_count, zend_long flags); PHPAPI void php_pcre_split_impl( pcre_cache_entry *pce, zend_string *subject_str, zval *return_value, zend_long limit_val, zend_long flags); diff --git a/ext/pcre/php_pcre.stub.php b/ext/pcre/php_pcre.stub.php index 0cd045b7efaed..71e41136377d0 100644 --- a/ext/pcre/php_pcre.stub.php +++ b/ext/pcre/php_pcre.stub.php @@ -22,6 +22,11 @@ * @cvalue PREG_UNMATCHED_AS_NULL */ const PREG_UNMATCHED_AS_NULL = UNKNOWN; +/** + * @var int + * @cvalue PREG_REPLACE_COUNT_CHANGES + */ +const PREG_REPLACE_COUNT_CHANGES = UNKNOWN; /** * @var int * @cvalue PREG_SPLIT_NO_EMPTY @@ -112,7 +117,7 @@ function preg_match_all(string $pattern, string $subject, &$matches = null, int * @return string|array|null * @frameless-function {"arity": 3} */ -function preg_replace(string|array $pattern, string|array $replacement, string|array $subject, int $limit = -1, &$count = null): string|array|null {} +function preg_replace(string|array $pattern, string|array $replacement, string|array $subject, int $limit = -1, &$count = null, int $flags = 0): string|array|null {} /** * @param int $count diff --git a/ext/pcre/php_pcre_arginfo.h b/ext/pcre/php_pcre_arginfo.h index 86eaf0d8d60f3..6c005bc93e727 100644 --- a/ext/pcre/php_pcre_arginfo.h +++ b/ext/pcre/php_pcre_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 63de1d37ab303e1d6af7c96eaeeba09d7f35d116 */ + * Stub hash: 43d65e62a4c23a153957521e30eb0a7a12a3eb70 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_preg_match, 0, 2, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0) @@ -17,9 +17,16 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_preg_replace, 0, 3, MAY_BE_STRIN ZEND_ARG_TYPE_MASK(0, subject, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1") ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, count, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -#define arginfo_preg_filter arginfo_preg_replace +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_preg_filter, 0, 3, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_NULL) + ZEND_ARG_TYPE_MASK(0, pattern, MAY_BE_STRING|MAY_BE_ARRAY, NULL) + ZEND_ARG_TYPE_MASK(0, replacement, MAY_BE_STRING|MAY_BE_ARRAY, NULL) + ZEND_ARG_TYPE_MASK(0, subject, MAY_BE_STRING|MAY_BE_ARRAY, NULL) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1") + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, count, "null") +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_preg_replace_callback, 0, 3, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_NULL) ZEND_ARG_TYPE_MASK(0, pattern, MAY_BE_STRING|MAY_BE_ARRAY, NULL) @@ -108,6 +115,7 @@ static void register_php_pcre_symbols(int module_number) REGISTER_LONG_CONSTANT("PREG_SET_ORDER", PREG_SET_ORDER, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PREG_OFFSET_CAPTURE", PREG_OFFSET_CAPTURE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PREG_UNMATCHED_AS_NULL", PREG_UNMATCHED_AS_NULL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PREG_REPLACE_COUNT_CHANGES", PREG_REPLACE_COUNT_CHANGES, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PREG_SPLIT_NO_EMPTY", PREG_SPLIT_NO_EMPTY, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PREG_SPLIT_DELIM_CAPTURE", PREG_SPLIT_DELIM_CAPTURE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PREG_SPLIT_OFFSET_CAPTURE", PREG_SPLIT_OFFSET_CAPTURE, CONST_PERSISTENT); diff --git a/ext/pcre/tests/preg_replace_count_changes.phpt b/ext/pcre/tests/preg_replace_count_changes.phpt new file mode 100644 index 0000000000000..e918a54f7b0ea --- /dev/null +++ b/ext/pcre/tests/preg_replace_count_changes.phpt @@ -0,0 +1,147 @@ +--TEST-- +PREG_REPLACE_COUNT_CHANGES counts only effective changes +--EXTENSIONS-- +pcre +--FILE-- + $m[0], $before, -1, $count, PREG_REPLACE_COUNT_CHANGES); +show("callback_identity", $before, $after, $count); + +/* Callback producing change */ +$after = preg_replace_callback('/a/', fn($m) => 'x', $before, -1, $count, PREG_REPLACE_COUNT_CHANGES); +show("callback_change", $before, $after, $count); +echo "callback_change: " . $after . "\n"; + +/* Replacements change locally but cancel out globally */ +function cancel_out_callback($arr) { + return match ($arr[0]) { + 'a' => 'ab', + 'bba' => 'ba', + }; +} +$before3 = "abba"; +$after = preg_replace_callback('/^a|bba/', 'cancel_out_callback', $before3, -1, $count, PREG_REPLACE_COUNT_CHANGES); +show("callback_cancel_out", $before3, $after, $count); + +/* Cancel out would happen, but limit prevents it */ +$after = preg_replace_callback('/^a|bba/', 'cancel_out_callback', $before3, 1, $count, PREG_REPLACE_COUNT_CHANGES); +show("callback_cancel_out_limit_1", $before3, $after, $count); +echo "callback_cancel_out_limit_1: " . $after . "\n"; + +/* Zero length match inside the string */ +$before4 = "ab"; +$after = preg_replace('/(?=b)/', '', $before4, -1, $count, PREG_REPLACE_COUNT_CHANGES); +show("empty_lookahead_identity", $before4, $after, $count); + +$after = preg_replace('/(?=b)/', 'X', $before4, -1, $count, PREG_REPLACE_COUNT_CHANGES); +show("empty_lookahead_change", $before4, $after, $count); +echo "empty_lookahead_change: " . $after . "\n"; + +/* Zero length match at every position */ +$after = preg_replace('/(?=)/', '', $before4, -1, $count, PREG_REPLACE_COUNT_CHANGES); +show("empty_everywhere_identity", $before4, $after, $count); + +$after = preg_replace('/(?=)/', '-', $before4, -1, $count, PREG_REPLACE_COUNT_CHANGES); +show("empty_everywhere_change", $before4, $after, $count); +echo "empty_everywhere_change: " . $after . "\n"; + +/* Subject array should count per element */ +$subjects = ["abba", "abca"]; +$after = preg_replace_callback('/^a|bba/', 'cancel_out_callback', $subjects, -1, $count, PREG_REPLACE_COUNT_CHANGES); +echo "array_subject_after: " . var_export($after, true) . "\n"; +echo "array_subject_count: " . $count . "\n"; + +/* UTF 8, ensure byte comparison is correct */ +$before5 = "éaé"; +$after = preg_replace('/é/u', 'é', $before5, -1, $count, PREG_REPLACE_COUNT_CHANGES); +show("utf8_identity", $before5, $after, $count); + +$after = preg_replace('/é/u', 'e', $before5, -1, $count, PREG_REPLACE_COUNT_CHANGES); +show("utf8_change", $before5, $after, $count); +echo "utf8_change: " . $after . "\n"; + +/* Empty string match behavior */ +$before2 = "abc"; +$after = preg_replace('/^/', '', $before2, -1, $count, PREG_REPLACE_COUNT_CHANGES); +show("empty_match_identity", $before2, $after, $count); + +$after = preg_replace('/^/', 'Z', $before2, -1, $count, PREG_REPLACE_COUNT_CHANGES); +show("empty_match_change", $before2, $after, $count); +echo "empty_match_change: " . $after . "\n"; + +?> +--EXPECT-- +default: NO REPLACEMENTS +default: 2 REPLACEMENTS +count_changes: NO REPLACEMENTS +count_changes: 0 REPLACEMENTS +count_changes_real: CHANGED +count_changes_real: 2 REPLACEMENTS +count_changes_real: xbcx +backref_identity: NO REPLACEMENTS +backref_identity: 0 REPLACEMENTS +callback_identity: NO REPLACEMENTS +callback_identity: 0 REPLACEMENTS +callback_change: CHANGED +callback_change: 2 REPLACEMENTS +callback_change: xbcx +callback_cancel_out: NO REPLACEMENTS +callback_cancel_out: 0 REPLACEMENTS +callback_cancel_out_limit_1: CHANGED +callback_cancel_out_limit_1: 1 REPLACEMENTS +callback_cancel_out_limit_1: abbba +empty_lookahead_identity: NO REPLACEMENTS +empty_lookahead_identity: 0 REPLACEMENTS +empty_lookahead_change: CHANGED +empty_lookahead_change: 1 REPLACEMENTS +empty_lookahead_change: aXb +empty_everywhere_identity: NO REPLACEMENTS +empty_everywhere_identity: 0 REPLACEMENTS +empty_everywhere_change: CHANGED +empty_everywhere_change: 3 REPLACEMENTS +empty_everywhere_change: -a-b- +array_subject_after: array ( + 0 => 'abba', + 1 => 'abbca', +) +array_subject_count: 1 +utf8_identity: NO REPLACEMENTS +utf8_identity: 0 REPLACEMENTS +utf8_change: CHANGED +utf8_change: 2 REPLACEMENTS +utf8_change: eae +empty_match_identity: NO REPLACEMENTS +empty_match_identity: 0 REPLACEMENTS +empty_match_change: CHANGED +empty_match_change: 1 REPLACEMENTS +empty_match_change: Zabc diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index a73e69519db51..87cd5d12dc688 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -1827,7 +1827,7 @@ PHP_METHOD(RegexIterator, accept) /* Property type is ?string, so this should always succeed. */ ZEND_ASSERT(replacement_str != NULL); - result = php_pcre_replace_impl(intern->u.regex.pce, subject, ZSTR_VAL(subject), ZSTR_LEN(subject), replacement_str, -1, &count); + result = php_pcre_replace_impl(intern->u.regex.pce, subject, ZSTR_VAL(subject), ZSTR_LEN(subject), replacement_str, -1, &count, 0); if (UNEXPECTED(!result)) { zend_string_release(replacement_str); diff --git a/sapi/cli/tests/006.phpt b/sapi/cli/tests/006.phpt index 957543c1a28da..cc28370a6da0e 100644 --- a/sapi/cli/tests/006.phpt +++ b/sapi/cli/tests/006.phpt @@ -44,11 +44,12 @@ string(%d) "Extension [ extension #%d pcre version %s ] { } } - - Constants [19] { + - Constants [20] { Constant [ int PREG_PATTERN_ORDER ] { 1 } Constant [ int PREG_SET_ORDER ] { 2 } Constant [ int PREG_OFFSET_CAPTURE ] { 256 } Constant [ int PREG_UNMATCHED_AS_NULL ] { 512 } + Constant [ int PREG_REPLACE_COUNT_CHANGES ] { 1 } Constant [ int PREG_SPLIT_NO_EMPTY ] { 1 } Constant [ int PREG_SPLIT_DELIM_CAPTURE ] { 2 } Constant [ int PREG_SPLIT_OFFSET_CAPTURE ] { 4 } @@ -91,12 +92,13 @@ string(%d) "Extension [ extension #%d pcre version %s ] { } Function [ function preg_replace ] { - - Parameters [5] { + - Parameters [6] { Parameter #0 [ array|string $pattern ] Parameter #1 [ array|string $replacement ] Parameter #2 [ array|string $subject ] Parameter #3 [ int $limit = -1 ] Parameter #4 [ &$count = null ] + Parameter #5 [ int $flags = 0 ] } - Return [ array|string|null ] } diff --git a/win32/sendmail.c b/win32/sendmail.c index 80dff3f390772..29a68583e562a 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -147,7 +147,8 @@ static zend_string *php_win32_mail_trim_header(const char *header) NULL, header, strlen(header), replace, -1, - NULL); + NULL, + 0); zend_string_release_ex(replace, 0); zend_string_release_ex(regex, 0); @@ -163,7 +164,8 @@ static zend_string *php_win32_mail_trim_header(const char *header) result, ZSTR_VAL(result), ZSTR_LEN(result), replace, -1, - NULL); + NULL, + 0); zend_string_release_ex(replace, 0); zend_string_release_ex(regex, 0); zend_string_release_ex(result, 0);