使用preg_replace_callback返回preg_replace

时间:2016-10-03 18:51:06

标签: php serialization preg-replace-callback

我已经看到很多关于此问题的答案,但由于这个问题有点具体,我仍然需要一些帮助。我尝试使用preg_replace()修饰符更新包含\e的Blogstudio&n; Fix Serialization脚本。

有问题的代码是:

$data = preg_replace('!s:(\d+):([\\\\]?"[\\\\]?"|[\\\\]?"((.*?)[^\\\\])[\\\\]?");!e', "'s:'.strlen(unescape_mysql('$3')).':\"'.unescape_quotes('$3').'\";'", $data);

我的困惑在于:

  1. 这些功能是否打算解决由/e修饰符引起的转义引号?
  2. 如果没有$3
  3. ,结果应该是什么?

    我已经重写了它,但仍然遇到警告和其他问题,所以结果与预期不一样:

    $data = preg_replace_callback(
        '!s:(\d+):([\\\\]?"[\\\\]?"|[\\\\]?"((.*?)[^\\\\])[\\\\]?");!',
        function($d) {
            $length = strlen(unescape_mysql($d[3]));
            $value = unescape_quotes($d[3]);
            $result = 's:' . $length . ':\"' . $value . '\";';
            return 's:' . $length . ':\"' . $value . '\";'
        },
        $data
    );
    

1 个答案:

答案 0 :(得分:2)

问题:

s:(\d+): # group 1
(        # group 2
    [\\\\]?"[\\\\]?"
  |
    [\\\\]?"
    ((.*?)[^\\\\]) # group 3 (and 4)
    [\\\\]?"
)
;

正如您所看到的那样,组内有2个分支的交替2.组3(和4)位于第二个分支中,当第一个分支成功时,这些组未定义。

让我们清理模式,删除无用的捕获组:

s:\d+:
(?:
    [\\\\]? " [\\\\]? "
  |
    [\\\\]? "
    (.*? [^\\\\])      # group 1
    [\\\\]? "
)
;

现在目标组是组1,但分支问题仍然存在。有两种可能的解决方法:

  • 您可以在回调函数中测试索引是否存在isset
  • 您可以使用分支重置功能以两个分支中定义组1的方式更改模式。

第一种方式:

$data = preg_replace_callback(
   '~s:\K\d+:(?:[\\\\]?"[\\\\]?"|[\\\\]?"(.*?[^\\\\])[\\\\]?");~', 
   function ($m) {
     return (isset($m[1]))
       ? strlen(unescape_mysql($m[1])) . ':\"' . $m[1] . '\";'
       : '0:\"\";';
   },
   $data
);

第二种方式(使用分支重置功能):

$data = preg_replace_callback(
   '~s:\K\d+:(?|[\\\\]?"[\\\\]?"()|[\\\\]?"(.*?[^\\\\])[\\\\]?");~', 
   function ($m) {
     return strlen(unescape_mysql($m[1])) . ':\"' . $m[1] . '\";';
   },
   $data
);

在分支重置组中,捕获组在每个分支中具有相同的编号,为了解决您的问题,您只需要在第一个分支中创建一个空的捕获组:

(?|  # open a branch reset group
     foo
     ()  # capture group 1
  |
     bar
     (baz) # capture group 1 (too)
)