preg_replace 带有评估支持的垫片
这是非常不可取的。但是,如果您不是程序员,或者真的更喜欢糟糕的代码,您可以使用替代的 preg_replace 函数来让您的 /e 标志暂时正常工作。
/**
* Can be used as a stopgap shim for preg_replace() calls with /e flag.
* Is likely to fail for more complex string munging expressions. And
* very obviously won't help with local-scope variable expressions.
*
* @license: CC-BY-*.*-comment-must-be-retained
* @security: Provides `eval` support for replacement patterns. Which
* poses troubles for user-supplied input when paired with overly
* generic placeholders. This variant is only slightly stricter than
* the C implementation, but still susceptible to varexpression, quote
* breakouts and mundane exploits from unquoted capture placeholders.
* @url: https://stackoverflow.com/q/15454220
*/
function preg_replace_eval($pattern, $replacement, $subject, $limit=-1) {
# strip /e flag
$pattern = preg_replace('/(\W[a-df-z]*)e([a-df-z]*)$/i', '$1$2', $pattern);
# warn about most blatant misuses at least
if (preg_match('/\(\.[+*]/', $pattern)) {
trigger_error("preg_replace_eval(): regex contains (.*) or (.+) placeholders, which easily causes security issues for unconstrained/user input in the replacement expression. Transform your code to use preg_replace_callback() with a sane replacement callback!");
}
# run preg_replace with eval-callback
return preg_replace_callback(
$pattern,
function ($matches) use ($replacement) {
# substitute $1/$2/… with literals from $matches[]
$repl = preg_replace_callback(
'/(?<!\\\\)(?:[$]|\\\\)(\d+)/',
function ($m) use ($matches) {
if (!isset($matches[$m[1]])) { trigger_error("No capture group for '$m[0]' eval placeholder"); }
return addcslashes($matches[$m[1]], '\"\'\`\$\\\0'); # additionally escapes '$' and backticks
},
$replacement
);
# run the replacement expression
return eval("return $repl;");
},
$subject,
$limit
);
}
本质上,您只需将该函数包含在您的代码库中,然后编辑preg_replace
到preg_replace_eval 使用/e 标志的地方。
优点和缺点:
- 真的只是用 Stack Overflow 的几个样本进行了测试。
- 只支持简单的情况(函数调用,不支持变量查找)。
- 包含更多限制和建议通知。
- 会因表达式失败而产生错位且难以理解的错误。
- 不过,它仍然是一个可用的临时解决方案,不会使正确过渡到
preg_replace_callback 变得复杂。
- 许可证评论只是为了阻止人们过度使用或传播它。
替换代码生成器
现在这有点多余。但可能会帮助那些仍然不知所措的用户
手动将他们的代码重组为preg_replace_callback。虽然这实际上更耗时,但代码生成器将/e 替换字符串扩展为表达式的麻烦更少。这是一个非常不起眼的转换,但对于最普遍的例子来说可能就足够了。
要使用此功能,请将任何损坏的 preg_replace 调用编辑到 preg_replace_eval_replacement 并运行它一次。这将打印出相应的 preg_replace_callback 块以代替其使用。
/**
* Use once to generate a crude preg_replace_callback() substitution. Might often
* require additional changes in the `return …;` expression. You'll also have to
* refit the variable names for input/output obviously.
*
* >>> preg_replace_eval_replacement("/\w+/", 'strtopupper("$1")', $ignored);
*/
function preg_replace_eval_replacement($pattern, $replacement, $subjectvar="IGNORED") {
$pattern = preg_replace('/(\W[a-df-z]*)e([a-df-z]*)$/i', '$1$2', $pattern);
$replacement = preg_replace_callback('/[\'\"]?(?<!\\\\)(?:[$]|\\\\)(\d+)[\'\"]?/', function ($m) { return "\$m[{$m[1]}]"; }, $replacement);
$ve = "var_export";
$bt = debug_backtrace(0, 1)[0];
print "<pre><code>
#----------------------------------------------------
# replace preg_*() call in '$bt[file]' line $bt[line] with:
#----------------------------------------------------
\$OUTPUT_VAR = preg_replace_callback(
{$ve($pattern, TRUE)},
function (\$m) {
return {$replacement};
},
\$YOUR_INPUT_VARIABLE_GOES_HERE
)
#----------------------------------------------------
</code></pre>\n";
}
请记住,仅仅复制和粘贴是不是编程。您必须将生成的代码调整回您的实际输入/输出变量名称或使用上下文。
- 如果在
if 中使用之前的 preg_replace 调用,则必须执行 $OUTPUT = 分配。
- 最好保留临时变量或多行代码块结构。
替换表达式可能需要更多的可读性改进或返工。
- 例如,
stripslashes() 在文字表达式中经常变得多余。
- 变量范围查找需要在回调中/在回调中使用
use 或 global 引用。
- 不均匀的引号括起来的
"-$1-$2" 捕获引用最终会在语法上被简单转换为"-$m[1]-$m[2] 所破坏。
代码输出只是一个起点。是的,这作为在线工具会更有用。这种代码重写方法(编辑、运行、编辑、编辑)有些不切实际。然而,对于那些习惯于以任务为中心的编码(更多的步骤,更多的发现)的人来说,可能更容易接近。所以这个替代方案可能会减少一些重复的问题。