web-dev-qa-db-ja.com

警告:preg_replace():不明な修飾子 ']'

次のエラーがあります。

警告:preg_replace():xxx.phpの行38の不明な修飾子 ']'

これは38行目のコードです:

<?php echo str_replace("</ul></div>", "", preg_replace("<div[^>]*><ul[^>]*>", "", wp_nav_menu(array('theme_location' => 'nav', 'echo' => false)) )); ?>

この問題を修正するにはどうすればよいですか?

40
user3122995

エラーが発生する理由

PHPでは、正規表現を delimiters のペアで囲む必要があります。区切り文字には、英数字、バックスラッシュ、空白文字以外を使用できます。 _/_、_#_、_~_は最も一般的に使用されるものです。開始および終了ブラケットが開始および終了区切り文字であるブラケットスタイルの区切り文字を使用することもできることに注意してください。つまり、_<pattern_goes_here>_、_[pattern_goes_here]_などがすべて有効です。

Unknown modifier X」エラーは通常、次の2つの場合に発生します。

  • 正規表現が区切り文字の欠落の場合。

  • 内部で区切り文字を使用する場合エスケープせずにパターンを使用します

この場合、正規表現は_<div[^>]*><ul[^>]*>_です。正規表現エンジンは、_<_から_>_までのすべてを正規表現パターンと見なし、その後すべてを修飾子と見なします。

_Regex: <div[^>  ]*><ul[^>]*>
       │     │  │          │
       └──┬──┘  └────┬─────┘
       pattern    modifiers
_

_]_は、不明な修飾子です。これは、閉じている_>_区切り文字の後に表示されるためです。 PHP=がそのエラーをスローするのはそのためです。

パターンに応じて、不明な修飾子の苦情は、_*_、_+_、p、_/_、または_)_または他のほとんどの文字に関するものである可能性があります/シンボル。 imsxeADSUXJuのみが 有効なPCRE修飾子 です。

修正方法

修正は簡単です。有効な区切り文字で正規表現パターンをラップするだけです。この場合、選択できます ~ そして、次を取得します。

_~<div[^>]*><ul[^>]*>~
│                   │
│                   └─ ending delimiter
└───────────────────── starting delimiter
_

区切り文字を使用しているにもかかわらずこのエラーを受け取った場合、パターン自体に、その区切り文字のエスケープされていないオカレンスが含まれている可能性があります。

または、区切り文字をエスケープします

_/foo[^/]+bar/i_は間違いなくエラーをスローします。だから、あなたはそれを使用してそれをエスケープすることができます \ 正規表現内のどこかにある場合はバックスラッシュ:

_/foo[^\/]+bar/i
│      │     │
└──────┼─────┴─ actual delimiters
       └─────── escaped slash(/) character
_

正規表現パターンに区切り文字が非常に多く含まれている場合、これは退屈な作業です。

もちろん、よりクリーンな方法は、まったく別の区切り文字を使用することです。理想的には、正規表現パターン内のどこにも現れない文字、たとえば_#_-_#foo[^/]+bar#i_です。

もっと読む:

88
Amal Murali

他の例

参照回答 は、「不明な修飾子」警告の理由をすでに説明しています。これは、他の典型的なバリアントの単なる比較です。

  • 正規表現_/_ delimiters _/_の追加を忘れると、最初の非文字記号は1と見なされます。したがって、警告は、多くの場合、グループ化_(…)_、_[…]_メタシンボルに続くものに関するものです。

    _preg_match("[a-zA-Z]+:\s*.$"
                ↑      ↑⬆
    _
  • 正規表現で既にカスタム区切り文字(ここでは_:_)が使用されていても、エスケープされていないリテラルと同じ文字が含まれている場合があります。それは時期尚早の区切り文字と間違えられます。これが、まさに次のシンボルが「Unknown modifier why」トロフィーを受け取る理由です。

    _preg_match(":\[[\d:/]+\]:"
                ↑     ⬆     ↑
    _
  • 古典的な_/_区切り文字を使用するときは、正規表現内に文字列を含めないように注意してください。これは、 エスケープされていないファイル名

    _preg_match("/pathname/filename/i"
                ↑        ⬆         ↑
    _

    または、アングル/角括弧スタイルと一致する場合 タグ

    _preg_match("/<%tmpl:id>(.*)</%tmpl:id>/Ui"
                ↑               ⬆         ↑
    _
  • テンプレートスタイル(SmartyまたはBBCode)の正規表現パターンには、多くの場合_{…}_または_[…]_ブラケットが必要です。通常、両方ともエスケープする必要があります。 (ただし、最も外側の_{}_ペアは例外です)。

    また、実際のデリミタが使用されていない場合、ペアデリミタとして誤って解釈されます。それらがリテラル文字としても使用されている場合、それはもちろん…エラーです。

    _preg_match("{bold[^}]+}"
                ↑      ⬆  ↑
    _
  • 警告に「Delimiterは英数字またはバックスラッシュであってはなりません」と表示された場合は、区切り文字も完全に忘れてしまいます。

    _preg_match("ab?c*"
                ↑
    _
  • 未知の修飾子 'g'」は、JavaScriptまたはPerlから逐語的にコピーされた正規表現を示すことがよくあります。

    _preg_match("/abc+/g"
                      ⬆
    _

    PHPは_/g_グローバルフラグを使用しません。代わりに、 _preg_replace_ 関数はすべてのオカレンスで機能し、 _preg_match_all_ は1回のオカレンスに対する「グローバル」検索ペンダントです _preg_match_

    したがって、_/g_フラグを削除するだけです。

    こちらもご覧ください:
    警告:preg_replace():不明な修飾子 'g'
    preg_replace:bad regex == 'Unknown Modifier'?

  • より特異なケースは、PCRE_EXTENDED _/x_ flagに関係します。これは、正規表現をより高品質で読みやすくするためによく使用されます(または使用する必要があります)。

    これにより、インライン_#_コメントを使用できます。 PHPは、PCREの上に正規表現区切り文字を実装します。ただし、_#_は特別な方法で処理されません。 _#_コメント内のリテラル区切り文字がエラーになる方法は次のとおりです。

    _preg_match("/
       ab?c+  # Comment with / slash in between
    /x"
    _

    (また、_#_を_#abc+#x_区切り文字として使用することは、お勧めできません。)

  • 変数を正規表現に補間するには、変数を事前にエスケープするか、それ自体が有効な正規表現である必要があります。これがうまくいくかどうかを事前に知ることはできません:

    _ preg_match("/id=$var;/"
                 ↑    ↺   ↑
    _

    そのような場合には $var = preg_quote($var, "/") を適用するのが最善です。

    こちらもご覧ください:
    ...の不明な修飾子 '/'?それは何ですか?

    別の選択肢は、引用符で囲まれていないリテラル文字列に_\Q…\E_エスケープを使用することです:

    _ preg_match("/id=\Q{$var}\E;/mix");
    _

    これはメタシンボルの便利なショートカットにすぎず、信頼できる/安全ではないことに注意してください。 _$var_にリテラル_'\E'_自体が含まれている場合は、バラバラになります(ただし、ありそうにないことです)。そして、それは not区切り文字をマスクします 自体です。

  • 非推奨の修飾子/ eはまったく別の問題です。これは区切り文字とは関係ありませんが、暗黙的な式解釈モードは段階的に廃止されます。参照: 非推奨のpreg_replace/eをpreg_replace_callbackに置き換えてください

代替正規表現の区切り文字

既に述べたように、このエラーの最も迅速な解決策は、明確な区切り文字を選択することです。任意の非文字記号を使用できます。視覚的に特徴的なものがしばしば好まれます:

  • _~abc+~_
  • _!abc+!_
  • _@abc+@_
  • _#abc+#_
  • _=abc+=_
  • _%abc+%_

技術的には、区切り文字に_$abc$_または_|abc|_を使用できます。ただし、正規表現のメタ文字自体として機能するシンボルは避けるのが最善です。

区切り文字としてのハッシュ_#_もかなり人気があります。ただし、x/_PCRE_EXTENDED_可読性修飾子と組み合わせて注意する必要があります。 _# inline_または_(?#…)_コメントを使用することはできません。これらは区切り文字として混乱するためです。

引用のみの区切り文字

ときどき_"_および_'_がPHP文字列エンクロージャーとしてのコンターパートとペアになっている正規表現の区切り文字として使用されていることがわかります。

_  preg_match("'abc+'"
  preg_match('"abc+"'
_

PHPに関する限り、これは完全に有効です。便利で目立たないこともありますが、IDEやエディターで常に読みやすいとは限りません。

ペアの区切り文字

興味深いバリエーションは、区切り文字のペアです。正規表現の両端で同じシンボルを使用する代わりに、任意の_<...>_ _(...)_ _[...]_ _{...}_ブラケット/ブレースの組み合わせを使用できます。

_  preg_match("(abc+)"   # just delimiters here, not a capture group
_

それらのほとんどは正規表現のメタ文字としても機能しますが、多くの場合、それ以上の努力なしで使用できます。正規表現内の特定のブレース/括弧が正しくペアリングまたはエスケープされている限り、これらのバリアントは非常に読みやすくなります。

派手な正規表現の区切り文字

やや怠laなトリック(これは推奨されません)は、印刷不可能なASCII文字を区切り文字として使用します。これは、PHPで正規表現文字列に二重引用符を使用し、区切り文字に8進エスケープを使用することで簡単に機能します。

_ preg_match("\001 abc+ \001mix"
_

_\001_は単なる制御文字です  通常は必要ありません。したがって、ほとんどの正規表現パターン内に表示されることはほとんどありません。非常に読みやすいわけではありませんが、ここで適切になります。

残念ながら、Unicodeグリプ__を区切り文字として使用することはできません。 PHPはシングルバイト文字のみを許可します。なんで?あなたが尋ねてくれてうれしいです:

PCRE上のPHP区切り文字

_preg_*_ 関数は [〜#〜] pcre [〜#〜] 正規表現エンジンを利用します。 Perlとの類似のために、_preg_*_関数はそれらを実装します。 定数としての定数 の代わりに 修飾子​​文字_/ism_ を使用できる理由でもあります。

正規表現文字列の前処理方法については、 ext/pcre/php_pcre.c をご覧ください。

  • 最初に、先頭の空白はすべて無視されます。

  • 英数字以外の記号はすべて、想定される区切り文字と見なされます。 PHPはシングルバイト文字のみを尊重することに注意してください。

    _delimiter = *p++;
    if (isalnum((int)*(unsigned char *)&delimiter) || delimiter == '\\') {
            php_error_docref(NULL,E_WARNING, "Delimiter must not…");
            return NULL;
    }
    _
  • 残りの正規表現文字列は左から右に走査されます。バックスラッシュ_\\_-エスケープされたシンボルのみが無視されます。 _\Q_および_\E_エスケープ は無視されます。

  • 区切り文字が再び見つかった場合、残りは修飾子文字のみを含むことが検証されます。

  • 区切り文字が_([{< )]}> )]}>_ペアリング可能な中括弧/括弧のいずれかである場合、処理ロジックはより複雑です。

    _int brackets = 1;   /* brackets nesting level */
    while (*pp != 0) {
            if (*pp == '\\' && pp[1] != 0) pp++;
            else if (*pp == end_delimiter && --brackets <= 0)
                    break;
            else if (*pp == start_delimiter)
                    brackets++;
            pp++;
    }
    _

    正しくペアになっている左右の区切り文字を探しますが、カウントするとき、他の中括弧/括弧タイプを無視します。

  • 生の正規表現文字列は、区切り文字と修飾子フラグが切り取られた後にのみPCREバックエンドに渡されます。

今、これはやや無関係です。しかし、区切り記号の警告の出所について説明します。そして、この手順全体は、最小限のPerl互換性を持つためのものです。もちろん、_[…]_文字クラスコンテキストがPHPで特別な扱いを受けていないなど、いくつかの小さな逸脱があります。

その他の参照

14
mario

警告の代わりに、またはpreg_last_error()を使用して例外(InvalidPatternException)を取得する場合は、 T-Regx library の使用を検討してください。

<?php
try 
{
    return pattern('invalid] pattern')->match($s)->all();
}
catch (InvalidPatternException $e) 
{
    // your pattern was invalid
}
0
Danon