我不擅长正则表达式。我正在尝试替换这个:
public static function camelize($word) {
return preg_replace('/(^|_)([a-z])/e', 'strtoupper("\\2")', $word);
}
使用preg_replace_callback和匿名函数进行替换。我不理解\2是做什么的,也不知道preg_replace_callback如何工作。
实现这个功能的正确代码是什么?
我不擅长正则表达式。我正在尝试替换这个:
public static function camelize($word) {
return preg_replace('/(^|_)([a-z])/e', 'strtoupper("\\2")', $word);
}
使用preg_replace_callback和匿名函数进行替换。我不理解\2是做什么的,也不知道preg_replace_callback如何工作。
实现这个功能的正确代码是什么?
(括号)
“捕获” 匹配字符串的部分;在这种情况下,您正在捕获匹配中的 (^|_)
和 ([a-z])
部分。它们从1开始编号,因此您有反向引用1和2。匹配0是整个匹配的字符串。
/e
修饰符接受替换字符串,并将反斜杠后跟数字(例如 \1
)用适当的反向引用替换 - 但由于您在字符串内部,因此需要转义反斜杠,因此您会得到 '\\1'
。然后(实际上)运行 eval
将生成的字符串作为 PHP 代码运行(这就是为什么它被弃用的原因,因为易于以不安全的方式使用 eval
)。
preg_replace_callback
函数采用回调函数,将匹配的反向引用传递给它。因此,您原本会写成'\\1'
的地方,您现在要访问该参数的元素1 - 例如,如果您有一个形如function($matches) { ... }
的匿名函数,则第一个反向引用是该函数内的$matches[1]
。
因此,/e
参数变为
'do_stuff(\\1) . "and" . do_stuff(\\2)'
可以成为回调函数
function($m) { return do_stuff($m[1]) . "and" . do_stuff($m[2]); }
或者在你的情况下
'strtoupper("\\2")'
可能成为
function($m) { return strtoupper($m[2]); }
$m
和$matches
不是魔术名称,它们只是我在声明回调函数时给定的参数名称。此外,您不必传递匿名函数,它可以是一个函数名作为字符串,或者类似于array($object, $method)
的形式,与PHP中的任何回调一样,例如:function stuffy_callback($things) {
return do_stuff($things[1]) . "and" . do_stuff($things[2]);
}
$foo = preg_replace_callback('/([a-z]+) and ([a-z]+)/', 'stuffy_callback', 'fish and chips');
与任何函数一样,默认情况下无法访问回调外部(周围作用域)的变量。当使用匿名函数时,您可以使用use
关键字导入需要访问的变量,如PHP手册中所述。例如,如果旧参数为
'do_stuff(\\1, $foo)'
function($m) use ($foo) { return do_stuff($m[1], $foo); }
preg_replace_callback
的使用是代替正则表达式中的/e
修饰符,因此需要从"pattern"参数中删除该标志。因此,像/blah(.*)blah/mei
这样的模式将变为/blah(.*)blah/mi
。/e
修饰符在参数上内部使用了addslashes()
的变体,因此一些替换使用stripslashes()
进行了删除;在大多数情况下,您可能希望从新的回调中删除对stripslashes
的调用。这非常不可取。但是,如果您不是程序员,或者真的喜欢糟糕的代码,您可以使用一个替代的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://dev59.com/eGUp5IYBdhLWcg3wLlRV
*/
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
);
}
/e
标志的任何地方将preg_replace
修改为preg_replace_eval
。
优点和缺点:
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";
}
不应该使用标志 e
(或一般的eval
)。
您还可以使用T-Regx库
pattern('(^|_)([a-z])')->replace($word)->by()->group(2)->callback('strtoupper');
preg_replace_callback
的手册页面。在该回调函数中,\\2
将变成$matches[2]
。或者你具体对哪一部分感到困惑? - mariocreate_function
,它只是eval
的另一个包装器。除非你因某种原因被困在PHP 5.2中,否则应该使用适当的匿名函数。 - IMSoPUndefined variable: matches
。 - t q