Replace preg_replace() e modifier with preg_replace_callback

后端 未结 3 1212
醉梦人生
醉梦人生 2020-11-21 05:33

I\'m terrible with regular expressions. I\'m trying to replace this:

public static function camelize($word) {
   return preg_replace(\'/(^|_)([a-z])/e\', \'s         


        
3条回答
  •  后悔当初
    2020-11-21 06:03

    preg_replace shim with eval support

    This is very inadvisable. But if you're not a programmer, or really prefer terrible code, you could use a substitute preg_replace function to keep your /e flag working temporarily.

    /**
     * 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(
                    '/(?

    In essence, you just include that function in your codebase, and edit preg_replace to preg_replace_eval wherever the /e flag was used.

    Pros and cons:

    • Really just tested with a few samples from Stack Overflow.
    • Does only support the easy cases (function calls, not variable lookups).
    • Contains a few more restrictions and advisory notices.
    • Will yield dislocated and less comprehensible errors for expression failures.
    • However is still a usable temporary solution and doesn't complicate a proper transition to preg_replace_callback.
    • And the license comment is just meant to deter people from overusing or spreading this too far.

    Replacement code generator

    Now this is somewhat redundant. But might help those users who are still overwhelmed with manually restructuring their code to preg_replace_callback. While this is effectively more time consuming, a code generator has less trouble to expand the /e replacement string into an expression. It's a very unremarkable conversion, but likely suffices for the most prevalent examples.

    To use this function, edit any broken preg_replace call into preg_replace_eval_replacement and run it once. This will print out the according preg_replace_callback block to be used in its place.

    /**
     * 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('/[\'\"]?(?
        #----------------------------------------------------
        # 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
        )
        #----------------------------------------------------
        
    \n"; }

    Take in mind that mere copy&pasting is not programming. You'll have to adapt the generated code back to your actual input/output variable names, or usage context.

    • Specificially the $OUTPUT = assignment would have to go if the previous preg_replace call was used in an if.
    • It's best to keep temporary variables or the multiline code block structure though.

    And the replacement expression may demand more readability improvements or rework.

    • For instance stripslashes() often becomes redundant in literal expressions.
    • Variable-scope lookups require a use or global reference for/within the callback.
    • Unevenly quote-enclosed "-$1-$2" capture references will end up syntactically broken by the plain transformation into "-$m[1]-$m[2].

    The code output is merely a starting point. And yes, this would have been more useful as an online tool. This code rewriting approach (edit, run, edit, edit) is somewhat impractical. Yet could be more approachable to those who are accustomed to task-centric coding (more steps, more uncoveries). So this alternative might curb a few more duplicate questions.

提交回复
热议问题