PHP中是否存在条件preg_replace


Is there a conditional preg_replace in PHP?

我有一些类似的代码,它用链接替换了一些短代码:

$search = array(
    '#'{r'|([^|]+)'|([^}]+)'}#',
    '#'{t'|([^|]+)'|([^}]+)'}#',
    ...,
);
$replace = array(
    '<a href="/ref/$1">$2</a>',
    '<a href="/type/$1">$2</a>',
    ...,
);
$content = preg_replace( $search, $replace, $content );

我还有很多类似的,所以我想知道,有没有办法把它简化为一个简单的带有条件的preg_replace

例如,使用正则表达式#'{([a-z])'|([^|]+)'|([^}]+)'}#,并根据字母将第一个匹配项替换为不同的内容(r=ref,t=type)?(如果有帮助的话,短代码就像{r|url-slug|LinkTitle}。)

这会调用preg_replace_callback(或者可能只是/e eval修饰符),这将允许您将映射t=typer=ref放入替换逻辑:

= preg_replace_callback('#'{([rt])'|([^|]+)'|([^}]+)'}#', "cb_123", ...
function cb_123($m) {
    $map = array("t" => "type",  "r" => "ref");
    $what = $map[  $m[1]  ];
    return "<a href='"/$what/$m[2]'">$m[3]</a>";
}

免责声明:下面的建议很糟糕,建议使用现在已经被弃用的PHP功能。我把它留在这里只是为了历史参考。

使用已接受答案中建议的技巧。


@mario建议的(完全有效的)preg_replace_callback()方法的替代方法是e修饰符,它仅适用于preg_replace(),并允许将替换字符串评估为PHP代码:

<?php
   $shortCodes = array (
     'r' => 'ref',
     't' => 'type'
   );
   $expr = '#'{([a-z])'|([^|]+)'|([^}]+)'}#e';
   $replace = '"<a href='"/{$shortCodes[''$1'']}/$2'">$3</a>"';
   $string = 'Some text as a ref {r|link1.php|link} and a type {r|link2.php|link}';
   echo preg_replace($expr, $replace, $string);

我能想到的唯一问题是,如果LinkTitle包含一个引号,它将被转义,并在输出中显示为''

看到它工作

编辑

经过一点尝试和错误,这里有一个版本可以处理任何你可以扔给它的东西,并在适当的地方通过urlencode()/htmlspecialchars()传递所有数据:

<?php
  $shortCodes = array (
    'r' => 'ref',
    't' => 'type'
  );
  $expr = array(
    '#'{([a-z])'|([^|]+)'|([^}]*"[^}]*)'}#e',
    '#'{([a-z])'|([^|]+)'|([^}]+)'}#e'
  );
  $replace = array(
    '"<a href='"/{$shortCodes[''$1'']}/".htmlspecialchars(urlencode(''$2''))."'">".htmlspecialchars(str_replace('''"'', ''"'', ''$3''))."</a>"',
    '"<a href='"/{$shortCodes[''$1'']}/".htmlspecialchars(urlencode(''$2''))."'">".htmlspecialchars(''$3'')."</a>"'
  );
  $string = 'Some text as a ref {r|link &1.php|link&'' with some bad characters in it} and a type {r|link2.php|link with some "quotes" in it}';
  echo preg_replace($expr, $replace, $string);

输出:

Some text as a ref <a href="/ref/link+%261.php">link&amp;' with some bad characters in it</a> and a type <a href="/ref/link2.php">link with some &quot;quotes&quot; in it</a>

查看它的工作

这个答案更多的是为了说明如何使用标记标签将替换字符串放入模式本身,而不是为@mario(又名欧洲松鼠)答案寻找替代方案。但是,如果有人发现这种技术更合适的情况,请告诉我。

标记(*MARK)最初用于确认正则表达式引擎已到达成功模式路径中的某个位置,但由于可以给它一个标签(*MARK:label),并且由于该标签在preg_match_all的结果数组中返回。。。不幸的是,您无法在preg_replace*函数中访问它,这就是为什么您必须匹配整个字符串(甚至是不关心替换的部分),并加入preg_match_all返回的不同匹配结果。

PHP>=7.4(用于箭头功能):

$s = 'Other text1 {r|url-slug1|LinkTitle1}
Other text2 {t|url-slug2|LinkTitle2}
Other text3';
$pattern = '~
    (?<before> .*? )
    {
        (?: # you can fill this group with anything you want
              r (*MARK:ref)
          |   t (*MARK:type)
        )
        'Q|'E   (?<url_slug> [^|]+ )
        'Q|'E   (?<linkTitle> [^}]+ )
    }
  |
    (?<after> .+ )
~xs';
preg_match_all($pattern, $s, $matches, PREG_SET_ORDER);
$res = array_reduce($matches, fn($c, $i) =>
    $c . (
             $i['after'] ??
             $i['before'] . '<a href="/' . $i['MARK'] . '/'
           . $i['url_slug']. '">'
           . $i['linkTitle'] . '</a>'
         )
);
echo $res;

演示