[0037]laravel-imapを利用してメールサーバからIMAPでメール受信
■インストールと設定
まず、laravel-imapをインストールします。
この例の環境は、Lalavelフレームワークのバージョン→10
PHPのバージョン→8.2.28
lalavel-imapのバージョン→6.2(特に指定せずインストールしたときのバージョン)
コマンドプロンプト
#インストール
% composer require webklex/laravel-imap
#config配下にimap.php作成
% php artisan vendor:publish --provider="Webklex\IMAP\Providers\LaravelServiceProvider"
vendor配下にwebklexフォルダ、config配下にimap.phpが作成されます。
次にimap.phpを修正します。
imap.php
/*
|--------------------------------------------------------------------------
| Available IMAP accounts
|--------------------------------------------------------------------------
|
| Please list all IMAP accounts which you are planning to use within the
| array below.
|
*/
'accounts' => [
'default' => [// account identifier
'host' => env('IMAP_HOST', 'ホスト'),
'port' => env('IMAP_PORT', ポート),
'protocol' => env('IMAP_PROTOCOL', 'imap'), //might also use imap, [pop3 or nntp (untested)]
'encryption' => env('IMAP_ENCRYPTION', 'ssl'), // Supported: false, 'ssl', 'tls', 'notls', 'starttls'
'validate_cert' => env('IMAP_VALIDATE_CERT', false),
'username' => env('IMAP_USERNAME', 'ユーザー'),
'password' => env('IMAP_PASSWORD', 'パスワード'),
'authentication' => env('IMAP_AUTHENTICATION', null),
'proxy' => [
'socket' => null,
'request_fulluri' => false,
'username' => null,
'password' => null,
],
"timeout" => 30,
"extensions" => []
],
'test2' => [// account identifier
'host' => env('IMAP_HOST', 'ホスト'),
'port' => env('IMAP_PORT',ポート),
'protocol' => env('IMAP_PROTOCOL', 'imap'), //might also use imap, [pop3 or nntp (untested)]
'encryption' => env('IMAP_ENCRYPTION', 'ssl'), // Supported: false, 'ssl', 'tls', 'notls', 'starttls'
'validate_cert' => env('IMAP_VALIDATE_CERT', false),
'username' => env('IMAP_USERNAME', 'ユーザー'),
'password' => env('IMAP_PASSWORD', 'パスワード'),
'authentication' => env('IMAP_AUTHENTICATION', null),
'proxy' => [
'socket' => null,
'request_fulluri' => false,
'username' => null,
'password' => null,
],
"timeout" => 30,
"extensions" => []
],
'test3' => [// account identifier
'host' => env('IMAP_HOST', 'ホスト'),
'port' => env('IMAP_PORT', ポート),
'protocol' => env('IMAP_PROTOCOL', 'imap'), //might also use imap, [pop3 or nntp (untested)]
'encryption' => env('IMAP_ENCRYPTION', 'ssl'), // Supported: false, 'ssl', 'tls', 'notls', 'starttls'
'validate_cert' => env('IMAP_VALIDATE_CERT', false),
'username' => env('IMAP_USERNAME', 'ユーザー'),
'password' => env('IMAP_PASSWORD', 'パスワード'),
'authentication' => env('IMAP_AUTHENTICATION', null),
'proxy' => [
'socket' => null,
'request_fulluri' => false,
'username' => null,
'password' => null,
],
"timeout" => 30,
"extensions" => []
],
/*
'gmail' => [ // account identifier
'host' => 'imap.gmail.com',
'port' => 993,
'encryption' => 'ssl',
'validate_cert' => true,
'username' => 'example@gmail.com',
'password' => 'PASSWORD',
'authentication' => 'oauth',
],
'another' => [ // account identifier
'host' => '',
'port' => 993,
'encryption' => false,
'validate_cert' => true,
'username' => '',
'password' => '',
'authentication' => null,
]
*/
],
default以外にもtest2,test3のように追加し、複数設定することができます。
■メール受信
次は利用する側です。今回の場合はCronでメール受信をしてDBへ保持するものです
メール受信に関係のある資料が少ない。。試行錯誤しました。
受信に関係あるところだけ抜粋しています。
Cronの一部
use Webklex\IMAP\Facades\Client;
use Storage;
//接続先 imap.phpのdefaultの設定に接続
$client = Client::account('default');
//test2に接続したい場合は、accountを変更するだけ
$client = Client::account('test2');
//接続
$client->connect();
//接続できた場合はtrue
$status = $client->isConnected();
//フォルダ操作
$folders = $client->getFolders();
foreach ($folders as $folder) {
echo 'フォルダーの名前は' .$folder->name;
echo 'メールの数は'.$folder->query()->all()->count();
echo "通です";
}
//フォルダ名を指定し、フォルダ情報を取得
$info = $client->checkFolder("INBOX");
//UIDの最後を取得
$uidlast = $info["uidnext"];
//フォルダ名を指定
$oFolder = $client->getFolder("INBOX");
//添付ファイル保存場所
$file_path = "/file_up/other/mail_attach_file/";
//メッセージの取得 それぞれ
$messages = $oFolder->messages()->UID($uid)->get();//UIDを指定し、1件取得
if(_helper_cnt_chk(@$messages) == 0){
echo "無いので終了uid:".$uid."\n";
break;//抜ける
}
//query() と messages() はどちらも変わらないよう?同じコードでもデータがない日付までさかのぼるとエラーになったりした
$query = $oFolder->query();
$messages = $query->since(now()->subDays(1))->get();
$messages = $query->since('9.05.2025')->get();
$messages = $query->since('16.05.2025')->setFetchOrder("desc")->limit()->get();
foreach ($messages as $message) {
echo "uid:".$uid."\n";
//エンコードを知りたいのでattributeを取得
$attributes = $message->getAttributes();
$content_type = $attributes['content_type'][0];
echo "content_type:".$content_type."\n";
//charset=gb2312の場合はタイトルが文字化けするので対策
if(strpos($content_type,'charset=gb2312') !== false){
$setArr['title'] = mb_convert_encoding($message->getSubject(), "UTF-8", "GBK");//文字化け対策
}else{
$setArr['title'] = strval($message->getSubject());//タイトルの取得
}
//受信時間はtimezoneが日本とは限らないので日本時間に合わせる
$dates = $message->getDate()[0];//Carbon
if($dates->timezone != "+09:00"){
if(strpos($dates->timezone,'+') !== false){
$timezones = str_replace("+", "", $dates->timezone);
$exs = explode(":", $timezones);
$timecnt = 9-intval($exs[0]);
}else{
$timezones = str_replace("-", "", $dates->timezone);
$exs = explode(":", $timezones);
$timecnt = 9+intval($exs[0]);
}
$dates = $message->getDate()[0]->addHours($timecnt);
}
$setArr['mail_date'] = $dates->toDateTimeString();//秒まで取得
$setArr['mail_d'] = $dates->toDateString();//日付のみ取得
$setArr['mail_t'] = $dates->toTimeString();//時分秒のみ取得
$setArr['uid'] = $message->getUid();//UID取得
//Fromアドレスと名前を分けて保持する
$setArr['from_nm'] = "";
$setArr['from_mails'] = $message->getFrom()[0]->mail;
if($message->getFrom()[0]->mail != $message->getFrom()[0]->full){
$setArr['from_nm'] = $message->getFrom()[0]->full;
$setArr['from_nm'] = str_replace('"','', $setArr['from_nm']);
if(strpos($setArr['from_nm'],'<') !== false){
$setArr['from_nm'] = str_replace("<".$setArr['from_mails'].">", "", $setArr['from_nm']);
$setArr['from_nm'] = str_replace(' ','', $setArr['from_nm']);
}
}
//Toは複数ある可能性があり、Toの数を取得するような関数が見つからなかったので、max宛先数を最大にしてToアドレスと名前を分けて保持する
$to_nm = "";
$tos = $message->getTo();
$cntm = 500;//max宛先数
$to_mails = array();
$to_nms = array();
for($i=0;$i < $cntm;$i++){
if(empty($tos[$i])){
break;
}else{
if($tos[$i]->mail != $tos[$i]->full){
$full = $tos[$i]->full;
$full = str_replace('"','', $full);
if(strpos($full,'<') !== false){
$full = str_replace("<".$tos[$i]->mail.">", "", $full);
$full = str_replace(' ','', $full);
}
$to_nms[] = $full;
}
$to_mails[] = $tos[$i]->mail;
}
}
if(!empty($from_nms)){
$setArr['to_nm'] = _helper_imp_chg(",",$to_nms);
}
$setArr['to_mails'] = _helper_imp_chg(",",$to_mails);
//Ccアドレスも同様
$tos = $message->getCc();
$cntm = 500;//max宛先数
$cc_mails = array();
for($i=0;$i < $cntm;$i++){
if(empty($tos[$i])){
break;
}else{
$cc_mails[] = $tos[$i]->mail;
}
}
$setArr['cc_mails'] = _helper_imp_chg(",",$cc_mails);
if($message->hasHTMLBody()){
//HTMLメールの場合の本文
$setArr['body'] = $message->getHTMLBody();
}else{
if($message->hasTextBody()){
//テキストメールの場合の本文
$setArr['body'] = $message->getTextBody();
}
}
if(empty($setArr['body'])){
$setArr['body'] = null;
}
//迷惑メールと判定されるもの除外 ※「XXX」は各時それぞれ
if(strpos($setArr['body'],'XXX') !== false && strpos($setArr['from_mails'],'XXXX') !== false){
continue;
}
$setArr['created_at'] = date('Y-m-d H:i:s');
if ($message->hasAttachments()) { //添付ファイルがあるかどうか
$fc = 0;
foreach ($message->getAttachments() as $attachment) {
$uniqid = $setArr['mail_id']."_".$fc;
if(!file_exists($file_path.$uniqid)){
mkdir($file_path.$uniqid);
}
Storage::put($uniqid.'/'.$attachment->name, $attachment->content);//添付ファイルを保存
$fc++;
}
}
echo "Insertデータ↓\n";
//var_dump($setArr);
}
文字化け、受信時間が日本時間に統一されていない、ToやCcアドレスは複数あるなど色々対応し、無事完了!