PHP preg_quote替换字符串

时间:2011-12-02 17:31:50

标签: php regex preg-replace

我使用preg_replace,我想在替换字符串中包含一个url。我怎么引用那个字符串?看来preg_quote仅适用于搜索模式。

$replace = '\1'.addslashes($url).'\3'.addslashes($title).'\4';

5 个答案:

答案 0 :(得分:4)

  1. 需要逃脱
  2. addslashes还不够
  3. preg_quote逃避太多
  4. 请参阅this demo

    正如Mario所说,你可以使用addcslashes($str, "\\$")

答案 1 :(得分:0)

不幸的是,没有通用的方法可以做到这一点,但在大多数情况下addslashes就足够了。

为了最大限度地提高安全性,您可以使用${1}语法。 E.g。

$replace = '${1}'.addslashes($url).'${3}'.addslashes($title).'${4}';

如果你真的想要完全防弹,请使用preg_replace_callback()的回调替换功能。从回调函数返回的字符串完全按原样使用,因此您不必担心将替换语法与普通文本混合使用。

preg_replace_callback()的示例:

class URLReplacer {
    public $pattern = '/my regex/';
    public $url;
    public $title;
    function __construct($url, $title) {
        $this->url = $url;
        $this->title = $title;
    }
    function _callback($matches) {
        return $matches[1].$url.$matches[3].$title.$matches[4];
    }
    function replace($subject) {
        return preg_replace_callback($this->pattern, array($this, '_callback'), $subject);
    }
}
$repl = new URLReplacer($url, $title);
$replaced = $repl->replace($subject);

答案 2 :(得分:0)

你没有提供一个例子,所以我自己编译了一个。我提出的工作解决方案是使用一个简单的回调函数:

$url = 'http://example.com/';
$title = 'Make it Complex \4';

$subject = 'Call \\4 " $me an url';
$pattern = '/(.*)an()( )(url)/';

$replace = function($m) use ($url, $title)
{
    return "$m[1]$url$m[3]$title$m[4]";
};

$result = preg_replace_callback($pattern, $replace, $subject);

结果:

Call \4 " $me http://example.com/ Make it Complex \4url

回调函数是一个所谓的anonymous function Docs,可以很容易地编辑代码。

如果你经常需要这个,你可以把它放到你自己的功能中,可能是为了让它更具可重复性。您甚至可以到目前为止创建自己的模式来替换子组匹配和变量。例如,{\1}代表子模式1匹配,{$2}代表第二个变量。将其包含在它自己的功能中:

$patternf = function()
{
    $values = func_get_args();
    $mask = $values ? array_shift($values) : NULL;
    return function($matches) use ($mask, $values)
    {
        $parts = preg_split('/({[\\\\\\$][0-9]{1,3}})/', $mask, 0, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
        foreach($parts as &$part)
            if (preg_match('/^{([\\\\\\$])([0-9]{1,3})}$/', $part, $p))             
                $part = $p[1] == '\\' ? $matches[(int)$p[2]] : $values[$p[2]-1];
        return implode('', $parts);
    };
};

可以让您更换更方便:

$replace = $patternf('{\1}{$1}{\3}{$2}{\4}', $url, $title);

$result = preg_replace_callback($pattern, $replace, $subject);

Demo。将其包含在它自己的功能中:

function preg_replace_subst($pattern, $replace, $subject)
{
    $values = func_get_args();
    $pattern = array_shift($values);
    $mask = array_shift($values);
    $subject = array_shift($values);
    $callback = function($matches) use ($mask, $values)
    {
        $parts = preg_split('/({[\\\\\\$][0-9]{1,3}})/', $mask, 0, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
        foreach($parts as &$part)
            if (preg_match('/^{([\\\\\\$])([0-9]{1,3})}$/', $part, $p))             
                $part = $p[1] == '\\' ? $matches[(int)$p[2]] : $values[$p[2]-1];
        return implode('', $parts);
    };
    return preg_replace_callback($pattern, $callback, $subject);
}

会给它一个简单的界面:

$url = 'http://example.com/';
$title = 'Make it Complex \4';

$subject = 'Call \\4 " $me an url';
$pattern = '/(.*)an()( )(url)/';

$replace = '{\1}{$1}{\3}{$2}{\4}';

$result = preg_replace_subst($pattern, $replace, $subject, $url, $title);

但是对于许多替换变量,应该可以将它们作为数组传递,否则它会变得有点冗长。

e修饰符与preg_replace一起使用(以及为什么它不起作用)

使用e修饰符时,匹配将替换为替换字符串,然后进行评估。由于其他变量没有被转义,匹配确实会干扰PHP变量替换,这很危险:

$url = 'http://example.com/';
$title = 'Make it Complex \4';

$subject = 'Call me an url.';

$pattern = '/(.*)an()( )(url)/e';
$replace = '"$1{$url}$3{$title}$4"';

$result = preg_replace($pattern, $replace, $subject);

输出:

Call me http://example.com/ Make it Complex \4url.

如上所述,第一个e - 修饰符示例已被破坏,因为$不会在$subject中转义,因此PHP会查找未设置的变量。那也很危险。我提出了一个变体,它解决了这个问题,但它不能处理主题中的双引号:

$url = 'http://example.com/';
$title = 'Make it Complex \4';

$subject = 'Call \\4 " $me an url';

$pattern = '/(.*)an()( )(url)/e';
$replace = "'\$1'.\$url.'\$3'.\$title.'$4'";

输出:

Call \4 \" $me http://example.com/ Make it Complex \4url
        ^ problem, not in input.

所以不是非常傻瓜,这就是为什么它需要回调函数,因为它可以获得不带引号的匹配子模式。

答案 3 :(得分:0)

为了明确一个人在$replacement的{​​{1}}参数中逃避任何潜在的反向引用,请使用函数:

preg_replace()

在OP案例中:

function preg_quote_replacement($input) {
    return addcslashes($input, '\\$');
}

答案 4 :(得分:0)

您可以使用具有T-Regx libraryPattern Builder。它的工作方式类似于SQL中的Prepared Statements:

Pattern::inject("\1@url\2@url\3", [
    'url' => $input
]);

甚至

Pattern::prepare("\1", [$input], "\2", [$input], "\3");
               //      ↑ this means 'ignoring special characters'