Skip to content

Commit d81ea5e

Browse files
committed
Fix preg_replace_callback_array() with array subject
Apparently this "feature" was completely untested...
1 parent da0663a commit d81ea5e

File tree

5 files changed

+59
-29
lines changed

5 files changed

+59
-29
lines changed

ext/pcre/php_pcre.c

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,9 +2398,9 @@ PHP_FUNCTION(preg_replace_callback)
23982398
/* {{{ Perform Perl-style regular expression replacement using replacement callback. */
23992399
PHP_FUNCTION(preg_replace_callback_array)
24002400
{
2401-
zval zv, *replace, *subject, *zcount = NULL;
2402-
HashTable *pattern;
2403-
zend_string *str_idx_regex;
2401+
zval zv, *replace, *zcount = NULL;
2402+
HashTable *pattern, *subject_ht;
2403+
zend_string *subject_str, *str_idx_regex;
24042404
zend_long limit = -1, flags = 0;
24052405
size_t replace_count = 0;
24062406
zend_fcall_info fci;
@@ -2409,7 +2409,7 @@ PHP_FUNCTION(preg_replace_callback_array)
24092409
/* Get function parameters and do error-checking. */
24102410
ZEND_PARSE_PARAMETERS_START(2, 5)
24112411
Z_PARAM_ARRAY_HT(pattern)
2412-
Z_PARAM_ZVAL(subject)
2412+
Z_PARAM_ARRAY_HT_OR_STR(subject_ht, subject_str)
24132413
Z_PARAM_OPTIONAL
24142414
Z_PARAM_LONG(limit)
24152415
Z_PARAM_ZVAL(zcount)
@@ -2420,41 +2420,66 @@ PHP_FUNCTION(preg_replace_callback_array)
24202420
fci.object = NULL;
24212421
fci.named_params = NULL;
24222422

2423+
if (subject_ht) {
2424+
GC_TRY_ADDREF(subject_ht);
2425+
} else {
2426+
GC_TRY_ADDREF(subject_str);
2427+
}
2428+
24232429
ZEND_HASH_FOREACH_STR_KEY_VAL(pattern, str_idx_regex, replace) {
24242430
if (!str_idx_regex) {
24252431
php_error_docref(NULL, E_WARNING, "Delimiter must not be alphanumeric or backslash");
2426-
zval_ptr_dtor(return_value);
2427-
RETURN_NULL();
2432+
RETVAL_NULL();
2433+
goto error;
24282434
}
24292435

24302436
if (!zend_is_callable_ex(replace, NULL, 0, NULL, &fcc, NULL)) {
24312437
zend_argument_type_error(1, "must contain only valid callbacks");
2432-
RETURN_THROWS();
2438+
goto error;
24332439
}
24342440

24352441
ZVAL_COPY_VALUE(&fci.function_name, replace);
24362442

24372443
replace_count += preg_replace_func_impl(&zv, str_idx_regex, /* regex_ht */ NULL, &fci, &fcc,
2438-
Z_STR_P(subject), Z_ARRVAL_P(subject),
2439-
limit, flags);
2440-
2441-
if (subject != return_value) {
2442-
subject = return_value;
2443-
} else {
2444-
zval_ptr_dtor(return_value);
2444+
subject_str, subject_ht, limit, flags);
2445+
switch (Z_TYPE(zv)) {
2446+
case IS_ARRAY:
2447+
ZEND_ASSERT(subject_ht);
2448+
zend_array_release(subject_ht);
2449+
subject_ht = Z_ARR(zv);
2450+
break;
2451+
case IS_STRING:
2452+
ZEND_ASSERT(subject_str);
2453+
zend_string_release(subject_str);
2454+
subject_str = Z_STR(zv);
2455+
break;
2456+
case IS_NULL:
2457+
RETVAL_NULL();
2458+
goto error;
2459+
EMPTY_SWITCH_DEFAULT_CASE()
24452460
}
24462461

2447-
ZVAL_COPY_VALUE(return_value, &zv);
2448-
2449-
if (UNEXPECTED(EG(exception))) {
2450-
zval_ptr_dtor(return_value);
2451-
RETURN_NULL();
2462+
if (EG(exception)) {
2463+
goto error;
24522464
}
24532465
} ZEND_HASH_FOREACH_END();
24542466

24552467
if (zcount) {
24562468
ZEND_TRY_ASSIGN_REF_LONG(zcount, replace_count);
24572469
}
2470+
2471+
if (subject_ht) {
2472+
RETURN_ARR(subject_ht);
2473+
} else {
2474+
RETURN_STR(subject_str);
2475+
}
2476+
2477+
error:
2478+
if (subject_ht) {
2479+
zend_array_release(subject_ht);
2480+
} else {
2481+
zend_string_release(subject_str);
2482+
}
24582483
}
24592484
/* }}} */
24602485

ext/pcre/php_pcre.stub.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,8 @@ function preg_filter(string|array $regex, string|array $replace, string|array $s
1717
/** @param int $count */
1818
function preg_replace_callback(string|array $regex, callable $callback, string|array $subject, int $limit = -1, &$count = null, int $flags = 0): string|array|null {}
1919

20-
/**
21-
* @param array|string $subject
22-
* @param int $count
23-
*/
24-
function preg_replace_callback_array(array $pattern, $subject, int $limit = -1, &$count = null, int $flags = 0): string|array|null {}
20+
/** @param int $count */
21+
function preg_replace_callback_array(array $pattern, string|array $subject, int $limit = -1, &$count = null, int $flags = 0): string|array|null {}
2522

2623
function preg_split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array|false {}
2724

ext/pcre/php_pcre_arginfo.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 8e8fea5b33408e8a1a39c1b1ae71f16fe1bdd391 */
2+
* Stub hash: 8270971708afa7fa9d82bec0f84c66cc8283f17d */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_preg_match, 0, 2, MAY_BE_LONG|MAY_BE_FALSE)
55
ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0)
@@ -38,7 +38,7 @@ ZEND_END_ARG_INFO()
3838

3939
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_preg_replace_callback_array, 0, 2, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_NULL)
4040
ZEND_ARG_TYPE_INFO(0, pattern, IS_ARRAY, 0)
41-
ZEND_ARG_INFO(0, subject)
41+
ZEND_ARG_TYPE_MASK(0, subject, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
4242
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, limit, IS_LONG, 0, "-1")
4343
ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, count, "null")
4444
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0")

ext/pcre/tests/bug73392.phpt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,5 @@ var_dump(preg_replace_callback_array(
2121
), 'a'));
2222
?>
2323
--EXPECTF--
24-
Warning: preg_replace_callback_array(): Delimiter must not be alphanumeric or backslash in %sbug73392.php on line %d
25-
2624
Warning: preg_replace_callback_array(): Delimiter must not be alphanumeric or backslash in %sbug73392.php on line %d
2725
NULL

ext/pcre/tests/preg_replace_callback_array.phpt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,21 @@ var_dump(preg_replace_callback_array(
3939
"/c/" => new Rep,
4040
"/a/" => 'b',
4141
"/b/" => function($a) { return "ok"; }), 'a', -1, $count));
42-
4342
var_dump($count);
43+
44+
var_dump(preg_replace_callback_array(
45+
array('/a/' => 'b', "/c/" => new Rep),
46+
array('a', 'c')));
47+
4448
?>
4549
--EXPECT--
4650
string(2) "ok"
4751
string(2) "ok"
4852
string(2) "ok"
4953
int(2)
54+
array(2) {
55+
[0]=>
56+
string(1) "b"
57+
[1]=>
58+
string(1) "d"
59+
}

0 commit comments

Comments
 (0)