markdown
はじめに (対象読者・この記事でわかること)
このページは、PHPで「山田太郎 taro@example.com」や「taro@example.com」のようなメールアドレス表記から、名前部分とメールアドレス部分を正確に切り出す方法を知りたい方を対象としています。
- プログラミング初心者から中級者まで、文字列操作や正規表現に多少の経験がある人
- 自作の問い合わせフォームやユーザー管理機能で、入力された文字列を安全に分解したい方
この記事を読み終えると、
1. メールアドレス表記のパターンを把握できる
2. preg_match と filter_var を組み合わせた実装が書ける
3. 不正な入力に対して例外処理を行うベストプラクティスが身につく
というスキルが身につきます。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。
- PHP の基本構文(変数、関数、if 文)
- 正規表現の基礎(メタ文字、量指定子)
- filter_var 関数によるメールアドレスバリデーション
メールアドレス文字列の分割が必要な背景と概要
Web アプリケーションでは、ユーザーがフリーフィールドに「名前 <メールアドレス>」や「メールアドレス」のみといった形で入力するケースが頻出します。
そのまま保存すると、名前とメールアドレスが混在した状態になるため、以下のような問題が発生します。
- 検索やソートが困難になる(メールアドレスだけで抽出したいが、余計な文字が混在)
- メール送信時にエラーになる(
mail()関数はFrom: 名前 <mail@domain>形式を期待しているが、余計な空白や記号があるとヘッダー不正) - セキュリティリスクになる(不正な文字列がそのまま DB に保存されると、XSS やメールヘッダーインジェクションの入口になる)
このようなリスクを回避するために、入力文字列を「名前」と「メールアドレス」に分割し、個別に検証・保存することが必須です。
PHP では、正規表現でパターンマッチを行い、マッチした結果を配列に格納する preg_match が有力です。さらに、FILTER_VALIDATE_EMAIL を併用すれば、メールアドレスの形式チェックを簡単に行えます。以下では、実装手順と注意点を詳しく解説します。
実装ステップ:正規表現とPHP組み込み関数で名前とメールアドレスを抽出
ステップ1 入力文字列の基本形を把握する
まずは想定する入力パターンを列挙します。代表的なケースは次の通りです。
| パターン | 例 |
|---|---|
名前とメールアドレスが < と > で囲まれる |
山田太郎 <taro@example.com> |
| メールアドレスだけ | taro@example.com |
| 名前だけ(メールアドレスが省略)※エラー対象 | 山田太郎 |
| 複数のスペースや改行が混在 | 山田太郎 < taro@example.com > |
このうち 有効な入力は 1 と 2 です。3 はエラー、4 は正規化してから処理します。
この前提で正規表現を設計します。
ステップ2 正規表現の構築
以下の正規表現は、名前(任意)とメールアドレス(必須)を抽出します。
Php$pattern = '/^\s*(?:(.+?)\s*<\s*)?([a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,})\s*>?\s*$/i';
ポイント解説:
^\s*…… 行頭の空白・タブを許容(?:(.+?)\s*<\s*)?…… 名前部分は任意。.+?は最短一致で「<」までを取得し、余計な空白を除去([a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,})…… RFC 5322 のサブセットでメールアドレスをキャプチャ\s*>?……>があってもなくても OK。末尾の空白も許容/i…… 大文字小文字を無視
この正規表現は次のようにマッチします。
| 入力 | $matches[1] (名前) |
$matches[2] (メール) |
|---|---|---|
山田太郎 <taro@example.com> |
山田太郎 |
taro@example.com |
taro@example.com |
未定義 | taro@example.com |
山田太郎 < taro@example.com > |
山田太郎 |
taro@example.com |
ステップ3 PHP コードで実装する
以下に完成形の関数を示します。コメントで重要ポイントを補足しています。
Php<?php /** * 入力文字列から名前とメールアドレスを抽出する * * @param string $input 生の入力文字列 * @return array ['name' => ?string, 'email' => string] 成功時は配列、失敗時は例外を投げる * @throws InvalidArgumentException 無効な形式の場合 */ function extractNameAndEmail(string $input): array { // 正規表現パターン(ステップ2で説明したもの) $pattern = '/^\s*(?:(.+?)\s*<\s*)?([a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,})\s*>?\s*$/i'; if (!preg_match($pattern, $input, $matches)) { // 正規表現にマッチしなければフォーマットエラー throw new InvalidArgumentException('入力文字列の形式が正しくありません。'); } // メールアドレスは必ず2番目のキャプチャグループ $email = $matches[2]; // さらに filter_var でメール形式を厳密に検証 if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { throw new InvalidArgumentException('メールアドレスの形式が無効です。'); } // 名前は存在すればトリム、無ければ null $name = isset($matches[1]) ? trim($matches[1]) : null; // 名前が空文字列の場合は null に正規化 if ($name === '') { $name = null; } return ['name' => $name, 'email' => $email]; }
使用例
Phptry { $data = extractNameAndEmail(' 山田太郎 < taro@example.com > '); // 結果: ['name' => '山田太郎', 'email' => 'taro@example.com'] var_export($data); } catch (InvalidArgumentException $e) { echo 'エラー: ' . $e->getMessage(); }
ステップ4 エラーハンドリングとテストケース
実装時に陥りやすいポイントと対策をまとめます。
| シナリオ | 失敗原因 | 対策 |
|---|---|---|
<> だけが入力される |
正規表現がマッチしない | 例外メッセージで「形式が正しくありません」 |
john@ のようにドメインが欠落 |
filter_var が false を返す |
FILTER_VALIDATE_EMAIL による二段階チェック |
名前に < や > が含まれる |
正規表現が早めに区切ってしまう | 必要に応じて名前用のエスケープルールを追加(例:\\<) |
| 複数メールアドレスが混在 | 正規表現が最初のメールだけを取得 | 入力バリデーションで「1件のみ許可」旨を明示 |
テストは PHPUnit でデータプロバイダーを使い、典型的な 10 種類以上のケースを網羅すると安心です。
Phppublic function emailProvider() { return [ ['山田太郎 <taro@example.com>', ['name'=>'山田太郎','email'=>'taro@example.com']], ['taro@example.com', ['name'=>null,'email'=>'taro@example.com']], [' taro@example.com ', ['name'=>null,'email'=>'taro@example.com']], ['山田 <invalid-email>', null], // 例外期待 // さらに多数... ]; }
ハマった点やエラー解決
-
空白文字の扱い
初期実装ではtrim()を忘れたため、名前に前後のスペースが残り、データベース検索でヒットしませんでした。正規表現内で\s*を使用し、マッチ後もtrim()を必ず呼ぶことで解決。 -
ローカルドメイン(例:user@localhost)
FILTER_VALIDATE_EMAILはデフォルトでローカルドメインを不正と見なすケースがあります。実務でローカル環境のテストが必要なときは、FILTER_FLAG_EMAIL_UNICODE等のフラグや正規表現の拡張で対応してください。 -
UTF-8 の名前
日本語名はマルチバイト文字で問題ありませんが、.+?がデフォルトでバイト単位の最短一致になるため、u修飾子(/u)を付加すると Unicode 対応が保証されます。上記コードではiのみ使用していますが、必要に応じてuiに変更してください。
まとめ
本記事では、PHP の正規表現と組み込みバリデーション関数を活用し、
- 「名前 <メールアドレス>」形式やメールアドレス単体の文字列から 名前とメールアドレスを安全に分割する手順を示しました。
- 正規表現の設計ポイント、例外処理、テスト手法、よくある落とし穴とその対策を具体例とともに解説しました。
この実装を取り入れることで、入力データの整合性が向上し、メール送信やデータベース検索時のエラーを未然に防げます。次回は、取得した名前とメールアドレスを MySQL に保存する際のエンコーディング注意点や、複数件入力を一括処理する方法について解説する予定です。
参考資料
- PHP: preg_match - Manual
- PHP: filter_var - Manual
- RFC 5322 – Internet Message Format
- PHP Coding Standards PSR-12
