PHP 8.6.0 Alpha 2 available for testing

preg_replace_callback_array

(PHP 7, PHP 8)

preg_replace_callback_array执行一个正则表达式搜索并使用回调进行替换

说明

function preg_replace_callback_array(
    array $pattern,
    string|array $subject,
    int $limit = -1,
    int &$count = null,
    int $flags = 0
): string|array|null

此函数的行为类似于 preg_replace_callback(),不同之处在于回调函数是基于每个模式分别执行的。

参数

pattern

一个关联数组,将模式(键)映射到 callable(值)。

subject

要搜索替换的目标字符串或字符串数组。

limit

对于每个模式用于每个 subject 字符串的最大可替换次数。 默认是 -1(无限制)。

count

如果指定,这个变量将被填充为替换执行的次数。

flags

flags 可以是 PREG_OFFSET_CAPTUREPREG_UNMATCHED_AS_NULL 标志的组合,这会影响匹配到的结果的格式。 相关详情请参阅 preg_match() 中的描述。

返回值

如果 subject 是一个数组, preg_replace_callback_array() 返回一个数组,其他情况返回字符串。错误发生时返回 null

如果查找到了匹配,返回替换后的目标字符串,其他情况 subject 将会无变化返回。

错误/异常

如果传递的正则表达式无法正常解析,会发出 E_WARNING

更新日志

版本 说明
7.4.0 新增 flags 参数。

示例

示例 #1 preg_replace_callback_array() 示例

<?php
$subject = 'Aaaaaa Bbb';

preg_replace_callback_array(
    [
        '~[a]+~i' => function ($match) {
            echo strlen($match[0]), ' matches for "a" found', PHP_EOL;
        },
        '~[b]+~i' => function ($match) {
            echo strlen($match[0]), ' matches for "b" found', PHP_EOL;
        }
    ],
    $subject
);
?>

以上示例会输出:

6 matches for "a" found
3 matches for "b" found

参见

添加备注

用户贡献的备注 4 notes

up
10
Sz.
8 years ago
Based on some tests, I found these important traits of the function. (These would
be nice to see documented as part of its spec, e.g. for confirmation. Without that,
this is just experimental curiosity. Still better than guesswork, though! ;) )

1. Changes cascade over a subject across callbacks, i.e. a change made to a
   subject by a callback will be seen by the next callback, if its pattern matches
   the changed subject.
   (But a change made by a previous call of the *same* callback (on any subject)
   will not be seen by that callback again.)

2. The pattern + callback pairs will be applied in the order of their appearance
   in $patterns_and_callbacks.

3. The callback can't be null (or '') for a quick shortcut for empty replacements.

4. Overall, the algorithm starts iterating over $patterns_and_callbacks, and then
   feeds each $subject to the current callback, repeatedly for every single match
   of its pattern on the current subject (unlike "preg_match_all", that is, which
   can do the same in one go, returning the accumulated results in an array).

   This basically means that the "crown jewel", an even more efficient function:
   "preg_replace_all_callback_array" is still missing from the collection.

   (Of course, that would better fit a new design of the regex API, where one
   API could flexibly handle various different modes via some $flags = [] array.)

5. (This last one is not specific to this function, but inherent to regexes, OTOH,
   it's probably more relevant here than anywhere else in PHP's regex support.)

   Even apparently simple cases can generate a crazy (and difficult-to-predict)
   number of matches, and therefore callback invokations, so remember the set
   $limit, where affordable. But, of course, try to sharpen your patterns first!

   E.g. use ^...$ anchoring to avoid unintended extra calls on matching substrings
   of a subject, (I.e. '/.*/', without anchoring, would match twice: once for the
   whole subject, and then for a trailing empty substring -- but I'm not quite sure
   this should actually be correct behavior, though.)
up
9
drevilkuko at gmail dot com
10 years ago
finally!!!

before (<=php5.6):

<?php
        $htmlString = preg_replace_callback(
            '/(href="?)(\S+)("?)/i',
            function (&$matches) {
                return $matches[1] . urldecode($matches[2]) . $matches[3];
            },
            $htmlString
        );

        $htmlString = preg_replace_callback(
            '/(href="?\S+)(%24)(\S+)?"?/i', // %24 = $
            function (&$matches) {
                return urldecode($matches[1] . '$' . $matches[3]);
            },
            $htmlString
        );
?>

php7

<?php

        $htmlString = preg_replace_callback_array(
            [
                '/(href="?)(\S+)("?)/i' => function (&$matches) {
                    return $matches[1] . urldecode($matches[2]) . $matches[3];
                },
                '/(href="?\S+)(%24)(\S+)?"?/i' => function (&$matches) {
                    return urldecode($matches[1] . '$' . $matches[3]);
                }
            ],
            $htmlString
        );
?>
up
1
claus at tondering dot dk
2 years ago
Note that the first replacement is applied to the whole string before the next replacement is applied.

For example:

<?php
$subject = 'a b a b a b';

preg_replace_callback_array(
    [
        '/a/' => function ($match) {
            echo '"a" found', PHP_EOL;
        },
        '/b/' => function ($match) {
            echo '"b" found', PHP_EOL;
        }
    ],
    $subject
);

?>

will print

"a" found
"a" found
"a" found
"b" found
"b" found
"b" found

This means that you cannot use global variables to communicate information between the functions about what point in the string you have reached.
up
-3
jfcherng at NOSPAM dot gmail dot com
10 years ago
Here's a possible alternative in older PHP.

<?php

// if (!function_exists('preg_replace_callback_array')) {

function preg_replace_callback_array (array $patterns_and_callbacks, $subject, $limit=-1, &$count=NULL) {
    $count = 0;
    foreach ($patterns_and_callbacks as $pattern => &$callback) {
        $subject = preg_replace_callback($pattern, $callback, $subject, $limit, $partial_count);
        $count += $partial_count;
    }
    return preg_last_error() == PREG_NO_ERROR ? $subject : NULL;
}

// }

?>
To Top