初心者向け:PHP strpos関数を使って文字列内の位置を取得する方法

基本文法・構文ガイド
  • カテゴリ: 文字列操作(PHP標準関数)
  • 対象バージョン: PHP 7.4 〜 8.4(offsetの負数指定はPHP 7.1以降、needleへのint渡しはPHP 8.0で廃止)
  • シグネチャ: strpos(string $haystack, string $needle, int $offset = 0): int|false
  • 関連: stripos / strrpos / str_contains / str_starts_with / mb_strpos
  • 変更履歴: PHP 7.1でoffsetの負数指定に対応/PHP 8.0でneedleへのint渡しが廃止され空文字列は許可に/PHP 8.0でoffsetがhaystackの文字数を超えるとValueError

要点(TL;DR)

  • strpos()は文字列$haystackの中で$needleが最初に見つかる位置(0始まりの整数)を返す。見つからなければfalse
  • 最重要の罠: 先頭(0文字目)で見つかった場合も戻り値は0if ($pos)ではなく必ず!== false / === falseで判定する
  • 「含むかどうか」だけ知りたいならPHP 8.0+のstr_contains()、日本語などマルチバイト文字はmb_strpos()を使う

概要

strposは、文字列$haystackの中に別の文字列$needleが含まれているか、含まれているならどの位置にあるかを調べるPHPの標準関数です。バリデーションでのキーワード判定、ログやURLからの部分文字列抽出、テンプレート処理など幅広い場面で使われる基本関数ですが、戻り値の0falseを見分けられずにバグを作り込みやすい関数としても知られています。strposバイト単位で位置を数えるため、日本語のようなマルチバイト文字列を扱う場合はmb_strposを使う必要がある点にも注意してください。

構文 / シグネチャ

int|false strpos(string $haystack, string $needle, int $offset = 0)

引数

引数必須既定値説明
$haystackstring必須検索対象の文字列
$needlestring必須探したい部分文字列(PHP 8.0以降は空文字列も指定可能)
$offsetint任意0検索開始位置。正の値は先頭からの文字数、負の値は末尾から数えた位置(PHP 7.1以降)
  • 戻り値:見つかった位置(0始まりの整数)、見つからなければboolfalse
  • 例外/副作用$offset$haystackの文字数を超えるとValueErrorが発生する(PHP 8.0以降)/$haystack自体は変更されない

使用例

基本の使い方(含むかどうかを判定する)

<?php

$text = 'Hello, PHP World!';

if (strpos($text, 'PHP') !== false) {
    echo '「PHP」が含まれています';
} else {
    echo '「PHP」は含まれていません';
}
// 出力: 「PHP」が含まれています

見つかった位置を取得する

<?php

$text = 'Welcome to PHP!';
$pos  = strpos($text, 'PHP');

if ($pos !== false) {
    echo '「PHP」は ' . $pos . ' 文字目にあります'; // 11文字目
}

0とfalseを区別する(最重要)

<?php

$str = 'PHP is great';
$pos = strpos($str, 'PHP'); // 先頭にあるので 0 が返る

// NG: 0 は false と等価に評価されるため誤動作する
if ($pos) {
    echo '見つかった'; // ← 実行されない(バグ!)
}

// OK: 厳密比較(!==)を使う
if ($pos !== false) {
    echo '見つかった: ' . $pos . ' 文字目'; // 正しく動作
}

PHPは0 == falsetrueと評価するため、if ($pos)では先頭にあるケースを「見つからなかった」と誤判定します。判定には必ず=== falseまたは!== falseを使ってください。

offsetを指定してすべての出現位置を検索する

strposは最初の1件しか返さないため、同じ文字列が複数回出現する位置をすべて取得したい場合は$offsetを1つずつずらしながらループします。

<?php

$text = 'PHPはシンプル。PHPは人気。PHPは学びやすい。';
$needle = 'PHP';
$offset = 0;
$positions = [];

while (($pos = strpos($text, $needle, $offset)) !== false) {
    $positions[] = $pos;
    $offset = $pos + strlen($needle);
}

print_r($positions);

大文字小文字を区別しない場合(stripos)

<?php

$text = 'Hello World';

var_dump(strpos($text, 'hello'));  // bool(false)(大文字小文字が異なる)
var_dump(stripos($text, 'hello')); // int(0)(区別しない)

末尾から検索する(strrpos)

最後に出現する位置を知りたい場合はstrrposを使います。

<?php

$path = '/var/www/html/index.php';

$firstSlash = strpos($path, '/');  // 0(最初の / の位置)
$lastSlash  = strrpos($path, '/'); // 13(最後の / の位置)

echo substr($path, $lastSlash + 1); // index.php

PHP 8.0+のstr_containsとの比較

<?php

$text = 'Hello, PHP!';

// PHP 7以前の書き方(strpos)
if (strpos($text, 'PHP') !== false) {
    echo '含む';
}

// PHP 8.0以降の書き方(str_contains)
if (str_contains($text, 'PHP')) {
    echo '含む';
}

位置を知る必要がなく「含むかどうか」だけを判定する場合は、PHP 8.0以上ならstr_contains()のほうがシンプルで0 / falseの混同も起きません。

日本語などマルチバイト文字列を検索する(mb_strpos)

<?php

$text = 'こんにちは PHP!';

// strpos: バイト単位(UTF-8では日本語1文字=3バイト)
$bytePos = strpos($text, 'PHP');    // 15(バイト位置)

// mb_strpos: 文字単位
$charPos = mb_strpos($text, 'PHP'); // 5(文字位置)

echo 'strpos: '    . $bytePos . PHP_EOL; // 15
echo 'mb_strpos: ' . $charPos . PHP_EOL; // 5

日本語・中国語・韓国語など2バイト以上の文字を含む文字列を扱う場合は、位置を文字単位で正しく取得できるmb_strpos()を選んでください。

よくある落とし穴・注意

  • 0とfalseの混同:見つかった位置が0(先頭)の場合、if ($pos)のような緩い判定ではfalse(見つからない)と区別できません。必ず!== false / === falseの厳密比較を使ってください。
  • 大文字小文字を区別するstrposは大文字小文字を区別して検索します。区別せずに検索したい場合はstriposを使います。
  • マルチバイト文字で位置がずれるstrposはバイト単位で数えるため、日本語などを含む文字列では返ってくる位置が文字数と一致しません。文字単位で正確な位置が必要な場合はmb_strposを使います。
  • offsetが文字数を超えるとエラーになる:PHP 8.0以降、$offset$haystackの文字数を超えるとValueErrorが発生します。ループで$offsetをずらす処理では、範囲外にならないか事前にチェックするかtry/catchで受け止めてください。
  • 複数の候補文字列を一度に検索できない$needleに配列を渡すことはできません(PHP 8.0でintの自動キャストも廃止済み)。複数のキーワードのいずれかが含まれるか調べたい場合は、候補をループして1つずつstrposを呼ぶか、preg_matchで正規表現のOR条件を使います。

代替・関連APIとの比較

関数PHPバージョン戻り値主な用途
strpos全バージョンint|false大文字小文字を区別して位置を取得・含む判定を行う(最も基本)
stripos全バージョンint|false大文字小文字を区別せずに検索したいとき
strrpos全バージョンint|false最後に出現する位置を知りたいとき
str_containsPHP 8.0+bool位置は不要で「含むかどうか」だけ調べたいとき
str_starts_withPHP 8.0+bool先頭一致かどうかだけ調べたいとき
mb_strpos全バージョン(mbstring)int|false日本語などマルチバイト文字列を文字単位で検索したいとき

トラブルシュート(症状別)

症状原因対処
先頭にあるはずなのに「見つからない」と判定されるif ($pos)で判定しており0falseが区別できていないif ($pos !== false)のように厳密比較にする
日本語を含む文字列で位置がずれる/文字化けした位置が返るstrposはバイト単位で計算しており、マルチバイト文字1文字が複数バイトになっているmb_strposに置き換える
大文字小文字が違うだけで見つからないstrposは大文字小文字を区別するstriposを使う
「ValueError: strpos(): Argument #3 ($offset) must be contained in argument #1 ($haystack)」が発生する$offset$haystackの文字数を超えている(PHP 8.0以降はエラーになる)ループ処理では$offsetが範囲内か事前にチェックする、またはstrlen($haystack)と比較してからループを止める

よくある質問(FAQ)

Q. strposとは?

A. strpos()はPHPの組み込み文字列関数で、指定した文字列($haystack)の中から別の文字列($needle)が最初に現れる位置を返します。見つかった場合は0以上の整数、見つからない場合はfalseを返します。PHPのすべてのバージョンで利用可能です。

Q. strposが0を返すのはどういう意味ですか?

A. 検索したい文字列が対象文字列の先頭(0文字目)で見つかったことを意味し、false(見つからない)ではありません。if ($pos)のように評価すると0falseが区別できずバグになるため、必ずif ($pos !== false)と書いてください。

Q. 文字列を含むかだけ判定するにはどうすればいいですか?

A. PHP 8.0以上ならstr_contains($text, 'keyword')が最もシンプルで安全です。PHP 7系ではstrpos($text, 'keyword') !== falseで代替できます。どちらも0 / falseの問題を意識せずに使えます(str_containsboolを返すため)。

Q. 複数の文字列のどれかが含まれているか調べるにはどうすればいいですか?

A. $needleに配列を直接渡すことはできません。候補の文字列を配列で用意し、foreachでループしながらstrpos(またはstr_contains)を呼び出すか、正規表現のOR条件(preg_match('/word1|word2/', $text))を使います。

Q. strposとmb_strposはどちらを使うべきですか?

A. 英数字のみを扱うならどちらでも結果は同じです。日本語・中国語・韓国語などのマルチバイト文字を含む文字列を扱う場合は、文字単位で正確な位置を返すmb_strposを使ってください。strposはバイト単位で計算するため、位置がずれることがあります。

関連記事

参考リンク

レン (Wren)

こんにちは。レンです。

Laravelのコードの森に住んでいる、小さな案内役です。
ルーティングの枝やクラスの影を歩きながら、コードの流れや仕組みを眺めています。

このサイトでは、Laravelの基本から実装のコツまで、開発で役立つポイントを静かに整理しています。
難しいことを増やすのではなく、コードの見通しが少し良くなるヒントを届けるのが役目です。

「この処理はどこに書くのがいいのか」
「Laravelではどう考えると整理できるのか」

そんな疑問に、小さなメモを残すような気持ちで記事を書いています。

コードを書いている途中で迷ったとき、
このサイトが少し立ち止まって整理できる場所になればうれしいです。

レン (Wren)をフォローする

コメント