[PHP] PHP调用IMAP协议读取邮件类库
生活随笔
收集整理的這篇文章主要介紹了
[PHP] PHP调用IMAP协议读取邮件类库
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
socket.php 為連接socket的類庫
imap.php 基于socket的imap協議封裝
test.php 進行測試
require_once 'socket.php'; require_once 'imap.php'; $imap=new Sina_Mail_Net_Imap("imap.sina.net:143",30,30); $imap->capability(); $imap->id(array('name' => 'SinaMail OtherMail Client','version' => '1','os' => 'SinaMail OtherMail','os-version' => '1.0', )); $imap->login("xxxx@xxxxx","xxxx"); $folders=$imap->getList('', '*'); var_dump($folders); $status = $imap->select('SENT'); var_dump($status); $ls = $imap->fetch(array(), array('uid', 'internaldate', 'rfc822.size'));foreach($ls as $k=>$i){$info=$imap->fetch(array($k), array('rfc822')); }?
imap.php
<?php class Sina_Mail_Net_Imap {const MAX_READ_SIZE = 100000000;const PATTERN_REQUEST_STRING_SEQUENCE = '/{\d+}/';const PATTERN_RESPONSE_STRING_SEQUENCE = '/{(\d+)}$/';const SEQUENCE_PARAM_NAME = '[]';const PARTIAL_PARAM_NAME = '<>';const PARAM_NO = 1;const PARAM_SINGLE = 2;const PARAM_PAIR = 4;const PARAM_LIST = 8;const PARAM_STRING = 16;const PARAM_NUMBER = 32;const PARAM_DATE = 64;const PARAM_FLAG = 128;const PARAM_SEQUENCE = 256;const PARAM_SEARCH = 512;const PARAM_BODY = 1024;const PARAM_PARTIAL = 2048;const PARAM_EXCLUSIVE = 4096;private static $statusKeywords = array('MESSAGES' => self::PARAM_NO,'RECENT' => self::PARAM_NO,'UIDNEXT' => self::PARAM_NO,'UIDVALIDITY' => self::PARAM_NO,'UNSEEN' => self::PARAM_NO,);private static $searchKeywords = array('ALL' => self::PARAM_NO,'ANSWERED' => self::PARAM_NO,'BCC' => 18, // self::PARAM_SINGLE | self::PARAM_STRING,'BEFORE' => 66, // self::PARAM_SINGLE | self::PARAM_DATE,'BODY' => 18, // self::PARAM_SINGLE | self::PARAM_STRING,'CC' => 18, // self::PARAM_SINGLE | self::PARAM_STRING,'DELETED' => self::PARAM_NO,'DRAFT' => self::PARAM_NO,'FLAGGED' => self::PARAM_NO,'FROM' => 18, // self::PARAM_SINGLE | self::PARAM_STRING,'HEADER' => 20, // self::PARAM_PAIR | self::PARAM_STRING,'KEYWORD' => 130, // self::PARAM_SINGLE | self::PARAM_FLAG,'LARGER' => 34, // self::PARAM_SINGLE | self::PARAM_NUMBER,'NEW' => self::PARAM_NO,'NOT' => 514, // self::PARAM_SINGLE | self::PARAM_SEARCH,'OLD' => self::PARAM_NO,'ON' => 66, // self::PARAM_SINGLE | self::PARAM_DATE,'OR' => 516, // self::PARAM_PAIR | self::PARAM_SEARCH,'RECENT' => self::PARAM_NO,'SEEN' => self::PARAM_NO,'SENTBEFORE' => 66, // self::PARAM_SINGLE | self::PARAM_DATE,'SENTON' => 66, // self::PARAM_SINGLE | self::PARAM_DATE,'SENTSINCE' => 66, // self::PARAM_SINGLE | self::PARAM_DATE,'SINCE' => 66, // self::PARAM_SINGLE | self::PARAM_DATE,'SMALLER' => 34, // self::PARAM_SINGLE | self::PARAM_NUMBER,'SUBJECT' => 18, // self::PARAM_SINGLE | self::PARAM_STRING,'TEXT' => 18, // self::PARAM_SINGLE | self::PARAM_STRING,'TO' => 18, // self::PARAM_SINGLE | self::PARAM_STRING,'UID' => 258, // self::PARAM_SINGLE | self::PARAM_SEQUENCE,'UNANSWERED' => self::PARAM_NO,'UNDELETED' => self::PARAM_NO,'UNDRAFT' => self::PARAM_NO,'UNFLAGGED' => self::PARAM_NO,'UNKEYWORD' => 130, // self::PARAM_SINGLE | self::PARAM_FLAG,'UNSEEN' => self::PARAM_NO,);private static $fetchKeywords = array('ALL' => 4097, // self::PARAM_NO | self::PARAM_EXCLUSIVE,'FAST' => 4097, // self::PARAM_NO | self::PARAM_EXCLUSIVE,'FULL' => 4097, // self::PARAM_NO | self::PARAM_EXCLUSIVE,'BODY' => 3075, // self::PARAM_NO | self::PARAM_SINGLE | self::PARAM_BODY | self::PARAM_PARTIAL,'BODY.PEEK' => 3074, // self::PARAM_SINGLE | self::PARAM_BODY | self::PARAM_PARTIAL,'BODYSTRUCTURE' => self::PARAM_NO,'ENVELOPE' => self::PARAM_NO,'FLAGS' => self::PARAM_NO,'INTERNALDATE' => self::PARAM_NO,'RFC822' => self::PARAM_NO,'RFC822.HEADER' => self::PARAM_NO,'RFC822.SIZE' => self::PARAM_NO,'RFC822.TEXT' => self::PARAM_NO,'UID' => self::PARAM_NO,);private $sock = null;private $timeout = 120;private $ts = 0;private $tagName = 'A';private $tagId = 0;private $capabilities = array();private $folders = array();private $currentFolder = null;private $currentCommand = null;private $lastSend = '';private $lastRecv = '';public function __construct($uri, $timeout = null, $connTimeout = null) {$this->sock = new Socket($uri, $timeout); // $t = intval($timeout); // if ($t > 0) { // $this->timeout = $t; // }$this->connect($connTimeout); }public function __destruct() {}public function connect($timeout) { $this->sock->connect($timeout); $this->getResponse();}public function capability() {$res = $this->request('capability');if (isset($res[0][0]) && $res[0][0] == '*' &&isset($res[0][1]) && strcasecmp($res[0][1], 'capability') == 0) {for ($i = 2, $n = count($res[0]); $i < $n; ++$i) {$this->capabilities[strtoupper($res[0][$i])] = true;}} }public function id($data) {if (isset($this->capabilities['ID'])) { $this->request('id', array($data)); } }public function login($username, $password) {try {$this->request('login', array($username, $password));} catch (Exception $ex) {throw new Exception($ex->getMessage(), $ex->getCode());}}public function logout() {$this->request('logout'); }public function getList($reference = '', $wildcard = '') {$res = $this->request('list', array($reference, $wildcard));foreach ($res as &$r) {if (isset($r[0]) && $r[0] == '*' && isset($r[1]) && strcasecmp($r[1], 'list') == 0 && isset($r[4])) {$this->folders[$r[4]] = array('id' => $r[4],'name' => mb_convert_encoding($r[4], 'UTF-8', 'UTF7-IMAP'),'path' => $r[3],'attr' => $r[2],);}}return $this->folders;}public function status($folder, $data) {$args = $this->formatArgsForCommand($data, self::$statusKeywords);$res = $this->request('status', array($folder, $args));$status = array();if (!empty($res)) { foreach ($res as &$r) {if (isset($r[0]) && $r[0] == '*' && isset($r[1]) && strcasecmp($r[1], 'status') == 0 && isset($r[3]) && is_array($r[3])) {for ($i = 0, $n = count($r[3]); $i < $n; $i += 2) {$status[$r[3][$i]] = $r[3][$i + 1];}}}}return $status;}public function select($folder) {$res = $this->request('select', array($folder));$status = array();if (!empty($res)) {foreach ($res as $r) {if (isset($r[0]) && $r[0] == '*') {if (isset($r[1]) && isset($r[2])) {if (strcasecmp($r[1], 'ok') == 0 && is_array($r[2])) {for ($i = 0, $n = count($i); $i < $n; $i += 2) {$status[$r[2][$i]] = $r[2][$i + 1];}} elseif (ctype_digit($r[1])) {$status[$r[2]] = $r[1];} else {$status[$r[1]] = $r[2];} }}}}$this->currentFolder = $folder;return $status;}public function search($data) { $args = $this->formatArgsForCommand($data, self::$searchKeywords, true); $res = $this->request('search', $args);$ls = array();foreach ($res as &$r) {if (isset($r[0]) && $r[0] == '*' && isset($r[1]) && strcasecmp($r[1], 'search') == 0) {for ($i = 2, $n = count($r); $i < $n; ++$i) {$ls[] = $r[$i];}}}return $ls;}public function fetch($seq, $data) { $seqStr = $this->formatSequence($seq); $args = $this->formatArgsForCommand($data, self::$fetchKeywords); $res = $this->request('fetch', array($seqStr, $args)); // var_dump($res);$ls = array();foreach ($res as &$r) {if (isset($r[0]) && $r[0] == '*' &&isset($r[1]) && is_numeric($r[1]) && isset($r[2]) && strcasecmp($r[2], 'fetch') == 0 &&isset($r[3]) && is_array($r[3])) {$a = array();for ($i = 0, $n = count($r[3]); $i < $n; $i += 2) {$key = $r[3][$i];if (((strcasecmp($key, 'BODY') == 0 && isset($args['BODY']) && is_array($args['BODY'])) || (strcasecmp($key, 'BODY.PEEK') == 0 && isset($args['BODY.PEEK']) && is_array($args['BODY.PEEK']))) && is_array($r[3][$i + 1])) {$key = trim($this->formatRequestArray(array($key => $r[3][$i + 1]), $placeHolder, 0), '()');$i++;} else {$key = $r[3][$i];}$a[$key] = $r[3][$i + 1];}if (!empty($a)) {$ls[$r[1]] = $a;}}}return $ls;}private function nextTag() {$this->tagId++;return sprintf('%s%d', $this->tagName, $this->tagId);}private function request($cmd, $args = array()) { $this->currentCommand = strtoupper(trim($cmd));$tag = $this->nextTag();$req = $tag . ' ' . $this->currentCommand;// 格式化參數列表$strSeqList = array();if (is_array($args)) {$argStr = $this->formatRequestArray($args, $strSeqList); } else { $argStr = $this->formatRequestString($args, $strSeqList);}//$argStr = $this->makeRequest($args, $strSeqList);$subReqs = array();if (isset($argStr[0])) {$req .= ' ' . $argStr;// 如果參數中包括需要序列化的數據,根據序列化標識{length}將命令拆分成多條if (!empty($strSeqList) && preg_match_all(self::PATTERN_REQUEST_STRING_SEQUENCE, $req, $matches, PREG_OFFSET_CAPTURE)) {$p = 0;foreach ($matches[0] as $m) {$e = $m[1] + strlen($m[0]);$subReqs[] = substr($req, $p, $e - $p); $p = $e;}$subReqs[] = substr($req, $p);// 校驗序列化標識與需要序列化的參數列表數量是否一致if (count($subReqs) != count($strSeqList) + 1) {$subReqs = null;}}}if (empty($subReqs)) {// 處理單條命令$this->sock->writeLine($req);$this->lastSend = $req; $res = $this->getResponse($tag);} else { // 處理多條命令$this->lastSend = '';foreach ($subReqs as $id => $req) {$this->sock->writeLine($req); $this->lastSend .= $req;$res = $this->getResponse($tag); if (isset($res[0][0]) && $res[0][0] == '+') {$this->sock->write($strSeqList[$id]); $this->lastSend .= "\r\n" . $strSeqList[$id];} else {// 如果服務器端返回其他相應,則定制后續執行break;}} }return $res;}private function formatRequestString($s, &$strSeqList) {$s = trim($s);$needQuote = false;if (!isset($s[0])) {$needQuote = true;} elseif ($this->currentCommand == 'ID') { $needQuote = true;} else {// 參數包含多行時,需要進行序列化if (strpos($s, "\r") !== false || strpos($s, "\n") !== false) { $strSeqList[] = $s;$s = sprintf('{%d}', strlen($s));} else {// 參數包含雙引號或空格時,需要將使用雙引號括起來if (strpos($s, '"') !== false) {$s = addcslashes($s, '"');$needQuote = true;}if (strpos($s, ' ') !== false) {$needQuote = true;}}}if ($needQuote) {return sprintf('"%s"', $s);} else {return $s;}}private function formatRequestArray($arr, &$strSeqList, $level = -1) {$a = array();foreach ($arr as $k => $v) {$isBody = false;$supportPartial = false;$partialStr = '';if ($this->currentCommand == 'FETCH') {// 識別是否body命令,是否可以包含<partial>$kw = strtoupper($k); if (isset(self::$fetchKeywords[$kw]) && (self::$fetchKeywords[$kw] & self::PARAM_BODY) > 0) {$isBody = true;}if (isset(self::$fetchKeywords[$kw]) && (self::$fetchKeywords[$kw] & self::PARAM_PARTIAL) > 0) {$supportPartial = true;}} if (is_array($v)) { if ($supportPartial && isset($v[self::PARTIAL_PARAM_NAME]) && is_array($v[self::PARTIAL_PARAM_NAME])) { // 處理包含<partial>的命令foreach ($v[self::PARTIAL_PARAM_NAME] as $spos => $mlen) {$partialStr = sprintf('<%d.%d>', $spos, $mlen);} unset($v[self::PARTIAL_PARAM_NAME]);}$s = $this->formatRequestArray($v, $strSeqList, $level + 1);} else {$s = $this->formatRequestString($v, $strSeqList);} if (!is_numeric($k)) {// 字典方式需要包含鍵名$k = $this->formatRequestString($k, $strSeqList);if ($isBody) {$s = $k . $s; } else {$s = $k . ' ' . $s;}// 包含<partial>if ($supportPartial) {$s .= $partialStr;}}$a[] = $s;}if ($level < 0) {return implode(' ', $a);} elseif (($level % 2) == 0) {return sprintf('(%s)', implode(' ', $a));} else {return sprintf('[%s]', implode(' ', $a));}}private function formatSequence($seq) {$n = count($seq);if ($n == 0) {return '1:*'; } elseif ($n == 1) {if (isset($seq[0])) {return strval($seq[0]);} else {foreach ($seq as $k => $v) {return $k . ':' . $v;}}} else {return implode(',', $seq);}}private function formatArgsForCommand(&$data, &$fields, $asList = false) {$args = array();foreach ($data as $k => $v) { if (is_numeric($k)) {// 無值參數$name = strtoupper($v);if (isset($fields[$name])) {// 對于排他性屬性,直接返回if (($fields[$name] & self::PARAM_EXCLUSIVE) > 0) {return $name;} elseif (($fields[$name] & self::PARAM_NO) > 0) { $args[] = $name;}}} elseif ($k == self::SEQUENCE_PARAM_NAME) {// 序列$args[] = $this->formatSequence($v); } else {$name = strtoupper($k);if (isset($fields[$name])) {$paramType = $fields[$name]; // 格式化參數類型if (($paramType & self::PARAM_DATE) > 0) {$v = date('j-M-Y', $v);} elseif (($paramType & self::PARAM_SEQUENCE) > 0) {$v = $this->formatSequence($v);} // 根據參數定義拼組參數列表if (($paramType & self::PARAM_SINGLE) > 0) {// 單值參數if ($asList) {$args[] = $name;$args[] = $v;} else {$args[$name] = $v;}} elseif (($paramType & self::PARAM_PAIR) > 0) {// 鍵值對參數if (is_array($v)) {foreach ($v as $x => $y) {$pk = $x;$pv = $y;break;}} else {$pk = $v;$pv = '';}if ($asList) {$args[] = $name;$args[] = $pk;$args[] = $pv;} else {$args[$name] = array($pk => $pv);}} elseif (($paramType & self::PARAM_LIST) > 0) {// 列表參數if ($asList) {$args[] = $name;foreach ($v as $i) {$args[] = $i;}} else {$args[$name] = $v;}} elseif (($paramType & self::PARAM_NO) > 0) {// 無值參數$args[] = $name;}}}}return $args;}private function getResponse($tag = null) {$r = array();$readMore = true; while ($readMore) {$ln = trim($this->sock->readLine()); if (!isset($ln[0])) {// connection closed or read empty string, throw exception to avoid dead loop and reconnectthrow new Exception('read response failed');}$matches = null;$strSeqKey = null;$strSeq = null;if (preg_match(self::PATTERN_RESPONSE_STRING_SEQUENCE, $ln, $matches)) {$strSeqKey = $matches[0];$this->readSequence($ln, $strSeq, $matches[1]);}$this->lastRecv = $ln;// 區分處理不同種響應switch ($ln[0]) {case '*':$r[] = $this->parseLine($ln, $strSeqKey, $strSeq); if (!$tag) {$readMore = false;}break;case $this->tagName:$r[] = $this->parseLine($ln);if ($tag) {$readMore = false;} else {}break;case '+':$r[] = $this->parseLine($ln);$readMore = false;break;default:$r[] = $ln;break;} } //var_dump($this->lastSend, $this->lastRecv);// 無響應數據if (empty($r)) {throw new Exception('no response');}$last = $r[count($r) - 1];if (isset($last[0]) && $last[0] == '+') {// 繼續發送請求數據} else {if ($tag) {if (!isset($last[0]) || strcasecmp($last[0], $tag) != 0) { throw new Exception('tag no match');} } else {if (!isset($last[0]) || strcasecmp($last[0], '*') != 0) { throw new Exception('untag no match');}}if (isset($last[1])) {// 處理響應出錯的情況if (strcasecmp($last[1], 'bad') == 0) { throw new Exception(implode(' ', $last));} elseif (strcasecmp($last[1], 'no') == 0) {throw new Exception(implode(' ', $last));}}//$this->currentCommand = null; }return $r;}private function readSequence(&$ln, &$strSeq, $seqLength) {// 對于字符串序列,讀取完整內容后再拼接響應 $readLen = 0;$st = microtime(true);// 網絡請求多次讀取字符串序列內容,直到讀好為止while ($readLen < $seqLength) {$sb = $this->sock->read($seqLength - $readLen);if (isset($sb[0])) {$strSeq .= $sb;$readLen = strlen($strSeq);} if ((microtime(true) - $st) > $this->timeout) {throw new Exception('read sequence timeout');}}// 讀取字符串序列后的剩余命令$leftLn = rtrim($this->sock->readLine());$ln = $ln . $leftLn;}private function parseLine($ln, $strSeqKey = null, $strSeq = null) {$r = array();$p =& $r;$stack = array();$token = '';$escape = false;$inQuote = false;for ($i = 0, $n = strlen($ln); $i < $n; ++$i) {$ch = $ln[$i];if ($ch == '"') {// 處理雙引號括起的字符串if (!$inQuote) {$inQuote = true;} else {$inQuote = false;}} elseif ($inQuote) {// 對于括起的字符串,處理雙引號轉義if ($ch == '\\') {if (!$escape && isset($ln[$i + 1]) && $ln[$i + 1] == '"') { $token .= '"';$i++;} else {$token .= $ch;$escape = !$escape;} } else {$token .= $ch;}} elseif ($ch == ' ' || $ch == '(' || $ch == ')' || $ch == '[' || $ch == ']') {// 處理子列表if (isset($token[0])) {// 將字符串序列標識:{length},替換為真實字符串if ($strSeqKey && $token == $strSeqKey) {$p[] = $strSeq;} else {$p[] = $token;}$token = '';} if ($ch == '(' || $ch == '[') { $p[] = array();$stack[] =& $p;$p =& $p[count($p) - 1]; } elseif ($ch == ')' || $ch == ']') { $p =& $stack[count($stack) - 1];array_pop($stack);}} else { // 處理字符串字面量$token .= $ch;} }if (isset($token[0])) {// 將字符串序列標識:{length},替換為真實字符串if ($strSeqKey && $token == $strSeqKey) {$p[] = $strSeq;} else {$p[] = $token;}}return $r;} }// end of php?
socket.php
<?php class Socket{const DEFAULT_READ_SIZE = 8192;const CRTL = "\r\n";private $uri = null;private $timeout = null;private $sock = null;private $connected = false;public function __construct($uri, $timeout = null){$this->uri = $uri;$this->timeout = $this->formatTimeout($timeout);}public function connect($timeout = null, $retryTimes = null){if ($this->connected) {$this->close();}$connTimeout = $this->formatTimeout($timeout, $this->timeout);$retryTimes = intval($retryTimes);if ($retryTimes < 1) {$retryTimes = 1;}for ($i = 0; $i < $retryTimes; ++$i) {$this->sock = stream_socket_client($this->uri, $errno, $error, $connTimeout);if ($this->sock) {break;}}if (!$this->sock) {}stream_set_timeout($this->sock, $this->timeout);$this->connected = true;}public function read($size){assert($this->connected);$buf = fread($this->sock, $size);if ($buf === false) {$this->handleReadError();}return $buf;}public function readLine(){assert($this->connected);$buf = '';while (true) {$s = fgets($this->sock, self::DEFAULT_READ_SIZE);if ($s === false) {$this->checkReadTimeout();break;}$n = strlen($s);if (!$n) {break;}$buf .= $s;if ($s[$n - 1] == "\n") {break;}}return $buf;}public function readAll(){assert($this->connected);$buf = '';while (true) {$s = fread($this->sock, self::DEFAULT_READ_SIZE);if ($s === false) {$this->handleReadError();}if (!isset($s[0])) {break;}$buf .= $s;}return $buf;}public function write($s){assert($this->connected);$n = strlen($s);$w = 0;while ($w < $n) {$buf = substr($s, $w, self::DEFAULT_READ_SIZE);$r = fwrite($this->sock, $buf);if (!$r) {$this->close();}$w += $r;}}public function writeLine($s){$this->write($s . self::CRTL);}public function close() {if ($this->connected) {fclose($this->sock);$this->connected = false;}}private function formatTimeout($timeout, $default = null){$t = intval($timeout);if ($t <= 0) {if (!$default) {$t = ini_get('default_socket_timeout');} else {$t = $default;}}return $t;}private function checkReadTimeout(){$meta = stream_get_meta_data($this->sock);if (isset($meta['timed_out'])) {$this->close();}}private function handleReadError(){$this->checkReadTimeout();$this->close();} }?
轉載于:https://www.cnblogs.com/taoshihan/p/11508391.html
總結
以上是生活随笔為你收集整理的[PHP] PHP调用IMAP协议读取邮件类库的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: neutron plugin 与 ext
- 下一篇: CodeForces 1204 (#58