Fix preg_replace_callback_array() with array subject

Apparently this "feature" was completely untested...
This commit is contained in:
Nikita Popov 2020-09-15 11:26:59 +02:00
parent da0663a337
commit d81ea5e928
5 changed files with 59 additions and 29 deletions

View File

@ -2398,9 +2398,9 @@ PHP_FUNCTION(preg_replace_callback)
/* {{{ Perform Perl-style regular expression replacement using replacement callback. */
PHP_FUNCTION(preg_replace_callback_array)
{
zval zv, *replace, *subject, *zcount = NULL;
HashTable *pattern;
zend_string *str_idx_regex;
zval zv, *replace, *zcount = NULL;
HashTable *pattern, *subject_ht;
zend_string *subject_str, *str_idx_regex;
zend_long limit = -1, flags = 0;
size_t replace_count = 0;
zend_fcall_info fci;
@ -2409,7 +2409,7 @@ PHP_FUNCTION(preg_replace_callback_array)
/* Get function parameters and do error-checking. */
ZEND_PARSE_PARAMETERS_START(2, 5)
Z_PARAM_ARRAY_HT(pattern)
Z_PARAM_ZVAL(subject)
Z_PARAM_ARRAY_HT_OR_STR(subject_ht, subject_str)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(limit)
Z_PARAM_ZVAL(zcount)
@ -2420,41 +2420,66 @@ PHP_FUNCTION(preg_replace_callback_array)
fci.object = NULL;
fci.named_params = NULL;
if (subject_ht) {
GC_TRY_ADDREF(subject_ht);
} else {
GC_TRY_ADDREF(subject_str);
}
ZEND_HASH_FOREACH_STR_KEY_VAL(pattern, str_idx_regex, replace) {
if (!str_idx_regex) {
php_error_docref(NULL, E_WARNING, "Delimiter must not be alphanumeric or backslash");
zval_ptr_dtor(return_value);
RETURN_NULL();
RETVAL_NULL();
goto error;
}
if (!zend_is_callable_ex(replace, NULL, 0, NULL, &fcc, NULL)) {
zend_argument_type_error(1, "must contain only valid callbacks");
RETURN_THROWS();
goto error;
}
ZVAL_COPY_VALUE(&fci.function_name, replace);
replace_count += preg_replace_func_impl(&zv, str_idx_regex, /* regex_ht */ NULL, &fci, &fcc,
Z_STR_P(subject), Z_ARRVAL_P(subject),
limit, flags);
if (subject != return_value) {
subject = return_value;
} else {
zval_ptr_dtor(return_value);
subject_str, subject_ht, limit, flags);
switch (Z_TYPE(zv)) {
case IS_ARRAY:
ZEND_ASSERT(subject_ht);
zend_array_release(subject_ht);
subject_ht = Z_ARR(zv);
break;
case IS_STRING:
ZEND_ASSERT(subject_str);
zend_string_release(subject_str);
subject_str = Z_STR(zv);
break;
case IS_NULL:
RETVAL_NULL();
goto error;
EMPTY_SWITCH_DEFAULT_CASE()
}
ZVAL_COPY_VALUE(return_value, &zv);
if (UNEXPECTED(EG(exception))) {
zval_ptr_dtor(return_value);
RETURN_NULL();
if (EG(exception)) {
goto error;
}
} ZEND_HASH_FOREACH_END();
if (zcount) {
ZEND_TRY_ASSIGN_REF_LONG(zcount, replace_count);
}
if (subject_ht) {
RETURN_ARR(subject_ht);
} else {
RETURN_STR(subject_str);
}
error:
if (subject_ht) {
zend_array_release(subject_ht);
} else {
zend_string_release(subject_str);
}
}
/* }}} */

View File

@ -17,11 +17,8 @@ function preg_filter(string|array $regex, string|array $replace, string|array $s
/** @param int $count */
function preg_replace_callback(string|array $regex, callable $callback, string|array $subject, int $limit = -1, &$count = null, int $flags = 0): string|array|null {}
/**
* @param array|string $subject
* @param int $count
*/
function preg_replace_callback_array(array $pattern, $subject, int $limit = -1, &$count = null, int $flags = 0): string|array|null {}
/** @param int $count */
function preg_replace_callback_array(array $pattern, string|array $subject, int $limit = -1, &$count = null, int $flags = 0): string|array|null {}
function preg_split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array|false {}

View File

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 8e8fea5b33408e8a1a39c1b1ae71f16fe1bdd391 */
* Stub hash: 8270971708afa7fa9d82bec0f84c66cc8283f17d */
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)
@ -38,7 +38,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_preg_replace_callback_array, 0, 2, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_NULL)
ZEND_ARG_TYPE_INFO(0, pattern, IS_ARRAY, 0)
ZEND_ARG_INFO(0, subject)
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")

View File

@ -21,7 +21,5 @@ var_dump(preg_replace_callback_array(
), 'a'));
?>
--EXPECTF--
Warning: preg_replace_callback_array(): Delimiter must not be alphanumeric or backslash in %sbug73392.php on line %d
Warning: preg_replace_callback_array(): Delimiter must not be alphanumeric or backslash in %sbug73392.php on line %d
NULL

View File

@ -39,11 +39,21 @@ var_dump(preg_replace_callback_array(
"/c/" => new Rep,
"/a/" => 'b',
"/b/" => function($a) { return "ok"; }), 'a', -1, $count));
var_dump($count);
var_dump(preg_replace_callback_array(
array('/a/' => 'b', "/c/" => new Rep),
array('a', 'c')));
?>
--EXPECT--
string(2) "ok"
string(2) "ok"
string(2) "ok"
int(2)
array(2) {
[0]=>
string(1) "b"
[1]=>
string(1) "d"
}