php中反向引用分三类:匹配时用、,替换时用$1、$2(仅双引号/preg_replace),回调中用$matches[1];命名组用${name}提升可读性,但不可用于单引号或str_replace。

PHP中用$1、$2实现反向引用
PHP正则匹配后,想在替换字符串里复用捕获的内容,直接写$1、$2就行——但仅限preg_replace()这类替换函数,且必须是双引号字符串或heredoc。单引号里$1会被当字面量。
常见错误:用preg_match()拿到$matches数组后,误以为也能在后续str_replace()里用$1,其实不行——那是preg_replace()内部解析的语法,不是PHP变量。
-
preg_replace('/(d+)-(w+)/', 'ID:$1, TYPE:$2', '123-abc')→'ID:123, TYPE:abc' - 若用单引号:
'ID:$1, TYPE:$2'→ 字面输出$1,不替换 - 数字超过9时,推荐用
${10}写法避免歧义(比如$10会被解释为$1加字符0)
、在模式内部回溯匹配
在正则表达式**本身**中复用前面的捕获组,用反斜杠加数字,比如/(w+)s+/能匹配"hello hello",但不匹配"hello world"。这是真正的“反向引用”,发生在匹配阶段,不是替换阶段。
注意:这种写法对性能有影响。每次遇到都要回溯检查之前捕获的内容,长文本+嵌套捕获时可能触发PCRE的回溯限制,报错PREG_BACKTRACK_LIMIT_ERROR。
-
preg_match('/"([^"]*)"s*=s*"\1"/', '"name" = "name"')→ 匹配成功 -
preg_match('/"([^"]*)"s*=s*"\1"/', '"name" = "value"')→ 失败 - 不能用
$1代替——模式里$1是行尾锚点,完全不是一回事
命名捕获组配合${name}提升可读性
当捕获组多于3个,用、容易数错。PHP支持命名捕获(?P<name>...)</name>,替换时用${name},语义清晰且不怕顺序变动。
命名组在preg_match()返回的$matches里也会多一个键名索引,调试更方便。但注意:命名和数字索引共存,$matches['name']和$matches[1]可能指向同一内容,具体取决于括号顺序。
preg_replace('/(?P<year>d{4})-(?P<month>d{2})-(?P<day>d{2})/', '${year}/${month}/${day}', '2023-12-01')</day></month></year>- 等价于
'$1/$2/$3',但改顺序不影响${year}含义 - 命名组名只能含字母、数字、下划线,且不能纯数字(
(?P...)非法)
替换回调函数里用$matches[1]取值
如果替换逻辑复杂,比如要转大小写、查数据库、做条件判断,就不能靠$1简单替换,得用preg_replace_callback(),在回调函数里操作$matches数组。
这时别再幻想用$1——回调函数参数是完整匹配数组,索引0是全匹配,1开始是各捕获组。闭包里用$matches[1]最稳妥,比依赖全局变量或use传参更直接。
-
preg_replace_callback('/(w+)/', function($m) { return strtoupper($m[1]); }, 'hello world')→'HELLO WORLD' - 若正则有命名组,
$m['name']和$m[1]都可用,但优先用数字索引——命名组名可能被用户输入污染(如正则来自配置) - 回调函数返回
null会导致该次匹配被替换成空字符串,不是报错
真正容易被忽略的是:反向引用的“引用时机”。在匹配时生效,$1在替换时生效,$matches[1]在回调执行时才可访问——三个场景互不替代,选错就白忙活。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











