PHP中有条件preg_replace吗?

时间:2012-03-07 14:28:32

标签: php regex preg-replace

我有一些像这样的代码,它用一个链接取代了一些短代码:

$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}。)

3 个答案:

答案 0 :(得分:9)

这需要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>";
}

答案 1 :(得分:3)

免责声明:以下内容是糟糕的建议,并建议使用现已弃用的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包含单个引号,它将被转义并在输出中显示为\'

See it working

修改

经过一些试验和错误后,这里有一个适用于您可以投放的任何内容的版本,并且还会在适当时通过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>

See it working

答案 2 :(得分:0)

这里的答案更多是为了说明如何使用标记标签在模式本身中放置替换字符串,而不是为了替代@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;

demo