我很讨厌正则表达式。我正在尝试替换为:
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的工作方式完全一样。
实现该目标的正确代码是什么?
create_function
,它只是另一个包装器eval
。除非由于某些原因而陷入PHP 5.2,否则您应该使用适当的匿名函数。
Answers:
在正则表达式中,您可以使用(brackets)
;来“捕获”匹配字符串的部分。在这种情况下,您正在捕获匹配的(^|_)
和([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
is代替/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://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
使用标志。
利弊:
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";
}
请记住,仅复制粘贴不是编程。您必须将生成的代码改编回实际的输入/输出变量名称或使用上下文。
$OUTPUT =
如果在preg_replace
中使用了先前的调用,则分配工作必须进行if
。并且替换表达式可能需要更多的可读性改进或返工。
stripslashes()
在文字表达式中经常变得多余。use
或global
引用。"-$1-$2"
捕获引用在语法上会因普通转换为而最终破裂"-$m[1]-$m[2]
。代码输出仅仅是一个起点。是的,这作为在线工具会更加有用。这种代码重写方法(编辑,运行,编辑,编辑)有些不切实际。对于习惯以任务为中心的编码(更多的步骤,更多的发现)的人来说,它可能更易于使用。因此,这种替代方法可能会减少一些重复的问题。