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
@HamZaDzCyber​​DeV私は知っています。それが私がそれをpreg_replace_callbackに置き換えたい理由の1つです
ケーシー

2
のマニュアルページがありますpreg_replace_callback。そして、上記のコールバックに\\2なり$matches[2]ます。それとも、具体的にどの部分について混乱していますか?
マリオ2013年

@mario ahh $ matches [2]だけで十分でした。私はまだそれがどのように機能するかを理解していませんが、それは理解しています。あなたがそれを答えに入れるならば、私はそれが問題を解決しているとマークします。
ケーシー

3
使用しないでください。これはcreate_function、のもう1つのラッパーevalです。何らかの理由でPHP5.2に固執しない限り、適切な無名関数を使用する必要があります。
IMSoP 2013年

回答:


75

正規表現では、一致した文字列の一部を(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');

他の関数と同様に、デフォルトでは、コールバックの外部(周囲のスコープから)の変数にアクセスすることはできません。匿名関数を使用する場合、PHPマニュアルで説明されているように、useキーワードを使用してアクセスする必要のある変数をインポートできます。たとえば、古い議論が

'do_stuff(\\1, $foo)'

その場合、新しいコールバックは次のようになります。

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

ガッチャ

  • 正規表現の修飾子の代わりpreg_replace_callbackis使用する/eため、「pattern」引数からそのフラグを削除する必要があります。したがって、のようなパターン/blah(.*)blah/meiはになり/blah(.*)blah/miます。
  • /e修飾子は、バリアントの使用addslashes()いくつかの代替品を使用して、引数に内部的にstripslashes()それを削除します。ほとんどの場合、stripslashes新しいコールバックからへの呼び出しを削除することをお勧めします。

1

preg_replaceシムを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_replace れたpreg_replace_eval場所に編集するだけ /eです。

長所と短所

  • 本当にStackOverflowからのいくつかのサンプルでテストされました。
  • 簡単なケースのみをサポートします(変数ルックアップではなく関数呼び出し)。
  • さらにいくつかの制限と注意事項が含まれています。
  • 式の失敗に対して、ずれた、理解しにくいエラーが発生します。
  • ただし、それでも使用可能な一時的な解決策であり、への適切な移行を複雑にすることはありませんpreg_replace_callback
  • そして、ライセンスコメントは、人々がこれを使いすぎたり広めすぎたりするのを阻止することを目的としています。

置換コードジェネレータ

現在、これはやや冗長です。ただし、コードを手動でに再構築することにまだ圧倒されているユーザーには役立つ可能性がありますpreg_replace_callback。これは事実上より時間がかかりますが、コードジェネレーターは/e置換文字列を式に展開するための問題が少なくなります。これは非常に目立たない変換ですが、最も一般的な例にはおそらく十分です。

この関数を使用するには、壊れたpreg_replace呼び出しを編集しpreg_replace_eval_replacement1回実行します。これにより、代わりに使用される対応するブロックが印刷さ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]ます。

コード出力は単なる出発点です。はい、これはオンラインツールとしてもっと便利だったでしょう。このコード書き換えアプローチ(編集、実行、編集、編集)はやや実用的ではありません。それでも、タスク中心のコーディングに慣れている人にはもっと親しみやすいかもしれません(より多くのステップ、より多くの発見)。したがって、この代替案は、さらにいくつかの重複する質問を抑制する可能性があります。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.