こんにちは!今回は、PHPの標準関数であるstristr()について詳しく解説していきます。大文字小文字を区別せずに部分文字列を取得できる、便利な関数です!
stristr関数とは?
stristr()関数は、文字列内で指定した部分文字列が最初に現れる位置を検索し、その位置から文字列の最後まで(またはその前まで)を大文字小文字を区別せずに返す関数です。
strstr()の大文字小文字を区別しないバージョンで、柔軟な文字列抽出が必要な場面で活躍します!
基本的な構文
stristr(
string $haystack,
string $needle,
bool $before_needle = false
): string|false
- $haystack: 検索対象の文字列
- $needle: 検索する部分文字列
- $before_needle: true の場合、見つかった文字列の前の部分を返す
- 戻り値:
- 見つかった場合: 部分文字列
- 見つからない場合:
false
基本的な使用例
シンプルな使用例
$email = "user@EXAMPLE.COM";
// @以降を取得(大文字小文字を区別しない)
$domain = stristr($email, '@');
echo $domain . "\n";
// 出力: @EXAMPLE.COM
// @より前を取得
$username = stristr($email, '@', true);
echo $username . "\n";
// 出力: user
strstr()との違い
$text = "Hello WORLD";
// strstr(): 大文字小文字を区別
$result1 = strstr($text, "world");
var_dump($result1); // bool(false)
$result2 = strstr($text, "WORLD");
echo $result2 . "\n"; // WORLD
// stristr(): 大文字小文字を区別しない
$result3 = stristr($text, "world");
echo $result3 . "\n"; // WORLD
$result4 = stristr($text, "WoRlD");
echo $result4 . "\n"; // WORLD
before_needleの使用
$url = "https://EXAMPLE.COM/path/to/page";
// プロトコル部分を除外
$afterProtocol = stristr($url, "://");
echo $afterProtocol . "\n";
// 出力: ://EXAMPLE.COM/path/to/page
// プロトコル部分を取得
$protocol = stristr($url, "://", true);
echo $protocol . "\n";
// 出力: https
実践的な使用例
例1: メールアドレスの処理
class EmailParser {
/**
* ドメイン部分を取得
*/
public static function getDomain($email) {
$domain = stristr($email, '@');
if ($domain === false) {
return null;
}
return substr($domain, 1); // @ を除去
}
/**
* ローカル部分を取得
*/
public static function getLocalPart($email) {
return stristr($email, '@', true);
}
/**
* トップレベルドメインを取得
*/
public static function getTLD($email) {
$domain = self::getDomain($email);
if ($domain === null) {
return null;
}
$tld = stristr($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 stristr($domain, '.', true);
}
/**
* メールアドレスが特定のドメインか確認
*/
public static function isDomain($email, $targetDomain) {
$domain = self::getDomain($email);
if ($domain === null) {
return false;
}
return strcasecmp($domain, $targetDomain) === 0;
}
/**
* メールアドレスをマスク
*/
public static function mask($email) {
$local = self::getLocalPart($email);
$domain = stristr($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;
}
}
// 使用例
$email = "John.Doe@EXAMPLE.COM";
echo "ドメイン: " . EmailParser::getDomain($email) . "\n";
// EXAMPLE.COM
echo "ローカル部分: " . EmailParser::getLocalPart($email) . "\n";
// John.Doe
echo "TLD: " . EmailParser::getTLD($email) . "\n";
// COM
echo "ドメイン名: " . EmailParser::getDomainName($email) . "\n";
// EXAMPLE
var_dump(EmailParser::isDomain($email, 'example.com'));
// bool(true)
echo "マスク: " . EmailParser::mask($email) . "\n";
// Joh*****@EXAMPLE.COM
例2: URLパーサー
class UrlParser {
/**
* プロトコルを取得
*/
public static function getProtocol($url) {
return stristr($url, '://', true);
}
/**
* プロトコルを除いた部分を取得
*/
public static function withoutProtocol($url) {
$result = stristr($url, '://');
if ($result === false) {
return $url;
}
return substr($result, 3); // :// を除去
}
/**
* ホスト部分を取得
*/
public static function getHost($url) {
$withoutProtocol = self::withoutProtocol($url);
// パス部分を除去
$host = stristr($withoutProtocol, '/', true);
if ($host === false) {
// クエリ文字列を除去
$host = stristr($withoutProtocol, '?', true);
if ($host === false) {
$host = $withoutProtocol;
}
}
return $host;
}
/**
* パス部分を取得
*/
public static function getPath($url) {
$withoutProtocol = self::withoutProtocol($url);
$path = stristr($withoutProtocol, '/');
if ($path === false) {
return '/';
}
// クエリ文字列を除去
$pathOnly = stristr($path, '?', true);
return $pathOnly !== false ? $pathOnly : $path;
}
/**
* クエリ文字列を取得
*/
public static function getQueryString($url) {
$query = stristr($url, '?');
if ($query === false) {
return '';
}
// フラグメントを除去
$queryOnly = stristr($query, '#', true);
return substr($queryOnly !== false ? $queryOnly : $query, 1); // ? を除去
}
/**
* フラグメントを取得
*/
public static function getFragment($url) {
$fragment = stristr($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)
];
}
}
// 使用例
$url = "HTTPS://Example.COM:8080/Path/To/Page?key=value&foo=bar#SECTION";
echo "プロトコル: " . UrlParser::getProtocol($url) . "\n";
// HTTPS
echo "ホスト: " . UrlParser::getHost($url) . "\n";
// Example.COM:8080
echo "パス: " . UrlParser::getPath($url) . "\n";
// /Path/To/Page
echo "クエリ: " . UrlParser::getQueryString($url) . "\n";
// key=value&foo=bar
echo "フラグメント: " . UrlParser::getFragment($url) . "\n";
// SECTION
echo "\n完全な解析:\n";
print_r(UrlParser::parse($url));
例3: ファイルパスの処理
class PathHelper {
/**
* ファイル名を取得
*/
public static function getFilename($path) {
// 最後の / または \ を探す
$lastSlash = max(
strrpos($path, '/'),
strrpos($path, '\\')
);
if ($lastSlash === false) {
return $path;
}
return substr($path, $lastSlash + 1);
}
/**
* 拡張子を取得
*/
public static function getExtension($path) {
$filename = self::getFilename($path);
$ext = stristr($filename, '.');
if ($ext === false) {
return '';
}
return substr($ext, 1); // . を除去
}
/**
* 拡張子を除いたファイル名を取得
*/
public static function getBasename($path) {
$filename = self::getFilename($path);
return stristr($filename, '.', true) ?: $filename;
}
/**
* ディレクトリ部分を取得
*/
public static function getDirectory($path) {
$lastSlash = max(
strrpos($path, '/'),
strrpos($path, '\\')
);
if ($lastSlash === false) {
return '';
}
return substr($path, 0, $lastSlash);
}
/**
* 特定の拡張子か確認
*/
public static function hasExtension($path, $extension) {
$ext = self::getExtension($path);
return strcasecmp($ext, $extension) === 0;
}
/**
* 拡張子を変更
*/
public static function changeExtension($path, $newExtension) {
$basename = self::getBasename($path);
$directory = self::getDirectory($path);
$newPath = $basename . '.' . $newExtension;
if (!empty($directory)) {
$newPath = $directory . '/' . $newPath;
}
return $newPath;
}
}
// 使用例
$path = "C:\\Users\\Documents\\Report.PDF";
echo "ファイル名: " . PathHelper::getFilename($path) . "\n";
// Report.PDF
echo "拡張子: " . PathHelper::getExtension($path) . "\n";
// PDF
echo "ベース名: " . PathHelper::getBasename($path) . "\n";
// Report
echo "ディレクトリ: " . PathHelper::getDirectory($path) . "\n";
// C:\Users\Documents
var_dump(PathHelper::hasExtension($path, 'pdf'));
// bool(true)
echo "拡張子変更: " . PathHelper::changeExtension($path, 'docx') . "\n";
// C:\Users\Documents/Report.docx
例4: コンテンツタイプの判定
class ContentTypeDetector {
/**
* Content-Typeヘッダーからメディアタイプを取得
*/
public static function getMediaType($contentType) {
// ; より前を取得
$mediaType = stristr($contentType, ';', true);
return $mediaType !== false ? trim($mediaType) : trim($contentType);
}
/**
* 文字セットを取得
*/
public static function getCharset($contentType) {
$charsetPart = stristr($contentType, 'charset=');
if ($charsetPart === false) {
return null;
}
$charset = substr($charsetPart, 8); // "charset=" を除去
// ; があればそこまで
$end = stristr($charset, ';', true);
return $end !== false ? trim($end) : trim($charset);
}
/**
* boundaryを取得(multipart用)
*/
public static function getBoundary($contentType) {
$boundaryPart = stristr($contentType, 'boundary=');
if ($boundaryPart === false) {
return null;
}
$boundary = substr($boundaryPart, 9); // "boundary=" を除去
// 引用符を除去
$boundary = trim($boundary, '"\'');
// ; があればそこまで
$end = stristr($boundary, ';', true);
return $end !== false ? trim($end) : trim($boundary);
}
/**
* Content-Typeを解析
*/
public static function parse($contentType) {
return [
'media_type' => self::getMediaType($contentType),
'charset' => self::getCharset($contentType),
'boundary' => self::getBoundary($contentType)
];
}
/**
* 特定のメディアタイプか確認
*/
public static function isMediaType($contentType, $targetType) {
$mediaType = self::getMediaType($contentType);
return strcasecmp($mediaType, $targetType) === 0;
}
}
// 使用例
$contentType1 = "text/html; CHARSET=UTF-8";
$contentType2 = "MULTIPART/FORM-DATA; boundary=----WebKitFormBoundary";
$contentType3 = "APPLICATION/JSON";
echo "=== 例1 ===\n";
print_r(ContentTypeDetector::parse($contentType1));
/*
Array (
[media_type] => text/html
[charset] => UTF-8
[boundary] =>
)
*/
echo "\n=== 例2 ===\n";
print_r(ContentTypeDetector::parse($contentType2));
/*
Array (
[media_type] => MULTIPART/FORM-DATA
[charset] =>
[boundary] => ----WebKitFormBoundary
)
*/
echo "\n=== JSON判定 ===\n";
var_dump(ContentTypeDetector::isMediaType($contentType3, 'application/json'));
// bool(true)
例5: ログエントリの解析
class LogEntryParser {
/**
* ログレベルを取得
*/
public static function getLevel($logEntry) {
// [ で始まるログレベル部分を探す
$levelPart = stristr($logEntry, '[');
if ($levelPart === false) {
return null;
}
// ] までを取得
$level = stristr($levelPart, ']', true);
if ($level === false) {
return null;
}
return trim($level, '[]');
}
/**
* タイムスタンプを取得
*/
public static function getTimestamp($logEntry) {
// 最初の [ ] で囲まれた部分を取得
$firstBracket = stristr($logEntry, '[');
if ($firstBracket === false) {
return null;
}
$timestamp = stristr($firstBracket, ']', true);
if ($timestamp === false) {
return null;
}
return trim($timestamp, '[]');
}
/**
* メッセージ部分を取得
*/
public static function getMessage($logEntry) {
// 最後の ] 以降を取得
$lastBracket = strrpos($logEntry, ']');
if ($lastBracket === false) {
return $logEntry;
}
return trim(substr($logEntry, $lastBracket + 1));
}
/**
* 特定のレベルのログか確認
*/
public static function isLevel($logEntry, $targetLevel) {
$level = self::getLevel($logEntry);
if ($level === null) {
return false;
}
return strcasecmp($level, $targetLevel) === 0;
}
}
// 使用例
$log1 = "[2024-02-23 10:30:45] [ERROR] Database connection failed";
$log2 = "[INFO] User logged in successfully";
$log3 = "[2024-02-23 10:31:00] [WARNING] Memory usage high";
echo "ログ1:\n";
echo " タイムスタンプ: " . LogEntryParser::getTimestamp($log1) . "\n";
echo " レベル: " . LogEntryParser::getLevel($log1) . "\n";
echo " メッセージ: " . LogEntryParser::getMessage($log1) . "\n";
echo "\nエラーログ判定:\n";
var_dump(LogEntryParser::isLevel($log1, 'error')); // true
var_dump(LogEntryParser::isLevel($log2, 'error')); // false
例6: HTTPヘッダーの解析
class HttpHeaderParser {
/**
* ヘッダー名を取得
*/
public static function getHeaderName($header) {
return stristr($header, ':', true);
}
/**
* ヘッダー値を取得
*/
public static function getHeaderValue($header) {
$value = stristr($header, ':');
if ($value === false) {
return '';
}
return trim(substr($value, 1)); // : を除去
}
/**
* 特定のヘッダーか確認
*/
public static function isHeader($header, $targetName) {
$name = self::getHeaderName($header);
if ($name === false) {
return false;
}
return strcasecmp($name, $targetName) === 0;
}
/**
* ヘッダー配列から特定のヘッダーを検索
*/
public static function findHeader($headers, $targetName) {
foreach ($headers as $header) {
if (self::isHeader($header, $targetName)) {
return self::getHeaderValue($header);
}
}
return null;
}
/**
* すべてのヘッダーを連想配列に変換
*/
public static function parseHeaders($headers) {
$parsed = [];
foreach ($headers as $header) {
$name = self::getHeaderName($header);
$value = self::getHeaderValue($header);
if ($name !== false) {
$parsed[strtolower($name)] = $value;
}
}
return $parsed;
}
}
// 使用例
$headers = [
"Content-Type: APPLICATION/JSON; charset=utf-8",
"CONTENT-LENGTH: 1234",
"Authorization: Bearer token123",
"Cache-Control: no-cache"
];
echo "Content-Type: " . HttpHeaderParser::findHeader($headers, 'content-type') . "\n";
// APPLICATION/JSON; charset=utf-8
echo "Content-Length: " . HttpHeaderParser::findHeader($headers, 'CONTENT-LENGTH') . "\n";
// 1234
echo "\n全ヘッダー:\n";
print_r(HttpHeaderParser::parseHeaders($headers));
例7: ファイルタイプの判定
class FileTypeChecker {
private static $mimeTypes = [
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'pdf' => 'application/pdf',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'xls' => 'application/vnd.ms-excel',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
];
/**
* 拡張子からMIMEタイプを取得
*/
public static function getMimeType($filename) {
$ext = PathHelper::getExtension($filename);
$lowerExt = strtolower($ext);
return self::$mimeTypes[$lowerExt] ?? 'application/octet-stream';
}
/**
* 画像ファイルか確認
*/
public static function isImage($filename) {
$mimeType = self::getMimeType($filename);
return stristr($mimeType, 'image/') !== false;
}
/**
* ドキュメントファイルか確認
*/
public static function isDocument($filename) {
$ext = strtolower(PathHelper::getExtension($filename));
$docExtensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'];
return in_array($ext, $docExtensions);
}
/**
* 許可された拡張子か確認
*/
public static function isAllowedExtension($filename, $allowedExtensions) {
$ext = PathHelper::getExtension($filename);
foreach ($allowedExtensions as $allowed) {
if (strcasecmp($ext, $allowed) === 0) {
return true;
}
}
return false;
}
}
// 使用例
$files = [
'photo.JPG',
'document.PDF',
'spreadsheet.XLSX',
'video.MP4'
];
foreach ($files as $file) {
echo "{$file}:\n";
echo " MIMEタイプ: " . FileTypeChecker::getMimeType($file) . "\n";
echo " 画像: " . (FileTypeChecker::isImage($file) ? 'はい' : 'いいえ') . "\n";
echo " ドキュメント: " . (FileTypeChecker::isDocument($file) ? 'はい' : 'いいえ') . "\n";
echo "\n";
}
$allowed = ['jpg', 'png', 'pdf'];
echo "許可された拡張子チェック:\n";
foreach ($files as $file) {
$isAllowed = FileTypeChecker::isAllowedExtension($file, $allowed);
echo "{$file}: " . ($isAllowed ? '許可' : '不許可') . "\n";
}
strchr()との関係
// stristr()とstrchr()は大文字小文字の扱いが異なるだけ
$email = "User@EXAMPLE.COM";
// strchr(): strstr()のエイリアス(大文字小文字を区別)
$result1 = strchr($email, '@');
echo $result1 . "\n"; // @EXAMPLE.COM
// stristr(): 大文字小文字を区別しない
$result2 = stristr($email, '@');
echo $result2 . "\n"; // @EXAMPLE.COM
// 大文字小文字が異なる場合
$result3 = strchr($email, 'user');
var_dump($result3); // bool(false)
$result4 = stristr($email, 'user');
echo $result4 . "\n"; // User@EXAMPLE.COM
パフォーマンスの考慮
// 大量の文字列検索
$text = str_repeat("Lorem ipsum DOLOR sit amet. ", 1000);
$keyword = "dolor";
// stristr()
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
stristr($text, $keyword);
}
$time1 = microtime(true) - $start;
// preg_match() (大文字小文字を区別しない)
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
preg_match('/' . preg_quote($keyword, '/') . '/i', $text);
}
$time2 = microtime(true) - $start;
echo "stristr(): {$time1}秒\n";
echo "preg_match(): {$time2}秒\n";
// stristr()の方が一般的に高速
まとめ
stristr()関数の特徴をまとめると:
できること:
- 大文字小文字を区別しない部分文字列の取得
- 指定文字列以降の取得
- 指定文字列より前の取得(第3引数)
strstr()との違い:
strstr(): 大文字小文字を区別stristr(): 大文字小文字を区別しない
推奨される使用場面:
- メールアドレスの解析
- URL・パスの処理
- ヘッダーの解析
- ファイル拡張子の取得
- ログエントリの解析
- Content-Typeの処理
注意点:
falseの判定には=== falseを使用- マルチバイト文字には
mb_stristr()を検討 - 最初の出現位置のみ
関連関数:
strstr(): 大文字小文字を区別strchr(): strstr()のエイリアスstrrchr(): 最後の出現位置から取得stripos(): 位置のみ取得(大文字小文字無視)mb_stristr(): マルチバイト対応版
使用例のまとめ:
- メールアドレス: ドメイン・ローカル部分の抽出
- URL: プロトコル・ホスト・パスの解析
- ファイルパス: 拡張子・ディレクトリの取得
- ヘッダー: 名前と値の分離
stristr()は、大文字小文字を気にせずに文字列を分割・抽出したい場合に非常に便利です。ユーザー入力の処理や柔軟なパース処理に活用しましょう!
