こんにちは!今回は、PHPの標準関数であるstrstr()について詳しく解説していきます。文字列内で指定した部分文字列を検索し、その位置から文字列の最後までを取得できる関数です!
strstr関数とは?
strstr()関数は、文字列内で指定した部分文字列が最初に現れる位置から、文字列の最後までを返す関数です。
strchr()の別名(エイリアス)でもあり、文字列の分割や部分抽出に便利です。メールアドレスやURLの解析、データの抽出など、様々な用途で使用されます!
基本的な構文
strstr(
string $haystack,
string $needle,
bool $before_needle = false
): string|false
- $haystack: 検索対象の文字列
- $needle: 検索する部分文字列
- $before_needle:
trueの場合、見つかった位置より前の部分を返す(PHP 5.3.0+) - 戻り値:
- 見つかった場合: その位置から最後まで(または前まで)の部分文字列
- 見つからない場合:
false
基本的な使用例
シンプルな使用例
$email = "user@example.com";
// @以降を取得
$domain = strstr($email, '@');
echo $domain . "\n";
// 出力: @example.com
// @より前を取得
$username = strstr($email, '@', true);
echo $username . "\n";
// 出力: user
文字列と1文字の検索
$text = "Hello World";
// 文字列で検索
$result = strstr($text, "World");
echo $result . "\n"; // World
// 1文字で検索
$result = strstr($text, 'o');
echo $result . "\n"; // o World(最初の'o'から)
見つからない場合
$text = "Hello World";
// 含まれていない文字列を検索
$result = strstr($text, "PHP");
var_dump($result); // bool(false)
// 正しい判定方法
if ($result !== false) {
echo "見つかりました\n";
} else {
echo "見つかりません\n";
}
strchr()との関係
$text = "apple@banana@orange";
// strstr()とstrchr()は同じ
$result1 = strstr($text, '@');
$result2 = strchr($text, '@');
echo $result1 . "\n"; // @banana@orange
echo $result2 . "\n"; // @banana@orange
var_dump($result1 === $result2); // true
実践的な使用例
例1: メールアドレスの処理
class EmailParser {
/**
* ドメイン部分を取得
*/
public static function getDomain($email) {
$domain = strstr($email, '@');
if ($domain === false) {
return null;
}
return substr($domain, 1); // @を除去
}
/**
* ローカル部分を取得
*/
public static function getLocalPart($email) {
return strstr($email, '@', true);
}
/**
* トップレベルドメインを取得
*/
public static function getTLD($email) {
$domain = self::getDomain($email);
if ($domain === null) {
return null;
}
$tld = strstr($domain, '.');
if ($tld === false) {
return null;
}
return substr($tld, 1); // .を除去
}
/**
* ドメイン名(TLD除く)を取得
*/
public static function getDomainName($email) {
$domain = self::getDomain($email);
if ($domain === null) {
return null;
}
return strstr($domain, '.', true);
}
/**
* メールアドレスをマスク
*/
public static function maskEmail($email) {
$local = self::getLocalPart($email);
$domain = strstr($email, '@');
if ($local === false || $domain === false) {
return $email;
}
$localLength = strlen($local);
$visibleChars = min(3, $localLength);
$masked = substr($local, 0, $visibleChars) .
str_repeat('*', max(0, $localLength - $visibleChars));
return $masked . $domain;
}
/**
* 企業メールか判定
*/
public static function isCompanyEmail($email, $companyDomains) {
$domain = self::getDomain($email);
if ($domain === null) {
return false;
}
foreach ($companyDomains as $companyDomain) {
if (stripos($domain, $companyDomain) !== false) {
return true;
}
}
return false;
}
}
// 使用例
$emails = [
'john.doe@example.com',
'admin@mail.company.co.jp',
'user@test.org'
];
echo "=== メールアドレス解析 ===\n";
foreach ($emails as $email) {
echo "\n{$email}:\n";
echo " ローカル部: " . EmailParser::getLocalPart($email) . "\n";
echo " ドメイン: " . EmailParser::getDomain($email) . "\n";
echo " ドメイン名: " . EmailParser::getDomainName($email) . "\n";
echo " TLD: " . EmailParser::getTLD($email) . "\n";
echo " マスク: " . EmailParser::maskEmail($email) . "\n";
}
$companyDomains = ['company.co.jp', 'example.com'];
echo "\n=== 企業メール判定 ===\n";
foreach ($emails as $email) {
$isCompany = EmailParser::isCompanyEmail($email, $companyDomains);
echo "{$email}: " . ($isCompany ? '企業' : '個人') . "\n";
}
例2: URL解析
class UrlParser {
/**
* プロトコルを除いた部分を取得
*/
public static function withoutProtocol($url) {
$result = strstr($url, '://');
if ($result === false) {
return $url;
}
return substr($result, 3); // ://を除去
}
/**
* プロトコルを取得
*/
public static function getProtocol($url) {
return strstr($url, '://', true);
}
/**
* パス部分を取得
*/
public static function getPath($url) {
$withoutProtocol = self::withoutProtocol($url);
$path = strstr($withoutProtocol, '/');
if ($path === false) {
return '/';
}
// クエリ文字列を除去
$pathOnly = strstr($path, '?', true);
return $pathOnly !== false ? $pathOnly : $path;
}
/**
* ホスト部分を取得
*/
public static function getHost($url) {
$withoutProtocol = self::withoutProtocol($url);
// パス部分を除去
$host = strstr($withoutProtocol, '/', true);
if ($host === false) {
// クエリ文字列を除去
$host = strstr($withoutProtocol, '?', true);
if ($host === false) {
$host = $withoutProtocol;
}
}
return $host;
}
/**
* クエリ文字列を取得
*/
public static function getQueryString($url) {
$query = strstr($url, '?');
if ($query === false) {
return '';
}
// フラグメントを除去
$queryOnly = strstr($query, '#', true);
return substr($queryOnly !== false ? $queryOnly : $query, 1); // ?を除去
}
/**
* フラグメントを取得
*/
public static function getFragment($url) {
$fragment = strstr($url, '#');
if ($fragment === false) {
return '';
}
return substr($fragment, 1); // #を除去
}
/**
* URL全体を解析
*/
public static function parse($url) {
return [
'protocol' => self::getProtocol($url),
'host' => self::getHost($url),
'path' => self::getPath($url),
'query' => self::getQueryString($url),
'fragment' => self::getFragment($url),
'full_domain' => self::withoutProtocol(
strstr($url, '/', true) ?: strstr($url, '?', true) ?: $url
)
];
}
}
// 使用例
$urls = [
'https://example.com:8080/path/to/page?key=value&foo=bar#section',
'http://test.org/index.html',
'ftp://files.example.com/download'
];
echo "=== URL解析 ===\n";
foreach ($urls as $url) {
echo "\nURL: {$url}\n";
$parsed = UrlParser::parse($url);
echo " プロトコル: {$parsed['protocol']}\n";
echo " ホスト: {$parsed['host']}\n";
echo " パス: {$parsed['path']}\n";
echo " クエリ: {$parsed['query']}\n";
echo " フラグメント: {$parsed['fragment']}\n";
}
例3: ファイルパスの処理
class PathParser {
/**
* 拡張子を取得
*/
public static function getExtension($path) {
$filename = basename($path);
$ext = strstr($filename, '.');
if ($ext === false) {
return '';
}
return substr($ext, 1); // .を除去
}
/**
* ファイル名(拡張子除く)を取得
*/
public static function getBasename($path) {
$filename = basename($path);
return strstr($filename, '.', true) ?: $filename;
}
/**
* ディレクトリ部分とファイル名を分離
*/
public static function splitPath($path) {
$lastSlash = max(strrpos($path, '/'), strrpos($path, '\\'));
if ($lastSlash === false) {
return ['dir' => '', 'file' => $path];
}
return [
'dir' => substr($path, 0, $lastSlash),
'file' => substr($path, $lastSlash + 1)
];
}
/**
* 拡張子を変更
*/
public static function changeExtension($path, $newExtension) {
$parts = self::splitPath($path);
$basename = self::getBasename($parts['file']);
if (!empty($newExtension) && $newExtension[0] !== '.') {
$newExtension = '.' . $newExtension;
}
$newFilename = $basename . $newExtension;
if (!empty($parts['dir'])) {
return $parts['dir'] . '/' . $newFilename;
}
return $newFilename;
}
}
// 使用例
$paths = [
'/var/www/html/index.php',
'C:\\Users\\Documents\\report.pdf',
'image.backup.jpg',
'README'
];
echo "=== パス解析 ===\n";
foreach ($paths as $path) {
echo "\nパス: {$path}\n";
echo " 拡張子: " . PathParser::getExtension($path) . "\n";
echo " ベース名: " . PathParser::getBasename($path) . "\n";
$parts = PathParser::splitPath($path);
echo " ディレクトリ: " . $parts['dir'] . "\n";
echo " ファイル名: " . $parts['file'] . "\n";
}
echo "\n=== 拡張子変更 ===\n";
echo PathParser::changeExtension('/path/to/file.txt', 'md') . "\n";
// /path/to/file.md
例4: データ抽出
class DataExtractor {
/**
* 特定マーカー以降のデータを取得
*/
public static function afterMarker($text, $marker) {
$result = strstr($text, $marker);
if ($result === false) {
return null;
}
return substr($result, strlen($marker));
}
/**
* 特定マーカーより前のデータを取得
*/
public static function beforeMarker($text, $marker) {
return strstr($text, $marker, true);
}
/**
* 2つのマーカーに挟まれた部分を取得
*/
public static function between($text, $start, $end) {
$afterStart = strstr($text, $start);
if ($afterStart === false) {
return null;
}
$afterStart = substr($afterStart, strlen($start));
$beforeEnd = strstr($afterStart, $end, true);
return $beforeEnd !== false ? $beforeEnd : $afterStart;
}
/**
* キー・値ペアを抽出
*/
public static function extractKeyValue($line, $separator = '=') {
$value = strstr($line, $separator);
if ($value === false) {
return null;
}
$key = strstr($line, $separator, true);
return [
'key' => trim($key),
'value' => trim(substr($value, 1))
];
}
/**
* HTMLタグの内容を抽出
*/
public static function extractTagContent($html, $tag) {
$openTag = "<{$tag}>";
$closeTag = "</{$tag}>";
$afterOpen = strstr($html, $openTag);
if ($afterOpen === false) {
return null;
}
$content = substr($afterOpen, strlen($openTag));
$beforeClose = strstr($content, $closeTag, true);
return $beforeClose !== false ? $beforeClose : $content;
}
}
// 使用例
echo "=== データ抽出 ===\n";
$text = "Name: John Doe, Age: 30";
echo "': 以降: " . DataExtractor::afterMarker($text, ': ') . "\n";
// John Doe, Age: 30
$config = "database.host=localhost";
$kv = DataExtractor::extractKeyValue($config);
print_r($kv);
// ['key' => 'database.host', 'value' => 'localhost']
$html = "<title>Page Title</title>";
echo "\nタイトル: " . DataExtractor::extractTagContent($html, 'title') . "\n";
// Page Title
$text = "Start [content here] End";
echo "挟まれた部分: " . DataExtractor::between($text, '[', ']') . "\n";
// content here
例5: ログ解析
class LogParser {
/**
* ログレベルを抽出
*/
public static function extractLevel($logLine) {
// [ERROR] のような形式を想定
$afterBracket = strstr($logLine, '[');
if ($afterBracket === false) {
return null;
}
$level = strstr($afterBracket, ']', true);
if ($level === false) {
return null;
}
return trim($level, '[]');
}
/**
* メッセージ部分を抽出
*/
public static function extractMessage($logLine) {
// 最後の ] 以降がメッセージ
$lastBracket = strrpos($logLine, ']');
if ($lastBracket === false) {
return $logLine;
}
return trim(substr($logLine, $lastBracket + 1));
}
/**
* タイムスタンプを抽出
*/
public static function extractTimestamp($logLine) {
$afterBracket = strstr($logLine, '[');
if ($afterBracket === false) {
return null;
}
$timestamp = strstr($afterBracket, ']', true);
return trim($timestamp, '[]');
}
/**
* ログエントリを解析
*/
public static function parse($logLine) {
// [timestamp] [level] message 形式を想定
$parts = [];
$remaining = $logLine;
// 1つ目の[]内容(タイムスタンプ)
$afterBracket = strstr($remaining, '[');
if ($afterBracket !== false) {
$timestamp = strstr($afterBracket, ']', true);
$parts['timestamp'] = trim($timestamp, '[]');
$remaining = substr(strstr($afterBracket, ']'), 1);
}
// 2つ目の[]内容(レベル)
$afterBracket = strstr($remaining, '[');
if ($afterBracket !== false) {
$level = strstr($afterBracket, ']', true);
$parts['level'] = trim($level, '[]');
$remaining = substr(strstr($afterBracket, ']'), 1);
}
// 残りがメッセージ
$parts['message'] = trim($remaining);
return $parts;
}
}
// 使用例
$logs = [
'[2024-02-23 10:30:00] [ERROR] Database connection failed',
'[2024-02-23 10:31:00] [INFO] User logged in',
'[2024-02-23 10:32:00] [WARN] High memory usage'
];
echo "=== ログ解析 ===\n";
foreach ($logs as $log) {
echo "\n{$log}\n";
$parsed = LogParser::parse($log);
echo " タイムスタンプ: {$parsed['timestamp']}\n";
echo " レベル: {$parsed['level']}\n";
echo " メッセージ: {$parsed['message']}\n";
}
例6: CSVとテキスト処理
class TextProcessor {
/**
* CSVフィールドを抽出(簡易版)
*/
public static function extractCsvField($line, $fieldNumber) {
$current = $line;
for ($i = 0; $i < $fieldNumber; $i++) {
$afterComma = strstr($current, ',');
if ($afterComma === false) {
return null;
}
$current = substr($afterComma, 1);
}
$field = strstr($current, ',', true);
return $field !== false ? $field : $current;
}
/**
* 文の始まりから特定の単語までを取得
*/
public static function upToWord($text, $word) {
return strstr($text, $word, true);
}
/**
* 特定の単語以降を取得
*/
public static function fromWord($text, $word) {
return strstr($text, $word);
}
/**
* 行を分割
*/
public static function splitAtDelimiter($text, $delimiter) {
$after = strstr($text, $delimiter);
if ($after === false) {
return [$text, ''];
}
$before = strstr($text, $delimiter, true);
return [$before, substr($after, strlen($delimiter))];
}
}
// 使用例
echo "=== テキスト処理 ===\n";
$csv = "apple,banana,orange,grape";
echo "2番目のフィールド: " . TextProcessor::extractCsvField($csv, 1) . "\n";
// banana
$sentence = "The quick brown fox jumps over the lazy dog";
echo "brownまで: " . TextProcessor::upToWord($sentence, "brown") . "\n";
// The quick
echo "jumps以降: " . TextProcessor::fromWord($sentence, "jumps") . "\n";
// jumps over the lazy dog
list($before, $after) = TextProcessor::splitAtDelimiter("key=value", "=");
echo "\n分割:\n";
echo " 前: {$before}\n"; // key
echo " 後: {$after}\n"; // value
例7: バージョン番号とIDの処理
class VersionIdProcessor {
/**
* バージョン番号のメジャー部分を取得
*/
public static function getMajorVersion($version) {
return strstr($version, '.', true) ?: $version;
}
/**
* マイナーバージョン以降を取得
*/
public static function getMinorAndPatch($version) {
$result = strstr($version, '.');
if ($result === false) {
return '';
}
return substr($result, 1);
}
/**
* IDのプレフィックスを取得
*/
public static function getIdPrefix($id, $separator = '-') {
return strstr($id, $separator, true) ?: $id;
}
/**
* IDの番号部分を取得
*/
public static function getIdNumber($id, $separator = '-') {
$result = strstr($id, $separator);
if ($result === false) {
return '';
}
return substr($result, 1);
}
}
// 使用例
$versions = ['1.2.3', '2.0', '10.5.8-beta'];
echo "=== バージョン処理 ===\n";
foreach ($versions as $version) {
echo "\n{$version}:\n";
echo " メジャー: " . VersionIdProcessor::getMajorVersion($version) . "\n";
echo " マイナー以降: " . VersionIdProcessor::getMinorAndPatch($version) . "\n";
}
$ids = ['USER-12345', 'PROD-999', 'ORDER-ABC-789'];
echo "\n=== ID処理 ===\n";
foreach ($ids as $id) {
echo "\n{$id}:\n";
echo " プレフィックス: " . VersionIdProcessor::getIdPrefix($id) . "\n";
echo " 番号: " . VersionIdProcessor::getIdNumber($id) . "\n";
}
パフォーマンスの考慮
// 大量のテキストで部分文字列を検索
$text = str_repeat("apple banana orange ", 10000);
$keyword = "orange";
// strstr()
$start = microtime(true);
for ($i = 0; $i < 10000; $i++) {
strstr($text, $keyword);
}
$time1 = microtime(true) - $start;
// strpos() + substr()
$start = microtime(true);
for ($i = 0; $i < 10000; $i++) {
$pos = strpos($text, $keyword);
if ($pos !== false) {
substr($text, $pos);
}
}
$time2 = microtime(true) - $start;
echo "strstr(): {$time1}秒\n";
echo "strpos() + substr(): {$time2}秒\n";
// strstr()は位置と抽出を一度に行うので効率的
まとめ
strstr()関数の特徴をまとめると:
できること:
- 部分文字列の検索と抽出を同時に実行
- 検索位置以降の取得
- 検索位置より前の取得(
before_needleパラメータ)
strchr()との関係:
strstr()とstrchr()は完全に同じstrchr()はstrstr()のエイリアス
推奨される使用場面:
- メールアドレスの解析
- URL・パスの処理
- データの分割と抽出
- ログ解析
- 設定ファイルのパース
- CSVデータの処理
注意点:
- 見つからない場合は
falseを返す - 最初の出現位置のみ処理
- 大文字小文字を区別する
関連関数:
strchr():strstr()のエイリアスstristr(): 大文字小文字を区別しないバージョンstrrchr(): 最後の出現位置から取得strpos(): 位置(整数)のみ返すsubstr(): 位置と長さで部分文字列を取得
パフォーマンス:
- 位置の検索と抽出を一度に実行
strpos() + substr()より効率的な場合が多い
before_needleパラメータ:
$email = "user@example.com";
$username = strstr($email, '@', true); // user
$domain = strstr($email, '@'); // @example.com
strstr()は、部分文字列の検索と抽出を同時に行いたい場合に非常に便利です。特にメールアドレスやURLの解析、データの分割処理で頻繁に使用される関数なので、しっかり使いこなしましょう!
