PHP Tips | 文字列処理:検索・置換 – 複数キーワードのいずれかを含むか

PHP PHP
スポンサーリンク

「複数キーワードのいずれかを含むか」とは何をしたいのか

やりたいことはこうです。

「この文章の中に、NGワード一覧のどれか1つでも含まれていたらアウトにしたい」

イメージしやすい例でいうと:

本文: "本日は大特価セール開催中です"

キーワード一覧:
- "セール"
- "無料"
- "当選"

→ "セール" が含まれているので true(いずれかを含む)

業務だと、こんな場面でよく使います。

  • コメントやレビューに、禁止ワードが含まれていないかチェックする
  • ログの1行に、監視対象のキーワードが含まれているか調べる
  • メール本文に、特定の注意文言が入っているか確認する

「1つのキーワード」ではなく、「複数キーワードのどれか1つでも含まれているか」がポイントです。


ベースになるのは「1つのキーワードの部分一致チェック」

まずは単体版の contains を思い出す

前にやった「部分一致チェック」を、もう一度整理します。

function contains(string $haystack, string $needle): bool
{
    return mb_strpos($haystack, $needle, 0, 'UTF-8') !== false;
}
PHP
  • $haystack が「探される側」
  • $needle が「探すキーワード」
  • 見つかれば true、見つからなければ false

これを「1キーワード版の部品」として使います。


複数キーワード版の考え方:配列をループして、どれか1つでも当たればOK

ロジックを言葉で分解する

「複数キーワードのいずれかを含むか」は、こう考えられます。

  1. キーワード一覧(配列)を1つずつ取り出す
  2. そのキーワードが含まれているか(contains)をチェックする
  3. 1つでも true になったら、全体として true
  4. 最後まで1つも見つからなければ false

つまり、「OR 条件のループ」です。

実装例:anyContains ユーティリティ

/**
 * 複数キーワードのいずれかを含むかチェック
 *
 * @param string   $haystack  調べたい文字列
 * @param string[] $keywords  キーワード配列
 * @return bool
 */
function anyContains(string $haystack, array $keywords): bool
{
    foreach ($keywords as $keyword) {
        if ($keyword === '') {
            // 空文字はスキップするか、常に true にするかは仕様次第
            continue;
        }

        if (mb_strpos($haystack, $keyword, 0, 'UTF-8') !== false) {
            return true;
        }
    }

    return false;
}
PHP

ここでは、contains() を中で呼ばずに、
そのまま mb_strpos を書いていますが、こうしてもOKです。

function anyContains(string $haystack, array $keywords): bool
{
    foreach ($keywords as $keyword) {
        if ($keyword === '') {
            continue;
        }

        if (contains($haystack, $keyword)) {
            return true;
        }
    }

    return false;
}
PHP

例題で動きをしっかりイメージする

例1:禁止ワードチェック

$ngWords = [
    "死ね",
    "バカ",
    "違法",
];

$comment = "この商品は違法コピーでは?";

if (anyContains($comment, $ngWords)) {
    echo "禁止ワードが含まれています";
} else {
    echo "OK";
}
PHP

この場合、

  • "死ね" → 含まれていない
  • "バカ" → 含まれていない
  • "違法" → 含まれている

ので、anyContains は true を返します。

例2:監視ログのキーワード検知

$watchWords = [
    "ERROR",
    "FATAL",
    "CRITICAL",
];

$line = "[INFO] all good";

if (anyContains($line, $watchWords)) {
    // 重要ログとして扱う
}
PHP

この場合はどれも含まれていないので false。
[ERROR][FATAL] を含む行だけを拾いたいときに、そのまま使えます。


大文字・小文字を無視したい場合

そのままだと “error” と “ERROR” は別物

さっきのログ例で、こういうケースを考えます。

$watchWords = ["error"];

$line = "[ERROR] something happened";
PHP

今の anyContains だと、"error""ERROR" は別物なので、
anyContains($line, $watchWords) は false になります。

英字の検索で「大文字・小文字を区別したくない」ことは多いので、
ケースインセンシティブ版を用意しておくと便利です。

大文字・小文字を無視する anyContainsIgnoreCase

function anyContainsIgnoreCase(string $haystack, array $keywords): bool
{
    // 調べる側を小文字にそろえる
    $haystackLower = mb_strtolower($haystack, 'UTF-8');

    foreach ($keywords as $keyword) {
        if ($keyword === '') {
            continue;
        }

        $keywordLower = mb_strtolower($keyword, 'UTF-8');

        if (mb_strpos($haystackLower, $keywordLower, 0, 'UTF-8') !== false) {
            return true;
        }
    }

    return false;
}
PHP

これなら、

$watchWords = ["error"];

$line1 = "[ERROR] something happened";
$line2 = "[Error] something happened";

anyContainsIgnoreCase($line1, $watchWords); // true
anyContainsIgnoreCase($line2, $watchWords); // true
PHP

のように、「大文字・小文字の違い」を気にせず判定できます。


設計上の細かいポイントを少し深掘りする

空文字をどう扱うか

$keywords の中に ""(空文字)が入っていた場合、
どう扱うかは仕様次第です。

  • 「空文字は無視する(スキップ)」
  • 「空文字があったら常に true(何にでも含まれているとみなす)」

多くのケースでは「スキップ」が無難なので、
上の実装では if ($keyword === '') continue; としています。

キーワードが多いときのパフォーマンス

anyContains は、キーワードの数だけ mb_strpos を呼びます。

  • キーワードが数個〜数十個程度なら、まったく問題なし
  • 数千〜数万になると、さすがに重くなる可能性がある

その場合は、正規表現でまとめて判定する、
インデックスを作る、など別のアプローチが必要になりますが、
「業務アプリでの禁止ワードチェック」レベルなら、
配列ループで十分です。


まとめ:今日からの「複数キーワードのいずれかを含むか」ユーティリティ

押さえておきたいポイントをコンパクトにまとめます。

  • 「複数キーワードのいずれかを含むか」は、
    「キーワード配列をループして、1つでも部分一致したら true」という OR 条件。
  • UTF-8 前提なら、mb_strpos を使って実装するのが安全。
  • 大文字・小文字を無視したい場合は、両方を小文字にそろえてから検索する。
  • 実務では、anyContains() / anyContainsIgnoreCase() のようなユーティリティ関数にしておくと、
    禁止ワードチェックやログ監視などで何度も使い回せる。

ベースになる実装は、まずはこれで十分です。

function anyContains(string $haystack, array $keywords): bool
{
    foreach ($keywords as $keyword) {
        if ($keyword === '') {
            continue;
        }

        if (mb_strpos($haystack, $keyword, 0, 'UTF-8') !== false) {
            return true;
        }
    }

    return false;
}
PHP

タイトルとURLをコピーしました