PHPで前方一致検索を実装する方法について

採用はこちら

PHPで前方一致検索を実装する方法は、検索対象が 配列・文字列・データベース のどれかによって変わります。

前方一致検索とは、検索キーワードが文字列の先頭と一致するデータを探す検索方法です。

たとえば、検索キーワードが app の場合、次のようなデータが一致します。

apple
application
apparel

一方で、次のようなデータは一致しません。

banana
mapple
grape

mapple には app が含まれていますが、先頭が app ではないため、前方一致検索では不一致になります。

目次

PHPで文字列の前方一致を判定する方法

PHP 8以降は str_starts_with() を使う

PHP 8以降であれば、前方一致の判定には str_starts_with() を使うのが最もシンプルです。

$text = 'apple';
$keyword = 'app';

if (str_starts_with($text, $keyword)) {
    echo '前方一致しました';
} else {
    echo '一致しませんでした';
}

str_starts_with() は、第一引数の文字列が第二引数の文字列で始まっているかを true または false で返します。

str_starts_with('apple', 'app'); // true
str_starts_with('banana', 'app'); // false
str_starts_with('mapple', 'app'); // false

前方一致かどうかを直感的に判定できるため、PHP 8以降ではこの方法がおすすめです。

PHP 7以前は strpos() を使う

PHP 7以前では str_starts_with() が使えません。

その場合は、strpos() を使って、検索キーワードが文字列の先頭にあるかどうかを判定します。

$text = 'apple';
$keyword = 'app';

if (strpos($text, $keyword) === 0) {
    echo '前方一致しました';
} else {
    echo '一致しませんでした';
}

strpos() は、指定した文字列が見つかった位置を返します。

strpos('apple', 'app'); // 0
strpos('mapple', 'app'); // 1
strpos('banana', 'app'); // false

前方一致の場合は、検索キーワードが先頭にあるため、戻り値は 0 になります。

ここで重要なのは、必ず === 0 で比較することです。

strpos($text, $keyword) === 0

次のように == 0 で比較すると、false0 が同じように扱われてしまい、意図しない判定になる可能性があります。

// 非推奨
strpos($text, $keyword) == 0

PHPの配列から前方一致検索する方法

array_filter()str_starts_with() を組み合わせる

配列の中から、指定したキーワードで始まる要素だけを取り出したい場合は、array_filter() を使います。

$items = [
    'apple',
    'application',
    'banana',
    'apparel',
    'orange',
    'grape',
];

$keyword = 'app';

$results = array_filter($items, function ($item) use ($keyword) {
    return str_starts_with($item, $keyword);
});

print_r($results);

実行結果は次のようになります。

Array
(
    [0] => apple
    [1] => application
    [3] => apparel
)

array_filter() を使うと、条件に一致した要素だけを抽出できます。

ただし、このままだと配列のキーが元のまま残ります。

検索結果を連番にしたい場合は、array_values() を使います。

$results = array_values(array_filter($items, function ($item) use ($keyword) {
    return str_starts_with($item, $keyword);
}));

print_r($results);

実行結果は次のようになります。

Array
(
    [0] => apple
    [1] => application
    [2] => apparel
)

配列検索を関数化する

前方一致検索を何度も使う場合は、関数化しておくと便利です。

function filterByPrefix(array $items, string $keyword): array
{
    return array_values(array_filter($items, function ($item) use ($keyword) {
        return str_starts_with($item, $keyword);
    }));
}

使い方は次のとおりです。

$items = [
    'apple',
    'application',
    'banana',
    'apparel',
    'orange',
];

$results = filterByPrefix($items, 'app');

print_r($results);

実行結果は次のようになります。

Array
(
    [0] => apple
    [1] => application
    [2] => apparel
)

大文字・小文字を区別しない前方一致検索

英数字中心なら strtolower() を使う

通常、str_starts_with() は大文字・小文字を区別します。

str_starts_with('Apple', 'app'); // false

大文字・小文字を区別せずに検索したい場合は、比較する前に両方の文字列を小文字に変換します。

$text = 'Apple';
$keyword = 'app';

if (str_starts_with(strtolower($text), strtolower($keyword))) {
    echo '前方一致しました';
}

配列検索の場合は、次のように書けます。

$items = [
    'Apple',
    'application',
    'Banana',
    'APPAREL',
    'orange',
];

$keyword = 'app';

$results = array_values(array_filter($items, function ($item) use ($keyword) {
    return str_starts_with(
        strtolower($item),
        strtolower($keyword)
    );
}));

print_r($results);

実行結果は次のようになります。

Array
(
    [0] => Apple
    [1] => application
    [2] => APPAREL
)

多言語対応なら mb_strtolower() を検討する

英数字中心の検索であれば strtolower() でも問題ないケースが多いです。

ただし、日本語を含むサイトや多言語対応を考える場合は、mb_strtolower() を使うほうが安全です。

function startsWithInsensitive(string $text, string $keyword): bool
{
    $text = mb_strtolower($text, 'UTF-8');
    $keyword = mb_strtolower($keyword, 'UTF-8');

    return str_starts_with($text, $keyword);
}

使い方は次のとおりです。

if (startsWithInsensitive('Apple', 'app')) {
    echo '前方一致しました';
}

日本語で前方一致検索する方法

単純な前方一致なら str_starts_with() でも使える

日本語でも、UTF-8同士の文字列で単純に「先頭が一致するか」を判定するだけなら、str_starts_with() を使えます。

$text = '東京都';
$keyword = '東京';

if (str_starts_with($text, $keyword)) {
    echo '前方一致しました';
}

この場合、東京都東京 で始まっているため、前方一致になります。

文字数ベースで判定するなら mb_substr() を使う

文字数ベースで先頭から切り出して比較したい場合は、mb_substr()mb_strlen() を使う方法もあります。

$text = '東京都';
$keyword = '東京';

if (mb_substr($text, 0, mb_strlen($keyword), 'UTF-8') === $keyword) {
    echo '前方一致しました';
}

このコードでは、検索対象の文字列からキーワードの文字数分だけ先頭を取り出し、それがキーワードと一致するかを判定しています。

mb_substr('東京都', 0, mb_strlen('東京'), 'UTF-8');

この結果は 東京 になるため、前方一致と判定できます。

日本語配列から前方一致検索する

日本語の配列から前方一致する要素を取り出す場合は、次のように書けます。

$areas = [
    '東京都',
    '東京駅',
    '大阪府',
    '京都府',
    '東大阪市',
    '神奈川県',
];

$keyword = '東京';

$results = array_values(array_filter($areas, function ($area) use ($keyword) {
    return str_starts_with($area, $keyword);
}));

print_r($results);

実行結果は次のようになります。

Array
(
    [0] => 東京都
    [1] => 東京駅
)

東大阪市 で始まっていますが、東京 では始まっていないため、不一致になります。

オブジェクト配列・連想配列から前方一致検索する方法

特定のキーを対象に検索する

実務では、単純な文字列配列ではなく、連想配列を検索するケースも多いです。

たとえば、次のようなユーザー一覧があるとします。

$users = [
    ['id' => 1, 'name' => '佐藤太郎'],
    ['id' => 2, 'name' => '佐々木花子'],
    ['id' => 3, 'name' => '田中一郎'],
    ['id' => 4, 'name' => '鈴木次郎'],
];

$keyword = '佐';

name で始まるユーザーだけを取得する場合は、次のように書きます。

$results = array_values(array_filter($users, function ($user) use ($keyword) {
    return str_starts_with($user['name'], $keyword);
}));

print_r($results);

実行結果は次のようになります。

Array
(
    [0] => Array
        (
            [id] => 1
            [name] => 佐藤太郎
        )

    [1] => Array
        (
            [id] => 2
            [name] => 佐々木花子
        )
)

キーの存在チェックを入れるとより安全

実務では、必ず name キーが存在するとは限りません。

そのため、安全に書くなら次のようにします。

$results = array_values(array_filter($users, function ($user) use ($keyword) {
    if (!isset($user['name'])) {
        return false;
    }

    return str_starts_with($user['name'], $keyword);
}));

このようにしておくと、name キーが存在しないデータが混ざっていてもエラーを防ぎやすくなります。

検索フォームで前方一致検索を実装する方法

HTMLフォームを用意する

まずは、検索キーワードを入力するフォームを用意します。

<form method="get">
    <input type="text" name="keyword" placeholder="検索キーワード">
    <button type="submit">検索</button>
</form>

PHPで検索処理を書く

フォームから送信されたキーワードを受け取り、配列に対して前方一致検索を行います。

$items = [
    'apple',
    'application',
    'banana',
    'apparel',
    'orange',
];

$keyword = trim($_GET['keyword'] ?? '');
$results = [];

if ($keyword !== '') {
    $results = array_values(array_filter($items, function ($item) use ($keyword) {
        return str_starts_with($item, $keyword);
    }));
}

ここで trim() を使って、前後の空白を取り除いています。

また、キーワードが空文字の場合は検索しないようにしています。

if ($keyword !== '') {
    // 検索処理
}

空文字のまま検索すると、すべての文字列が一致する可能性があるためです。

検索結果を表示する

検索結果を画面に表示する場合は、htmlspecialchars() を使ってエスケープします。

<?php if ($keyword !== ''): ?>
    <h2>検索結果</h2>

    <?php if (!empty($results)): ?>
        <ul>
            <?php foreach ($results as $result): ?>
                <li><?= htmlspecialchars($result, ENT_QUOTES, 'UTF-8') ?></li>
            <?php endforeach; ?>
        </ul>
    <?php else: ?>
        <p>該当するデータはありません。</p>
    <?php endif; ?>
<?php endif; ?>

ユーザーが入力した値や、データベース・配列から取得した値をHTMLに出力する場合は、XSS対策として htmlspecialchars() を使うのが基本です。

MySQLで前方一致検索する方法

SQLでは LIKE 'キーワード%' を使う

MySQLなどのデータベースで前方一致検索をする場合は、SQLの LIKE を使います。

SELECT *
FROM users
WHERE name LIKE '佐%';

% は、任意の0文字以上に一致するワイルドカードです。

'佐%'

は、「佐で始まる文字列」を意味します。

前方一致・後方一致・部分一致の違い

LIKE では、% を付ける位置によって検索方法が変わります。

検索方法書き方意味
前方一致keyword%keywordで始まる
後方一致%keywordkeywordで終わる
部分一致%keyword%keywordを含む

PHPでパラメータを作る場合は、次のようになります。

// 前方一致
$keyword . '%';

// 後方一致
'%' . $keyword;

// 部分一致
'%' . $keyword . '%';

前方一致検索をしたい場合は、キーワードの後ろにだけ % を付けます。

PHPとPDOでMySQLの前方一致検索を実装する方法

PDOのプリペアドステートメントを使う

PHPからMySQLに対して前方一致検索を行う場合は、PDOのプリペアドステートメントを使います。

$pdo = new PDO(
    'mysql:host=localhost;dbname=test_db;charset=utf8mb4',
    'db_user',
    'db_password',
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ]
);

$keyword = trim($_GET['keyword'] ?? '');
$users = [];

if ($keyword !== '') {
    $stmt = $pdo->prepare(
        'SELECT id, name, email FROM users WHERE name LIKE :keyword'
    );

    $stmt->bindValue(':keyword', $keyword . '%', PDO::PARAM_STR);
    $stmt->execute();

    $users = $stmt->fetchAll();
}

ポイントは、次の部分です。

$stmt->bindValue(':keyword', $keyword . '%', PDO::PARAM_STR);

検索キーワードの後ろに % を付けることで、前方一致検索になります。

空文字の場合は検索しないようにする

検索キーワードが空文字の場合、次のような条件になる可能性があります。

LIKE '%'

これは、ほぼ全件に一致する条件です。

そのため、検索フォームでは空文字の場合に検索しない設計にするのが安全です。

$keyword = trim($_GET['keyword'] ?? '');
$users = [];

if ($keyword === '') {
    $users = [];
} else {
    $stmt = $pdo->prepare(
        'SELECT id, name, email FROM users WHERE name LIKE :keyword'
    );

    $stmt->bindValue(':keyword', $keyword . '%', PDO::PARAM_STR);
    $stmt->execute();

    $users = $stmt->fetchAll();
}

LIKE%_ を通常文字として扱う方法

プリペアドステートメントだけではワイルドカードの意味は消えない

PDOのプリペアドステートメントは、SQLインジェクション対策として重要です。

ただし、LIKE における %_ のワイルドカードとしての意味までは消してくれません。

SQLの LIKE では、次の文字に特別な意味があります。

文字意味
%任意の0文字以上
_任意の1文字

たとえば、ユーザーが % を入力した場合、そのまま LIKE に使うと、意図せず多くのデータに一致する可能性があります。

LIKE 用のエスケープ関数を作る

%_ を通常の文字として検索したい場合は、LIKE用のエスケープ処理を入れます。

function escapeLike(string $value, string $escapeChar = '\\'): string
{
    return str_replace(
        [$escapeChar, '%', '_'],
        [$escapeChar . $escapeChar, $escapeChar . '%', $escapeChar . '_'],
        $value
    );
}

使い方は次のとおりです。

$keyword = trim($_GET['keyword'] ?? '');
$users = [];

if ($keyword !== '') {
    $escapedKeyword = escapeLike($keyword);

    $stmt = $pdo->prepare(
        "SELECT id, name, email
         FROM users
         WHERE name LIKE :keyword ESCAPE '\\\\'
         ORDER BY name ASC
         LIMIT 50"
    );

    $stmt->bindValue(':keyword', $escapedKeyword . '%', PDO::PARAM_STR);
    $stmt->execute();

    $users = $stmt->fetchAll();
}

このようにすると、ユーザーが %_ を入力した場合でも、ワイルドカードではなく通常文字として扱いやすくなります。

実務向けの前方一致検索フォーム例

検索処理のサンプル

以下は、PDOを使った実務向けの前方一致検索の例です。

<?php
function escapeLike(string $value, string $escapeChar = '\\'): string
{
    return str_replace(
        [$escapeChar, '%', '_'],
        [$escapeChar . $escapeChar, $escapeChar . '%', $escapeChar . '_'],
        $value
    );
}

$pdo = new PDO(
    'mysql:host=localhost;dbname=test_db;charset=utf8mb4',
    'db_user',
    'db_password',
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ]
);

$keyword = trim($_GET['keyword'] ?? '');
$users = [];

if ($keyword !== '') {
    $escapedKeyword = escapeLike($keyword);

    $stmt = $pdo->prepare(
        "SELECT id, name, email
         FROM users
         WHERE name LIKE :keyword ESCAPE '\\\\'
         ORDER BY name ASC
         LIMIT 50"
    );

    $stmt->bindValue(':keyword', $escapedKeyword . '%', PDO::PARAM_STR);
    $stmt->execute();

    $users = $stmt->fetchAll();
}
?>

HTMLで検索フォームと結果を表示する

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>前方一致検索</title>
</head>
<body>

<form method="get">
    <input
        type="text"
        name="keyword"
        value="<?= htmlspecialchars($keyword, ENT_QUOTES, 'UTF-8') ?>"
        placeholder="名前を入力"
    >
    <button type="submit">検索</button>
</form>

<?php if ($keyword !== ''): ?>
    <h2>検索結果</h2>

    <?php if (!empty($users)): ?>
        <ul>
            <?php foreach ($users as $user): ?>
                <li>
                    <?= htmlspecialchars($user['name'], ENT_QUOTES, 'UTF-8') ?>
                    /
                    <?= htmlspecialchars($user['email'], ENT_QUOTES, 'UTF-8') ?>
                </li>
            <?php endforeach; ?>
        </ul>
    <?php else: ?>
        <p>該当するユーザーは見つかりませんでした。</p>
    <?php endif; ?>
<?php endif; ?>

</body>
</html>

この実装では、次の対策を入れています。

対策内容
SQLインジェクション対策PDOのプリペアドステートメントを使用
LIKEワイルドカード対策%_ をエスケープ
XSS対策htmlspecialchars() で出力をエスケープ
大量取得対策空文字では検索しない
件数制限LIMIT 50 を指定
並び順ORDER BY name ASC を指定

Laravelで前方一致検索を実装する方法

Eloquentで前方一致検索する

LaravelでEloquentを使う場合は、次のように書けます。

$keyword = trim(request('keyword', ''));

$users = $keyword === ''
    ? collect()
    : User::where('name', 'LIKE', $keyword . '%')
        ->orderBy('name')
        ->limit(50)
        ->get();

where() の第2引数に LIKE を指定し、第3引数に $keyword . '%' を渡すことで前方一致検索になります。

クエリビルダで前方一致検索する

クエリビルダを使う場合は、次のように書けます。

$keyword = trim(request('keyword', ''));

$users = $keyword === ''
    ? collect()
    : DB::table('users')
        ->where('name', 'LIKE', $keyword . '%')
        ->orderBy('name')
        ->limit(50)
        ->get();

Laravelでも、空文字の場合に全件取得しないようにするのが重要です。

複数カラムを前方一致検索する

名前またはメールアドレスで前方一致検索したい場合は、次のように書けます。

$keyword = trim(request('keyword', ''));

$users = $keyword === ''
    ? collect()
    : User::where(function ($query) use ($keyword) {
            $query->where('name', 'LIKE', $keyword . '%')
                  ->orWhere('email', 'LIKE', $keyword . '%');
        })
        ->orderBy('name')
        ->limit(50)
        ->get();

where() の中でクロージャを使うことで、複数条件をまとめて指定できます。

前方一致検索でインデックスを活用する

LIKE 'keyword%' はインデックスを使いやすい

MySQLでは、検索対象のカラムにインデックスを作成しておくと、前方一致検索で検索速度を改善できる場合があります。

CREATE INDEX idx_users_name ON users(name);

前方一致検索は、次のような条件です。

WHERE name LIKE '佐%'

このような検索は、先頭から文字列を比較できるため、通常のB-treeインデックスを活用しやすい傾向があります。

LIKE '%keyword%' はインデックスが効きにくい

一方で、次のような部分一致検索は、通常のB-treeインデックスが効きにくくなります。

WHERE name LIKE '%佐%'

先頭に % があると、文字列のどこにキーワードが出てくるかわからないため、インデックスを使いにくくなるからです。

そのため、要件として問題がなければ、部分一致よりも前方一致のほうが検索速度の面で有利になりやすいです。

ただし、実際にインデックスが使われるかどうかは、DBの種類、照合順序、データ量、SQLの書き方によって変わります。

必要に応じて、EXPLAIN を使って確認しましょう。

EXPLAIN
SELECT id, name, email
FROM users
WHERE name LIKE '佐%';

日本語検索で考慮したい正規化

表記ゆれを考慮する

日本語の検索では、単純な前方一致だけではユーザーの入力ゆれに対応しきれないことがあります。

たとえば、次のような表記ゆれがあります。

入力ゆれ
全角・半角トウキョウ / トウキョウ
ひらがな・カタカナとうきょう / トウキョウ
大文字・小文字ABC / abc
スペース東京 駅 / 東京駅
表記ゆれ斎藤 / 齋藤 / サイトウ

これらに対応したい場合は、検索前に文字列を正規化する設計が必要です。

mb_convert_kana() で全角・半角を寄せる

半角カナや全角英数字などをある程度そろえたい場合は、mb_convert_kana() を使えます。

function normalizeForSearch(string $value): string
{
    $value = trim($value);
    $value = mb_convert_kana($value, 'asKV', 'UTF-8');
    $value = mb_strtolower($value, 'UTF-8');

    return $value;
}

この関数では、検索用に文字列を整えています。

$keyword = normalizeForSearch($_GET['keyword'] ?? '');

ただし、mb_convert_kana() だけですべての表記ゆれを吸収できるわけではありません。

サイトの要件に合わせて、どこまで正規化するかを決めることが重要です。

表示用カラムと検索用カラムを分ける

実務では、表示用の値と検索用の値を分ける設計がよく使われます。

name        : 東京タワー
name_search : トウキョウタワー

表示するときは name を使い、検索するときは name_search を使います。

$keyword = normalizeForSearch($_GET['keyword'] ?? '');

$stmt = $pdo->prepare(
    'SELECT id, name FROM users WHERE name_search LIKE :keyword LIMIT 50'
);

$stmt->bindValue(':keyword', $keyword . '%', PDO::PARAM_STR);
$stmt->execute();

$users = $stmt->fetchAll();

このようにすると、ユーザーに見せる文字列は自然なまま保ちつつ、検索しやすい形でデータを扱えます。

サジェスト検索で前方一致検索を使う方法

APIとして検索結果を返す

前方一致検索は、検索ボックスのサジェスト機能でもよく使われます。

たとえば、ユーザーが文字を入力するたびに、候補を取得するAPIを作る場合です。

<?php
header('Content-Type: application/json; charset=utf-8');

$pdo = new PDO(
    'mysql:host=localhost;dbname=test_db;charset=utf8mb4',
    'db_user',
    'db_password',
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ]
);

$keyword = trim($_GET['keyword'] ?? '');

if ($keyword === '') {
    echo json_encode([]);
    exit;
}

$stmt = $pdo->prepare(
    'SELECT id, name
     FROM users
     WHERE name LIKE :keyword
     ORDER BY name ASC
     LIMIT 10'
);

$stmt->bindValue(':keyword', $keyword . '%', PDO::PARAM_STR);
$stmt->execute();

$users = $stmt->fetchAll();

echo json_encode($users, JSON_UNESCAPED_UNICODE);

サジェスト検索では、候補数を絞ることが重要です。

LIMIT 10

のように件数制限を入れておくと、レスポンスの肥大化を防ぎやすくなります。

前方一致検索を実装するときの注意点

空文字検索をどう扱うか決める

空文字で検索したときに全件表示するのか、検索しないのかは、事前に決めておく必要があります。

検索フォームやサジェスト機能では、空文字の場合は検索しない設計が安全です。

if ($keyword === '') {
    $users = [];
}

検索結果の件数を制限する

前方一致検索でも、条件によっては大量のデータが一致します。

そのため、SQLでは LIMIT を指定するのがおすすめです。

LIMIT 50

サジェスト検索なら、さらに少なくしてもよいでしょう。

LIMIT 10

並び順を明示する

検索結果の並び順も明示しておくと、ユーザーにとって見やすくなります。

ORDER BY name ASC

更新日時順にしたい場合は、次のようにします。

ORDER BY updated_at DESC

並び順を指定しないと、表示順が不安定になることがあります。

検索対象カラムにインデックスを作成する

データ件数が多い場合は、検索対象のカラムにインデックスを作成しましょう。

CREATE INDEX idx_users_name ON users(name);

特に、ユーザー名、商品名、カテゴリ名、メールアドレスなどを検索対象にする場合は、インデックス設計が重要です。

出力時は必ずエスケープする

検索結果をHTMLに表示するときは、htmlspecialchars() を使います。

echo htmlspecialchars($user['name'], ENT_QUOTES, 'UTF-8');

検索処理そのものが安全でも、画面出力時にエスケープしていないとXSSのリスクがあります。

前方一致検索でよくある間違い

部分一致検索になっている

前方一致検索をしたいのに、次のように書いてしまうケースがあります。

$stmt->bindValue(':keyword', '%' . $keyword . '%');

これは前方一致ではなく、部分一致検索です。

前方一致検索にする場合は、キーワードの後ろにだけ % を付けます。

$stmt->bindValue(':keyword', $keyword . '%');

strpos()== 0 で比較している

PHP 7以前で strpos() を使う場合、次のような書き方は避けましょう。

if (strpos($text, $keyword) == 0) {
    echo '前方一致しました';
}

正しくは、厳密比較の === を使います。

if (strpos($text, $keyword) === 0) {
    echo '前方一致しました';
}

strpos() は見つからない場合に false を返すため、0false を区別する必要があります。

空文字で全件取得してしまう

検索キーワードが空文字の場合、次のような条件になることがあります。

WHERE name LIKE '%'

この条件は多くのデータに一致するため、意図せず全件取得に近い状態になる可能性があります。

$keyword = trim($_GET['keyword'] ?? '');

if ($keyword === '') {
    $users = [];
}

空文字の場合の扱いは、必ず実装しておきましょう。

検索結果をエスケープせずに表示している

検索結果をそのままHTMLに出力するのは危険です。

echo $user['name'];

安全に表示するには、次のようにします。

echo htmlspecialchars($user['name'], ENT_QUOTES, 'UTF-8');

ユーザー入力やデータベースの値をHTMLに表示する場合は、基本的にエスケープが必要です。

目的別のおすすめ実装方法

PHPの配列を検索する場合

PHP 8以降なら、array_filter()str_starts_with() を使うのがおすすめです。

$results = array_values(array_filter($items, function ($item) use ($keyword) {
    return str_starts_with($item, $keyword);
}));

PHP 7以前で前方一致判定する場合

PHP 7以前では、strpos() を使います。

if (strpos($text, $keyword) === 0) {
    echo '前方一致しました';
}

MySQLで前方一致検索する場合

MySQLでは、LIKEkeyword% を指定します。

SELECT *
FROM users
WHERE name LIKE 'keyword%';

PHPから実行する場合は、PDOのプリペアドステートメントを使います。

$stmt = $pdo->prepare('SELECT * FROM users WHERE name LIKE :keyword');
$stmt->bindValue(':keyword', $keyword . '%', PDO::PARAM_STR);
$stmt->execute();

Laravelで前方一致検索する場合

Laravelでは、Eloquentやクエリビルダで LIKE を使います。

$users = User::where('name', 'LIKE', $keyword . '%')->get();

空文字対策や件数制限を入れるなら、次のようにします。

$keyword = trim(request('keyword', ''));

$users = $keyword === ''
    ? collect()
    : User::where('name', 'LIKE', $keyword . '%')
        ->limit(50)
        ->get();

まとめ

PHPで前方一致検索を実装する場合、検索対象によって使う方法が変わります。

PHPの文字列で判定するなら、PHP 8以降では str_starts_with() を使うのが最も簡単です。

str_starts_with($text, $keyword)

PHP 7以前では、strpos() を使って先頭位置が 0 かどうかを判定します。

strpos($text, $keyword) === 0

配列から前方一致するデータを取り出す場合は、array_filter() と組み合わせます。

$results = array_values(array_filter($items, function ($item) use ($keyword) {
    return str_starts_with($item, $keyword);
}));

MySQLで前方一致検索をする場合は、LIKE にキーワードと % を組み合わせます。

WHERE name LIKE 'keyword%'

PHPからMySQLにアクセスする場合は、PDOのプリペアドステートメントを使い、次のように実装します。

$stmt = $pdo->prepare('SELECT * FROM users WHERE name LIKE :keyword');
$stmt->bindValue(':keyword', $keyword . '%', PDO::PARAM_STR);
$stmt->execute();

実務では、前方一致検索のロジックだけでなく、次の点も重要です。

項目対策
空文字検索空文字では検索しない、または仕様を明確にする
SQLインジェクションPDOのプリペアドステートメントを使う
LIKEワイルドカード%_ を通常文字として扱うならエスケープする
XSS出力時に htmlspecialchars() を使う
パフォーマンスLIMITORDER BY、インデックスを考慮する
日本語検索表記ゆれに対応するなら正規化や検索用カラムを検討する

前方一致検索は、部分一致検索よりも検索条件を絞りやすく、インデックスも活用しやすい検索方法です。

検索フォーム、サジェスト機能、ユーザー検索、商品検索など、幅広い場面で使えるため、基本的な実装方法と注意点を押さえておくと実務でも役立ちます。

以上、PHPで前方一致検索を実装する方法についてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次