「複数キーワードのいずれかを含むか」とは何をしたいのか
やりたいことはこうです。
「この文章の中に、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つずつ取り出す
- そのキーワードが含まれているか(contains)をチェックする
- 1つでも true になったら、全体として true
- 最後まで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