使用 preg_replace_callback 返工 preg_replace

rework preg_replace with preg_replace_callback

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

有问题的代码是这样的:

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

我的困惑在于:

  1. 这些函数是否打算解决由于 /e 修饰符引起的转义引号?
  2. 没有 </code> 时结果应该是什么?</li> </ol> <p>我已将其重写为这样,但仍然 运行 出现警告和其他问题,因此结果与预期的不一样:</p> <pre><code>$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 );

问题:

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)
)