こんにちは!今回は、PHPの標準関数であるstr_ends_with()について詳しく解説していきます。文字列が特定の文字列で終わっているかを簡単にチェックできる、PHP 8.0で追加された便利な関数です!
str_ends_with関数とは?
str_ends_with()関数は、文字列が指定した文字列で終わっているかを判定する関数です。
PHP 8.0で追加された関数で、サフィックス(接尾辞)のチェックが非常に簡潔に書けるようになりました!
基本的な構文
str_ends_with(string $haystack, string $needle): bool
- $haystack: 検索対象の文字列
- $needle: 末尾で検索する文字列
- 戻り値: 末尾が一致する場合は
true、一致しない場合はfalse
基本的な使用例
シンプルな判定
// 基本的な使用(PHP 8.0+)
$text = "Hello World";
if (str_ends_with($text, "World")) {
echo "「World」で終わっています\n";
}
// 出力: 「World」で終わっています
if (str_ends_with($text, "Hello")) {
echo "「Hello」で終わっています\n";
} else {
echo "「Hello」で終わっていません\n";
}
// 出力: 「Hello」で終わっていません
大文字小文字の区別
$text = "Hello World";
// 大文字小文字は区別される
var_dump(str_ends_with($text, "world")); // false
var_dump(str_ends_with($text, "World")); // true
// 大文字小文字を区別しない場合
$text_lower = strtolower($text);
var_dump(str_ends_with($text_lower, strtolower("WORLD"))); // true
空文字列の扱い
$text = "Hello World";
// 空文字列は常にtrueを返す
var_dump(str_ends_with($text, "")); // true
// 空の文字列で判定
var_dump(str_ends_with("", "test")); // false
var_dump(str_ends_with("", "")); // true
1文字の判定
$text = "Hello World";
// 1文字でも判定可能
var_dump(str_ends_with($text, "d")); // true
var_dump(str_ends_with($text, "D")); // false
PHP 8.0未満での代替実装
// PHP 8.0未満用のポリフィル
if (!function_exists('str_ends_with')) {
function str_ends_with($haystack, $needle) {
if ($needle === '') {
return true;
}
$length = strlen($needle);
return substr($haystack, -$length) === $needle;
}
}
// 使用例
$text = "Hello World";
var_dump(str_ends_with($text, "World")); // true
実践的な使用例
例1: ファイル拡張子チェック
class FileExtensionChecker {
/**
* 特定の拡張子か判定
*/
public static function hasExtension($filename, $extension) {
if ($extension[0] !== '.') {
$extension = '.' . $extension;
}
return str_ends_with(strtolower($filename), strtolower($extension));
}
/**
* 複数の拡張子のいずれかか判定
*/
public static function hasAnyExtension($filename, $extensions) {
$filename = strtolower($filename);
foreach ($extensions as $extension) {
if ($extension[0] !== '.') {
$extension = '.' . $extension;
}
if (str_ends_with($filename, strtolower($extension))) {
return true;
}
}
return false;
}
/**
* 画像ファイルか判定
*/
public static function isImage($filename) {
$extensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg'];
return self::hasAnyExtension($filename, $extensions);
}
/**
* ドキュメントファイルか判定
*/
public static function isDocument($filename) {
$extensions = ['.pdf', '.doc', '.docx', '.txt', '.rtf', '.odt'];
return self::hasAnyExtension($filename, $extensions);
}
/**
* 動画ファイルか判定
*/
public static function isVideo($filename) {
$extensions = ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv'];
return self::hasAnyExtension($filename, $extensions);
}
/**
* 音声ファイルか判定
*/
public static function isAudio($filename) {
$extensions = ['.mp3', '.wav', '.flac', '.aac', '.ogg', '.m4a'];
return self::hasAnyExtension($filename, $extensions);
}
/**
* 圧縮ファイルか判定
*/
public static function isArchive($filename) {
$extensions = ['.zip', '.rar', '.7z', '.tar', '.gz', '.bz2'];
return self::hasAnyExtension($filename, $extensions);
}
/**
* 実行ファイルか判定
*/
public static function isExecutable($filename) {
$extensions = ['.exe', '.bat', '.cmd', '.sh', '.app'];
return self::hasAnyExtension($filename, $extensions);
}
/**
* スクリプトファイルか判定
*/
public static function isScript($filename) {
$extensions = ['.php', '.js', '.py', '.rb', '.pl', '.sh'];
return self::hasAnyExtension($filename, $extensions);
}
/**
* 拡張子を取得
*/
public static function getExtension($filename) {
$dotPos = strrpos($filename, '.');
if ($dotPos === false) {
return null;
}
return substr($filename, $dotPos);
}
/**
* ファイル種別を取得
*/
public static function getFileType($filename) {
if (self::isImage($filename)) return 'image';
if (self::isDocument($filename)) return 'document';
if (self::isVideo($filename)) return 'video';
if (self::isAudio($filename)) return 'audio';
if (self::isArchive($filename)) return 'archive';
if (self::isExecutable($filename)) return 'executable';
if (self::isScript($filename)) return 'script';
return 'other';
}
}
// 使用例
echo "=== ファイル拡張子チェック ===\n";
$files = [
'photo.jpg',
'document.pdf',
'video.mp4',
'music.mp3',
'archive.zip',
'script.php',
'program.exe',
'README'
];
foreach ($files as $file) {
echo "\nファイル: {$file}\n";
echo " 拡張子: " . (FileExtensionChecker::getExtension($file) ?? 'なし') . "\n";
echo " 種別: " . FileExtensionChecker::getFileType($file) . "\n";
echo " 画像: " . (FileExtensionChecker::isImage($file) ? 'Yes' : 'No') . "\n";
echo " ドキュメント: " . (FileExtensionChecker::isDocument($file) ? 'Yes' : 'No') . "\n";
echo " スクリプト: " . (FileExtensionChecker::isScript($file) ? 'Yes' : 'No') . "\n";
}
echo "\n=== 特定拡張子チェック ===\n";
var_dump(FileExtensionChecker::hasExtension('image.jpg', '.jpg')); // true
var_dump(FileExtensionChecker::hasExtension('image.jpg', 'jpg')); // true
var_dump(FileExtensionChecker::hasExtension('image.JPG', '.jpg')); // true
var_dump(FileExtensionChecker::hasExtension('image.png', '.jpg')); // false
例2: URL/パスの処理
class PathSuffixChecker {
/**
* スラッシュで終わるか判定
*/
public static function endsWithSlash($path) {
return str_ends_with($path, '/') || str_ends_with($path, '\\');
}
/**
* ディレクトリパスか判定(スラッシュで終わる)
*/
public static function isDirectory($path) {
return self::endsWithSlash($path);
}
/**
* クエリ文字列を含むか判定
*/
public static function hasQueryString($url) {
return str_contains($url, '?');
}
/**
* フラグメントで終わるか判定
*/
public static function hasFragment($url) {
return str_contains($url, '#');
}
/**
* 特定のパスで終わるか判定
*/
public static function endsWithPath($url, $path) {
return str_ends_with(rtrim($url, '/'), rtrim($path, '/'));
}
/**
* ファイル名で終わるか判定
*/
public static function endsWithFile($path, $filename) {
return str_ends_with($path, $filename);
}
/**
* バックアップファイルか判定
*/
public static function isBackupFile($filename) {
$backupSuffixes = ['.bak', '.backup', '.old', '~'];
foreach ($backupSuffixes as $suffix) {
if (str_ends_with(strtolower($filename), $suffix)) {
return true;
}
}
return false;
}
/**
* 一時ファイルか判定
*/
public static function isTempFile($filename) {
$tempSuffixes = ['.tmp', '.temp', '.swp', '~'];
foreach ($tempSuffixes as $suffix) {
if (str_ends_with(strtolower($filename), $suffix)) {
return true;
}
}
return false;
}
}
// 使用例
echo "=== パス/URLチェック ===\n";
$paths = [
'/var/www/html/',
'/var/www/html/index.php',
'https://example.com/path/',
'https://example.com/page.html',
'document.pdf.bak',
'file.txt.tmp'
];
foreach ($paths as $path) {
echo "\nパス: {$path}\n";
echo " スラッシュ終了: " . (PathSuffixChecker::endsWithSlash($path) ? 'Yes' : 'No') . "\n";
echo " ディレクトリ: " . (PathSuffixChecker::isDirectory($path) ? 'Yes' : 'No') . "\n";
echo " バックアップ: " . (PathSuffixChecker::isBackupFile($path) ? 'Yes' : 'No') . "\n";
echo " 一時ファイル: " . (PathSuffixChecker::isTempFile($path) ? 'Yes' : 'No') . "\n";
}
echo "\n=== パス終了チェック ===\n";
var_dump(PathSuffixChecker::endsWithPath('/var/www/html', '/html')); // true
var_dump(PathSuffixChecker::endsWithPath('/var/www/html/', '/html')); // true
var_dump(PathSuffixChecker::endsWithFile('/path/to/file.txt', 'file.txt')); // true
例3: 文の終わり方検出
class SentenceEnding {
/**
* 文が完結しているか判定
*/
public static function isComplete($sentence) {
$endings = ['.', '!', '?', '。', '!', '?'];
$sentence = trim($sentence);
foreach ($endings as $ending) {
if (str_ends_with($sentence, $ending)) {
return true;
}
}
return false;
}
/**
* 疑問文か判定
*/
public static function isQuestion($sentence) {
$sentence = trim($sentence);
return str_ends_with($sentence, '?') || str_ends_with($sentence, '?');
}
/**
* 感嘆文か判定
*/
public static function isExclamation($sentence) {
$sentence = trim($sentence);
return str_ends_with($sentence, '!') || str_ends_with($sentence, '!');
}
/**
* 平叙文か判定
*/
public static function isStatement($sentence) {
$sentence = trim($sentence);
return str_ends_with($sentence, '.') || str_ends_with($sentence, '。');
}
/**
* 省略符で終わるか判定
*/
public static function isEllipsis($sentence) {
$sentence = trim($sentence);
return str_ends_with($sentence, '...') ||
str_ends_with($sentence, '…');
}
/**
* 引用符で終わるか判定
*/
public static function endsWithQuote($sentence) {
$sentence = trim($sentence);
return str_ends_with($sentence, '"') ||
str_ends_with($sentence, "'") ||
str_ends_with($sentence, '」') ||
str_ends_with($sentence, '』');
}
/**
* 文のタイプを取得
*/
public static function getSentenceType($sentence) {
if (self::isQuestion($sentence)) return 'question';
if (self::isExclamation($sentence)) return 'exclamation';
if (self::isStatement($sentence)) return 'statement';
if (self::isEllipsis($sentence)) return 'ellipsis';
return 'incomplete';
}
/**
* 丁寧な終わり方か判定(日本語)
*/
public static function isPoliteEnding($sentence) {
$politeEndings = ['です。', 'ます。', 'ました。', 'ません。'];
foreach ($politeEndings as $ending) {
if (str_ends_with($sentence, $ending)) {
return true;
}
}
return false;
}
}
// 使用例
echo "=== 文の終わり方検出 ===\n";
$sentences = [
'What is PHP?',
'This is amazing!',
'PHP is a programming language.',
'I was thinking...',
'He said "Hello"',
'これはPHPです。',
'ありがとうございます。',
'Incomplete sentence'
];
foreach ($sentences as $sentence) {
echo "\n文: {$sentence}\n";
echo " 完結: " . (SentenceEnding::isComplete($sentence) ? 'Yes' : 'No') . "\n";
echo " 種類: " . SentenceEnding::getSentenceType($sentence) . "\n";
echo " 疑問文: " . (SentenceEnding::isQuestion($sentence) ? 'Yes' : 'No') . "\n";
echo " 感嘆文: " . (SentenceEnding::isExclamation($sentence) ? 'Yes' : 'No') . "\n";
echo " 省略符: " . (SentenceEnding::isEllipsis($sentence) ? 'Yes' : 'No') . "\n";
echo " 引用符終了: " . (SentenceEnding::endsWithQuote($sentence) ? 'Yes' : 'No') . "\n";
echo " 丁寧: " . (SentenceEnding::isPoliteEnding($sentence) ? 'Yes' : 'No') . "\n";
}
例4: データバリデーション
class DataValidator {
/**
* メールアドレスのドメインチェック
*/
public static function hasEmailDomain($email, $domain) {
return str_ends_with(strtolower($email), '@' . strtolower($domain));
}
/**
* 会社のメールアドレスか判定
*/
public static function isCompanyEmail($email, $companyDomains) {
$email = strtolower($email);
foreach ($companyDomains as $domain) {
if (str_ends_with($email, '@' . strtolower($domain))) {
return true;
}
}
return false;
}
/**
* フリーメールか判定
*/
public static function isFreeEmail($email) {
$freeDomains = ['gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com'];
return self::isCompanyEmail($email, $freeDomains);
}
/**
* 電話番号が特定の市外局番で終わるか
*/
public static function hasAreaCode($phone, $areaCode) {
$phone = preg_replace('/[^0-9]/', '', $phone);
return str_ends_with($phone, $areaCode);
}
/**
* URLがトップレベルドメインで終わるか
*/
public static function hasTLD($url, $tld) {
if ($tld[0] !== '.') {
$tld = '.' . $tld;
}
// クエリ文字列やフラグメントを除去
$url = strtok($url, '?#');
return str_ends_with(strtolower($url), strtolower($tld));
}
/**
* 特定の国のTLDか判定
*/
public static function hasCountryTLD($url) {
$countryTLDs = ['.jp', '.us', '.uk', '.cn', '.de', '.fr'];
foreach ($countryTLDs as $tld) {
if (self::hasTLD($url, $tld)) {
return true;
}
}
return false;
}
/**
* セキュアなファイル名か判定(安全な拡張子)
*/
public static function isSafeExtension($filename) {
$safeExtensions = ['.txt', '.pdf', '.jpg', '.png', '.gif', '.doc', '.docx'];
$filename = strtolower($filename);
foreach ($safeExtensions as $ext) {
if (str_ends_with($filename, $ext)) {
return true;
}
}
return false;
}
/**
* 危険なファイル名か判定
*/
public static function isDangerousExtension($filename) {
$dangerousExtensions = ['.exe', '.bat', '.cmd', '.sh', '.php', '.js', '.vbs'];
$filename = strtolower($filename);
foreach ($dangerousExtensions as $ext) {
if (str_ends_with($filename, $ext)) {
return true;
}
}
return false;
}
}
// 使用例
echo "=== データバリデーション ===\n";
// メールアドレスチェック
$emails = [
'user@example.com',
'john@gmail.com',
'staff@company.co.jp'
];
echo "メールアドレスチェック:\n";
foreach ($emails as $email) {
$isFree = DataValidator::isFreeEmail($email) ? 'フリー' : '独自';
echo " {$email}: {$isFree}\n";
}
// ドメインチェック
echo "\n会社ドメインチェック:\n";
$companyDomains = ['example.com', 'company.co.jp'];
foreach ($emails as $email) {
$isCompany = DataValidator::isCompanyEmail($email, $companyDomains) ? 'Yes' : 'No';
echo " {$email}: {$isCompany}\n";
}
// TLDチェック
echo "\nTLDチェック:\n";
$urls = [
'https://example.com',
'https://example.co.jp',
'https://example.de'
];
foreach ($urls as $url) {
echo " {$url}:\n";
echo " .com: " . (DataValidator::hasTLD($url, '.com') ? 'Yes' : 'No') . "\n";
echo " .jp: " . (DataValidator::hasTLD($url, '.jp') ? 'Yes' : 'No') . "\n";
echo " 国別TLD: " . (DataValidator::hasCountryTLD($url) ? 'Yes' : 'No') . "\n";
}
// ファイル拡張子の安全性チェック
echo "\nファイル安全性チェック:\n";
$files = ['document.pdf', 'script.php', 'photo.jpg', 'program.exe'];
foreach ($files as $file) {
$safe = DataValidator::isSafeExtension($file) ? '安全' : '要確認';
$dangerous = DataValidator::isDangerousExtension($file) ? '危険' : 'OK';
echo " {$file}: {$safe} / {$dangerous}\n";
}
例5: バージョン文字列の処理
class VersionChecker {
/**
* ベータバージョンか判定
*/
public static function isBeta($version) {
return str_ends_with(strtolower($version), '-beta') ||
str_ends_with(strtolower($version), '.beta');
}
/**
* アルファバージョンか判定
*/
public static function isAlpha($version) {
return str_ends_with(strtolower($version), '-alpha') ||
str_ends_with(strtolower($version), '.alpha');
}
/**
* リリース候補か判定
*/
public static function isReleaseCandidate($version) {
return str_ends_with(strtolower($version), '-rc') ||
preg_match('/-rc\d+$/i', $version);
}
/**
* 開発版か判定
*/
public static function isDev($version) {
return str_ends_with(strtolower($version), '-dev') ||
str_ends_with(strtolower($version), '.dev');
}
/**
* 安定版か判定
*/
public static function isStable($version) {
return !self::isBeta($version) &&
!self::isAlpha($version) &&
!self::isReleaseCandidate($version) &&
!self::isDev($version);
}
/**
* LTS(長期サポート)版か判定
*/
public static function isLTS($version) {
return str_ends_with(strtolower($version), '-lts') ||
str_ends_with(strtolower($version), '.lts');
}
/**
* バージョンタイプを取得
*/
public static function getVersionType($version) {
if (self::isAlpha($version)) return 'alpha';
if (self::isBeta($version)) return 'beta';
if (self::isReleaseCandidate($version)) return 'rc';
if (self::isDev($version)) return 'dev';
if (self::isLTS($version)) return 'lts';
if (self::isStable($version)) return 'stable';
return 'unknown';
}
}
// 使用例
echo "=== バージョンチェック ===\n";
$versions = [
'1.0.0',
'2.0.0-beta',
'3.0.0-alpha',
'4.0.0-rc1',
'5.0.0-dev',
'6.0.0-lts'
];
foreach ($versions as $version) {
$type = VersionChecker::getVersionType($version);
echo "{$version}: {$type}\n";
}
echo "\n=== 詳細チェック ===\n";
foreach ($versions as $version) {
echo "\n{$version}:\n";
echo " Alpha: " . (VersionChecker::isAlpha($version) ? 'Yes' : 'No') . "\n";
echo " Beta: " . (VersionChecker::isBeta($version) ? 'Yes' : 'No') . "\n";
echo " RC: " . (VersionChecker::isReleaseCandidate($version) ? 'Yes' : 'No') . "\n";
echo " Dev: " . (VersionChecker::isDev($version) ? 'Yes' : 'No') . "\n";
echo " Stable: " . (VersionChecker::isStable($version) ? 'Yes' : 'No') . "\n";
echo " LTS: " . (VersionChecker::isLTS($version) ? 'Yes' : 'No') . "\n";
}
例6: ユーザー名/IDの検証
class UsernameValidator {
/**
* ボット用アカウントか判定
*/
public static function isBot($username) {
$botSuffixes = ['_bot', '-bot', 'Bot', '_BOT'];
foreach ($botSuffixes as $suffix) {
if (str_ends_with($username, $suffix)) {
return true;
}
}
return false;
}
/**
* 管理者アカウントか判定
*/
public static function isAdmin($username) {
$adminSuffixes = ['_admin', '-admin', 'Admin', '_ADMIN'];
foreach ($adminSuffixes as $suffix) {
if (str_ends_with($username, $suffix)) {
return true;
}
}
return false;
}
/**
* テストアカウントか判定
*/
public static function isTest($username) {
$testSuffixes = ['_test', '-test', 'Test', '_TEST', '_demo'];
foreach ($testSuffixes as $suffix) {
if (str_ends_with($username, $suffix)) {
return true;
}
}
return false;
}
/**
* 数字で終わるか判定
*/
public static function endsWithNumber($username) {
return preg_match('/\d$/', $username) === 1;
}
/**
* アンダースコアで終わるか判定
*/
public static function endsWithUnderscore($username) {
return str_ends_with($username, '_');
}
/**
* 公式アカウントか判定
*/
public static function isOfficial($username) {
$officialSuffixes = ['_official', 'Official', '_公式'];
foreach ($officialSuffixes as $suffix) {
if (str_ends_with($username, $suffix)) {
return true;
}
}
return false;
}
}
// 使用例
echo "=== ユーザー名検証 ===\n";
$usernames = [
'john_doe',
'support_bot',
'admin_user',
'test_account',
'user123',
'company_official',
'normaluser'
];
foreach ($usernames as $username) {
echo "\nユーザー名: {$username}\n";
echo " ボット: " . (UsernameValidator::isBot($username) ? 'Yes' : 'No') . "\n";
echo " 管理者: " . (UsernameValidator::isAdmin($username) ? 'Yes' : 'No') . "\n";
echo " テスト: " . (UsernameValidator::isTest($username) ? 'Yes' : 'No') . "\n";
echo " 公式: " . (UsernameValidator::isOfficial($username) ? 'Yes' : 'No') . "\n";
echo " 数字終了: " . (UsernameValidator::endsWithNumber($username) ? 'Yes' : 'No') . "\n";
echo " _終了: " . (UsernameValidator::endsWithUnderscore($username) ? 'Yes' : 'No') . "\n";
}
例7: 言語/文字コード検出
class LanguageDetector {
/**
* 日本語で終わるか判定
*/
public static function endsWithJapanese($text) {
$japaneseEndings = ['です', 'ます', 'した', 'だ', 'か', 'ね', 'よ', '。'];
foreach ($japaneseEndings as $ending) {
if (str_ends_with($text, $ending)) {
return true;
}
}
return false;
}
/**
* 英語の文で終わるか判定
*/
public static function endsWithEnglishSentence($text) {
$text = trim($text);
return str_ends_with($text, '.') ||
str_ends_with($text, '!') ||
str_ends_with($text, '?');
}
/**
* 中国語で終わるか判定
*/
public static function endsWithChinese($text) {
$chineseEndings = ['。', '!', '?', '了', '的', '吗'];
foreach ($chineseEndings as $ending) {
if (str_ends_with($text, $ending)) {
return true;
}
}
return false;
}
/**
* HTMLタグで終わるか判定
*/
public static function endsWithHtmlTag($text) {
return str_ends_with(trim($text), '>');
}
/**
* コードブロックで終わるか判定
*/
public static function endsWithCodeBlock($text) {
return str_ends_with(trim($text), '```') ||
str_ends_with(trim($text), '</code>');
}
}
// 使用例
echo "=== 言語検出 ===\n";
$texts = [
'これはテストです',
'This is a test.',
'这是测试。',
'<p>HTML content</p>',
'```code block```',
'Incomplete'
];
foreach ($texts as $text) {
echo "\nテキスト: {$text}\n";
echo " 日本語終了: " . (LanguageDetector::endsWithJapanese($text) ? 'Yes' : 'No') . "\n";
echo " 英語終了: " . (LanguageDetector::endsWithEnglishSentence($text) ? 'Yes' : 'No') . "\n";
echo " 中国語終了: " . (LanguageDetector::endsWithChinese($text) ? 'Yes' : 'No') . "\n";
echo " HTMLタグ: " . (LanguageDetector::endsWithHtmlTag($text) ? 'Yes' : 'No') . "\n";
echo " コードブロック: " . (LanguageDetector::endsWithCodeBlock($text) ? 'Yes' : 'No') . "\n";
}
従来の方法との比較
$text = "Hello World";
$suffix = "World";
// 新しい方法(PHP 8.0+)
if (str_ends_with($text, $suffix)) {
echo "末尾が一致\n";
}
// 従来の方法
if (substr($text, -strlen($suffix)) === $suffix) {
echo "末尾が一致\n";
}
// または正規表現
if (preg_match('/' . preg_quote($suffix) . '$/', $text)) {
echo "末尾が一致\n";
}
// 利点:
// - より直感的
// - 文字列長の計算不要
// - コードが読みやすい
まとめ
str_ends_with()関数の特徴をまとめると:
できること:
- 文字列の末尾一致チェック
- サフィックスの検証
- ブール値を返す
推奨される使用場面:
- ファイル拡張子チェック
- URL/パス処理
- 文の終わり方検出
- バリデーション
- バージョン判定
- ユーザー名検証
利点:
- 直感的で読みやすい
- 長さ計算不要
- PHP 8.0で標準化
注意点:
- PHP 8.0以降でのみ使用可能
- 大文字小文字を区別する
- 空文字列は常に
true
関連関数:
str_starts_with(): 先頭一致str_contains(): 含まれるかsubstr(): 部分文字列を取得strrpos(): 最後の出現位置
使い分け:
// 末尾一致
str_ends_with($text, $suffix)
// 先頭一致
str_starts_with($text, $prefix)
// 含まれるか
str_contains($text, $needle)
// 末尾部分を取得
substr($text, -$length)
str_ends_with()は、PHP 8.0で追加された非常に便利な関数です。ファイル拡張子のチェックや文の終わり方の判定など、様々な場面で活躍します。PHP 8.0以降を使用している場合は積極的に活用しましょう!
