preg_replace () e 수정자를 preg_replace_callback으로 바꿉니다.


83

정규 표현식이 끔찍합니다. 나는 이것을 대체하려고 해요 :

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

익명 함수가있는 preg_replace_callback을 사용합니다. \\ 2이 (가) 무엇을하는지 이해할 수 없습니다. 또는 그 문제에 대해 preg_replace_callback이 정확히 어떻게 작동하는지.

이를 달성하기위한 올바른 코드는 무엇입니까?


1
전자 되고 수정 되지 PHP 5.5.0의로
함자

8
@HamZaDzCyberDeV 알아요. 그건 내가 preg_replace_callback로 교체하려는 이유 중 하나
케이시

2
에 대한 매뉴얼 페이지가 preg_replace_callback있습니다. 그리고 \\2이 될 것이다 $matches[2]고 말했다 콜백. 아니면 어떤 부분에 대해 구체적으로 혼란 스럽습니까?
마리오

@mario ahh $ matches [2]가 필요한 전부였습니다. 나는 여전히 그것이 어떻게 작동하는지 이해하지 못하지만 작동합니다. 대답에 넣으면 문제 해결이라고 표시하겠습니다.
Casey

3
사용하지 마십시오 create_function. 그것은 단지 또 다른 래퍼 일뿐 eval입니다. 어떤 이유로 PHP 5.2에 갇혀 있지 않는 한 적절한 익명 함수를 사용해야합니다.
IMSoP 2013 년

답변:


75

정규식에서 일치하는 문자열의 일부를 "캡처"할 수 있습니다 (brackets). 이 경우, 당신은 경기 의 (^|_)([a-z])부분을 캡처하고 있습니다. 이것들은 1부터 시작하여 번호가 매겨져 있으므로 역 참조 1과 2가 있습니다. Match 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');

모든 함수와 마찬가지로 기본적으로 콜백 외부의 변수 (주변 범위에서)에 액세스 할 수 없습니다. 익명 함수를 사용할 때 PHP 매뉴얼에서 논의 된 것처럼use 키워드를 사용하여 액세스해야하는 변수를 가져올 수 있습니다 . 예를 들어 이전 인수가

'do_stuff(\\1, $foo)'

새 콜백은 다음과 같을 수 있습니다.

function($m) use ($foo) { return do_stuff($m[1], $foo); }

Gotchas

  • 의 사용 preg_replace_callback입니다 대신/e 당신이 당신의 "패턴"인수에서 해당 플래그를 제거 할 필요가 있으므로, 정규식에 대한 수정. 그래서 패턴 /blah(.*)blah/mei/blah(.*)blah/mi.
  • /e수정이의 변형 사용하는 addslashes()일부 대체 사용, 그래서 인수에 내부를 stripslashes()제거하기 위해; 대부분의 경우 stripslashes새 콜백에서에 대한 호출을 제거 할 수 있습니다.

1

preg_replace shim with eval 지원

이것은 매우 바람직하지 않습니다. 그러나 프로그래머가 아니거나 정말 끔찍한 코드를 선호한다면 대체 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_replacepreg_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";
}

단순한 복사 및 붙여 넣기는 프로그래밍 이 아닙니다 . 생성 된 코드를 실제 입력 / 출력 변수 이름 또는 사용 컨텍스트에 맞게 다시 조정해야합니다.

  • 구체적으로 $OUTPUT =이전 preg_replace호출이 if.
  • 하지만 임시 변수 또는 여러 줄 코드 블록 구조를 유지하는 것이 가장 좋습니다.

그리고 대체 표현은 더 많은 가독성 향상이나 재 작업을 요구할 수 있습니다.

  • 예를 들어 stripslashes()리터럴 표현에서는 종종 중복됩니다.
  • 가변 범위 조회에는 콜백에 대한 use또는 global참조가 필요합니다 .
  • 따옴표로 묶인 "-$1-$2"캡처 참조가 균등하지 않게 일반 변환에 의해 "-$m[1]-$m[2].

코드 출력은 단지 시작점 일뿐입니다. 그리고 예, 이것은 온라인 도구로 더 유용했을 것입니다. 이 코드 재 작성 방식 (편집, 실행, 편집, 편집)은 다소 비실용적입니다. 그러나 작업 중심 코딩에 익숙한 사람들에게 더 접근하기 쉬울 수 있습니다 (더 많은 단계, 더 많은 발견). 따라서이 대안은 몇 가지 중복 질문을 더 억제 할 수 있습니다.


0

플래그 e(또는 eval일반적으로)를 사용하면 안됩니다 .

T-Regx 라이브러리를 사용할 수도 있습니다.

pattern('(^|_)([a-z])')->replace($word)->by()->group(2)->callback('strtoupper');
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.