apeescape2.com
  • メイン
  • Kpiと分析
  • プロセスとツール
  • データサイエンスとデータベース
  • エンジニアリング管理
バックエンド

PHPを使用したIMAP電子メールクライアントの構築

開発者は、電子メールメールボックスへのアクセスを必要とするタスクに遭遇することがあります。ほとんどの場合、これは インターネットメッセージアクセスプロトコル、またはIMAP 。として PHP開発者 、私は最初にPHPに目を向けました 組み込みのIMAPライブラリ 、ただし、このライブラリにはバグがあり、デバッグや変更は不可能です。また、プロトコルの機能を最大限に活用するためにIMAPコマンドをカスタマイズすることもできません。

プットオプションの価値

そこで本日は、PHPを使用してゼロから機能するIMAP電子メールクライアントを作成します。使い方も見ていきます Gmailの特別なコマンド 。

IMAPをカスタムクラスimap_driverに実装します。クラスを構築する際の各ステップについて説明します。全体をダウンロードできますimap_driver.php記事の終わりに。



接続の確立

IMAPは接続ベースのプロトコルであり、通常はTCP / IPを介して動作します。 SSLセキュリティ したがって、IMAP呼び出しを行う前に、接続を開く必要があります。

接続するIMAPサーバーのURLとポート番号を知る必要があります。この情報は通常、サービスのWebサイトまたはドキュメントで宣伝されます。例えば、 Gmailの場合 、URLはssl://imap.gmail.comですポート993。

初期化が成功したかどうかを知りたいので、クラスコンストラクターを空のままにし、すべての接続はカスタムinit()で行われます。 falseを返すメソッド接続を確立できない場合:

class imap_driver { private $fp; // file pointer public $error; // error message ... public function init($host, $port) { if (!($this->fp = fsockopen($host, $port, $errno, $errstr, 15))) { $this->error = 'Could not connect to host ($errno) $errstr'; return false; } if (!stream_set_timeout($this->fp, 15)) { $this->error = 'Could not set timeout'; return false; } $line = fgets($this->fp); // discard the first line of the stream return true; } private function close() { fclose($this->fp); } ... }

上記のコードでは、fsockopen()の両方に15秒のタイムアウトを設定しました接続を確立し、データストリーム自体が開いたら要求に応答するため。多くの場合、サーバーが応答しないため、ネットワークへのすべての呼び出しに対してタイムアウトを設定することが重要です。このようなフリーズを処理できる必要があります。

また、ストリームの最初の行を取得して無視します。通常、これはサーバーからの単なる挨拶メッセージ、またはサーバーが接続されていることの確認です。特定のメールサービスのドキュメントをチェックして、これが当てはまることを確認してください。

次に、上記のコードを実行して、init()を確認します。成功しました:

include('imap_driver.php'); // test for init() $imap_driver = new imap_driver(); if ($imap_driver->init('ssl://imap.gmail.com', 993) === false) { echo 'init() failed: ' . $imap_driver->error . ' '; exit; }

基本的なIMAP構文

これで、IMAPサーバーに対してアクティブなソケットが開かれたので、IMAPコマンドの送信を開始できます。 IMAP構文を見てみましょう。

正式なドキュメントは、インターネット技術特別調査委員会(IETF)にあります。 RFC3501 。 IMAPの相互作用は通常、クライアントがコマンドを送信し、サーバーが成功の兆候で応答し、要求された可能性のあるデータで構成されます。

コマンドの基本的な構文は次のとおりです。

line_number command arg1 arg2 ...

行番号または「タグ」は、コマンドの一意の識別子であり、サーバーが複数のコマンドを一度に処理する場合に、サーバーが応答しているコマンドを示すために使用します。

これは、LOGINを示す例です。コマンド:

00000001 LOGIN [email protected] password

サーバーの応答は、「タグなし」のデータ応答で始まる場合があります。たとえば、Gmailはログインの成功に、サーバーの機能とオプションに関する情報を含むタグなしの応答で応答し、メールメッセージをフェッチするコマンドは、メッセージ本文を含むタグなしの応答を受信します。いずれの場合も、応答は常に「タグ付き」コマンド完了応答行で終了し、応答が適用されるコマンドの行番号、完了ステータスインジケーター、およびコマンドに関する追加のメタデータ(存在する場合)を識別します。

line_number status metadata1 metadata2 ...

GmailがLOGINにどのように応答するかを次に示します。コマンド:

  • 成功:
* CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA ID XLIST CHILDREN X-GM-EXT-1 UIDPLUS COMPRESS=DEFLATE ENABLE MOVE CONDSTORE ESEARCH UTF8=ACCEPT LIST-EXTENDED LIST-STATUS 00000001 OK [email protected] authenticated (Success)
  • 失敗:
00000001 NO [AUTHENTICATIONFAILED] Invalid credentials (Failure)

ステータスは、成功を示すOK、失敗を示すNO、または無効なコマンドまたは不正な構文を示すBADのいずれかになります。

基本コマンドの実装:

コマンドをIMAPサーバーに送信し、応答とエンドラインを取得する関数を作成してみましょう。

class imap_driver { private $command_counter = '00000001'; public $last_response = array(); public $last_endline = ''; private function command($command) { $this->last_response = array(); $this->last_endline = ''; fwrite($this->fp, '$this->command_counter $command '); // send the command while ($line = fgets($this->fp)) { // fetch the response one line at a time $line = trim($line); // trim the response $line_arr = preg_split('/s+/', $line, 0, PREG_SPLIT_NO_EMPTY); // split the response into non-empty pieces by whitespace if (count($line_arr) > 0) { $code = array_shift($line_arr); // take the first segment from the response, which will be the line number if (strtoupper($code) == $this->command_counter) { $this->last_endline = join(' ', $line_arr); // save the completion response line to parse later break; } else { $this->last_response[] = $line; // append the current line to the saved response } } else { $this->last_response[] = $line; } } $this->increment_counter(); } private function increment_counter() { $this->command_counter = sprintf('%08d', intval($this->command_counter) + 1); } ... }

LOGINコマンド

これで、command()を呼び出す特定のコマンドの関数を記述できます。ボンネットの下で機能します。 LOGINの関数を書いてみましょうコマンド:

class imap_driver { ... public function login($login, $pwd) { $this->command('LOGIN $login $pwd'); if (preg_match('~^OK~', $this->last_endline)) { return true; } else { $this->error = join(', ', $this->last_response); $this->close(); return false; } } ... }

これで、このようにテストできます。 (テストするには、アクティブな電子メールアカウントが必要であることに注意してください。)

... // test for login() if ($imap_driver->login(' [email protected] ', 'password') === false) { echo 'login() failed: ' . $imap_driver->error . ' '; exit; }

Gmailはデフォルトでセキュリティに関して非常に厳格であることに注意してください。デフォルト設定があり、アカウントプロファイルの国以外の国からアクセスしようとすると、IMAPでメールアカウントにアクセスできなくなります。しかし、修正するのは簡単です。説明されているように、Gmailアカウントで安全性の低い設定を設定するだけです ここに 。

SELECTコマンド

それでは、メールで何か役立つことをするためにIMAPフォルダを選択する方法を見てみましょう。 LOGINのおかげで、構文はcommand()の構文と似ています。方法。 SELECTを使用します代わりにコマンドを実行し、フォルダを指定します。

class imap_driver { ... public function select_folder($folder) { $this->command('SELECT $folder'); if (preg_match('~^OK~', $this->last_endline)) { return true; } else { $this->error = join(', ', $this->last_response); $this->close(); return false; } } ... }

それをテストするために、受信ボックスを選択してみましょう。

... // test for select_folder() if ($imap_driver->select_folder('INBOX') === false) { echo 'select_folder() failed: ' . $imap_driver->error . ' '; return false; }

高度なコマンドの実装

IMAPのより高度なコマンドのいくつかを実装する方法を見てみましょう。

SEARCHコマンド

電子メール分析の一般的なルーチンは、特定の日付範囲の電子メールを検索したり、フラグが立てられた電子メールを検索したりすることです。検索条件をSEARCHに渡す必要がありますコマンドを引数として、スペースを区切り文字として使用します。たとえば、2015年11月20日以降にすべてのメールを取得する場合は、次のコマンドを渡す必要があります。

データの視覚化に最適なツール
00000005 SEARCH SINCE 20-Nov-2015

そして、応答は次のようになります。

* SEARCH 881 882 00000005 OK SEARCH completed

可能な検索用語の詳細なドキュメントを見つけることができます ここに SEARCHの出力コマンドは、空白で区切られた電子メールのUIDのリストです。 UIDは、ユーザーのアカウント内のメールの一意の識別子であり、時系列で表示されます。1は最も古いメールです。 SEARCHを実装するにはコマンドは、結果のUIDを返す必要があります。

class imap_driver { ... public function get_uids_by_search($criteria) { $this->command('SEARCH $criteria'); if (preg_match('~^OK~', $this->last_endline) && is_array($this->last_response) && count($this->last_response) == 1) { $splitted_response = explode(' ', $this->last_response[0]); $uids = array(); foreach ($splitted_response as $item) { if (preg_match('~^d+$~', $item)) { $uids[] = $item; // put the returned UIDs into an array } } return $uids; } else { $this->error = join(', ', $this->last_response); $this->close(); return false; } } ... }

このコマンドをテストするために、過去3日間のメールを受け取ります。

... // test for get_uids_by_search() $ids = $imap_driver->get_uids_by_search('SINCE ' . date('j-M-Y', time() - 60 * 60 * 24 * 3)); if ($ids === false) { echo 'get_uids_failed: ' . $imap_driver->error . ' '; exit; }

FETCH BODY.PEEKを使用したコマンド

もう1つの一般的なタスクは、電子メールをSEENとしてマークせずに電子メールヘッダーを取得することです。 IMAPマニュアルから、 電子メールの全部または一部を取得するためのコマンド FETCHです。最初の引数は、関心のある部分を示し、通常はBODYです。が渡されます。これにより、メッセージ全体とそのヘッダーが返され、SEENとしてマークされます。代替引数BODY.PEEKメッセージをSEENとしてマークせずに、同じことを行います。

IMAP構文では、フェッチする電子メールのセクション(この例では[HEADER])も角括弧内に指定する必要があります。その結果、コマンドは次のようになります。

00000006 FETCH 2 BODY.PEEK[HEADER]

そして、次のような応答が期待されます。

* 2 FETCH (BODY[HEADER] {438} MIME-Version: 1.0 x-no-auto-attachment: 1 Received: by 10.170.97.214; Fri, 30 May 2014 09:13:45 -0700 (PDT) Date: Fri, 30 May 2014 09:13:45 -0700 Message-ID: < [email protected] om> Subject: The best of Gmail, wherever you are From: Gmail Team < [email protected] > To: Example Test < [email protected] > Content-Type: multipart/alternative; boundary=001a1139e3966e26ed04faa054f4 ) 00000006 OK Success

ヘッダーをフェッチするための関数を作成するには、ハッシュ構造(キーと値のペア)で応答を返すことができる必要があります。

class imap_driver { ... public function get_headers_from_uid($uid) { $this->command('FETCH $uid BODY.PEEK[HEADER]'); if (preg_match('~^OK~', $this->last_endline)) { array_shift($this->last_response); // skip the first line $headers = array(); $prev_match = ''; foreach ($this->last_response as $item) { if (preg_match('~^([a-z][a-z0-9-_]+):~is', $item, $match)) { $header_name = strtolower($match[1]); $prev_match = $header_name; $headers[$header_name] = trim(substr($item, strlen($header_name) + 1)); } else { $headers[$prev_match] .= ' ' . $item; } } return $headers; } else { $this->error = join(', ', $this->last_response); $this->close(); return false; } } ... }

そして、このコードをテストするために、関心のあるメッセージのUIDを指定するだけです。

... // test for get_headers_by_uid if (($headers = $imap_driver->get_headers_from_uid(2)) === false) { echo 'get_headers_by_uid() failed: ' . $imap_driver->error . ' '; return false; }

GmailIMAP拡張機能

Gmailには、私たちの生活をはるかに楽にする特別なコマンドのリストが用意されています。 GmailのIMAP拡張コマンドのリストが利用可能です ここに 。私の意見では、最も重要なコマンドを確認しましょう。 X-GM-RAW 。これにより、IMAPでGmail検索構文を使用できるようになります。たとえば、プライマリ、ソーシャル、プロモーション、更新、またはフォーラムのカテゴリにある電子メールを検索できます。

機能的には、X-GM-RAW SEARCHの拡張ですコマンドを使用して、上記のコードをSEARCHに再利用できます。コマンド。キーワードX-GM-RAWを追加するだけです。および基準:

... // test for gmail extended search functionality $ids = $imap_driver->get_uids_by_search(' X-GM-RAW 'category:primary''); if ($ids === false) { echo 'get_uids_failed: ' . $imap_driver->error . ' '; return false; }

上記のコードは、「プライマリ」カテゴリにリストされているすべてのUIDを返します。

注:2015年12月の時点で、Gmailは一部のアカウントで「プライマリ」カテゴリと「更新」カテゴリを混同することがよくあります。これはGmailのバグで、まだ修正されていません。

結論

メールが届いています。それで? PHPでカスタムIMAP電子メールクライアントを構築する方法を読み、条件に応じてメールを確認してください。 つぶやき

全体として、カスタムソケットアプローチは開発者により多くの自由を提供します。ですべてのコマンドを実装することが可能になります IMAP RFC3501 。また、「舞台裏」で何が起こっているのか不思議に思う必要がないため、コードをより適切に制御できます。

完全なimap_driverこの記事で実装したクラスは ここに 。そのまま使用でき、開発者が新しい関数を記述したり、IMAPサーバーにリクエストしたりするのに数分しかかかりません。また、詳細な出力のために、クラスにデバッグ機能を含めました。

スマートソフトウェアの価格戦略の究極のガイド

プロセスとツール

スマートソフトウェアの価格戦略の究極のガイド
jQueryでのFusionChartsのドリルダウンに関するチュートリアル

jQueryでのFusionChartsのドリルダウンに関するチュートリアル

データサイエンスとデータベース

人気の投稿
バイオテクノロジー評価の特異性とベストプラクティス
バイオテクノロジー評価の特異性とベストプラクティス
リモートUXワークショップを完成させて活用する方法
リモートUXワークショップを完成させて活用する方法
台湾の開発者HsiaoWeiChenが第7回ApeeScape奨学金を獲得
台湾の開発者HsiaoWeiChenが第7回ApeeScape奨学金を獲得
究極のUXフック– UXにおける予測的、説得力のある、感情的なデザイン
究極のUXフック– UXにおける予測的、説得力のある、感情的なデザイン
iOSアニメーションと効率のためのチューニング
iOSアニメーションと効率のためのチューニング
 
チームの未来:混合労働力の管理
チームの未来:混合労働力の管理
ガイド:小規模チーム向けのソフトウェアリリース管理
ガイド:小規模チーム向けのソフトウェアリリース管理
外国為替リスク管理ガイド
外国為替リスク管理ガイド
スロベニアの開発者AnaSusticが2回目のApeeScape奨学金を獲得
スロベニアの開発者AnaSusticが2回目のApeeScape奨学金を獲得
事業継続計画の作成
事業継続計画の作成
人気の投稿
  • セレンのページオブジェクトモデル
  • 需要の価格弾力性が2を測定する場合、これは消費者が
  • ccorpとscorpとllcの違い
  • 独自のerc20トークンを作成する
  • adobexdは何をしますか
  • スタートアップのための資金調達方法
カテゴリー
Webフロントエンド モバイルデザイン 分散チーム プロセスとツール 技術 Uxデザイン ヒントとツール データサイエンスとデータベース 革新 エンジニアリング管理

© 2021 | 全著作権所有

apeescape2.com