正在显示
24 个修改的文件
包含
3225 行增加
和
0 行删除
lib/Imap/Imap.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace Lib\Imap; | ||
4 | + | ||
5 | +use Lib\Imap\Request\Folder; | ||
6 | +use Lib\Imap\Request\Folders; | ||
7 | +use Lib\Imap\Request\Login; | ||
8 | +use Lib\Imap\Request\Noop; | ||
9 | + | ||
10 | +class Imap { | ||
11 | + | ||
12 | + /** | ||
13 | + * 配置 | ||
14 | + * @var ImapConfig | ||
15 | + */ | ||
16 | + public ImapConfig $config; | ||
17 | + | ||
18 | + /** | ||
19 | + * @var ImapClient | ||
20 | + */ | ||
21 | + public ImapClient $client; | ||
22 | + | ||
23 | + /** | ||
24 | + * 当前在那个文件夹下面目录 | ||
25 | + * @var string | ||
26 | + */ | ||
27 | + protected string $checkedFolder = ''; | ||
28 | + | ||
29 | + | ||
30 | + /** | ||
31 | + * Imap constructor. | ||
32 | + * @param ImapConfig $config | ||
33 | + */ | ||
34 | + public function __construct(ImapConfig $config) | ||
35 | + { | ||
36 | + $this->config = $config; | ||
37 | + | ||
38 | + $this->client = new ImapClient($this->config->getHost()); | ||
39 | + } | ||
40 | + | ||
41 | + /** | ||
42 | + * 设置是否调试 | ||
43 | + * @param bool $debug | ||
44 | + * @return $this | ||
45 | + * @author:dc | ||
46 | + * @time 2024/9/19 9:41 | ||
47 | + */ | ||
48 | + public function debug(bool $debug = true):static { | ||
49 | + $this->client->setDebug($debug); | ||
50 | + return $this; | ||
51 | + } | ||
52 | + | ||
53 | + /** | ||
54 | + * @param string $host | ||
55 | + * @param string $email | ||
56 | + * @param string $password | ||
57 | + * @return Login | ||
58 | + * @throws \Exception | ||
59 | + * @author:dc | ||
60 | + * @time 2024/9/13 17:45 | ||
61 | + */ | ||
62 | + public function login():Login { | ||
63 | + // 打开连接 | ||
64 | + $this->client->open(); | ||
65 | + | ||
66 | + //有的服务器是强制要求设置id属性 已知 163 有的服务器没有id命令 这里就不处理结果了 | ||
67 | + $this->client->request('ID ("name" "测试本地 Client" "version" "1" "os" "测试本地" "os-version" "1.0")'); | ||
68 | + | ||
69 | + // 登录 | ||
70 | + return (new Login($this))->exec(); | ||
71 | + | ||
72 | + } | ||
73 | + | ||
74 | + /** | ||
75 | + * 选择文件夹 | ||
76 | + * @param string|\Lib\Imap\Parse\Folder\Folder $folder 目录 | ||
77 | + * @param bool $utf7 是否是utf7编码 | ||
78 | + * @return Folder | ||
79 | + * @author:dc | ||
80 | + * @time 2024/9/19 9:57 | ||
81 | + */ | ||
82 | + public function folder(string|\Lib\Imap\Parse\Folder\Folder $folder, bool $utf7 = true):Folder { | ||
83 | + if($folder instanceof \Lib\Imap\Parse\Folder\Folder){ | ||
84 | + $folder = $folder->folder; | ||
85 | + }else{ | ||
86 | + if(!$utf7) $folder = mb_convert_encoding($folder,"UTF7-IMAP","UTF-8"); | ||
87 | + } | ||
88 | + return new Folder($this,$folder); | ||
89 | + } | ||
90 | + | ||
91 | + /** | ||
92 | + * 读取文件夹 | ||
93 | + * @return \Lib\Imap\Parse\Folder\Folders | ||
94 | + * @author:dc | ||
95 | + * @time 2024/9/19 14:10 | ||
96 | + */ | ||
97 | + public function getFolders():\Lib\Imap\Parse\Folder\Folders { | ||
98 | + $folders = new Folders($this); | ||
99 | + | ||
100 | + return $folders->get(); | ||
101 | + } | ||
102 | + | ||
103 | + | ||
104 | + /** | ||
105 | + * 类似ping | ||
106 | + * @return bool | ||
107 | + * @author:dc | ||
108 | + * @time 2024/9/20 14:45 | ||
109 | + */ | ||
110 | + public function noop():bool { | ||
111 | + return (new Noop($this))->exec()->isOk(); | ||
112 | + } | ||
113 | + | ||
114 | + | ||
115 | + /** | ||
116 | + * 读取或验证是否在当前目录下面 | ||
117 | + * @param string $folder | ||
118 | + * @return bool|string | ||
119 | + * @author:dc | ||
120 | + * @time 2024/9/20 13:58 | ||
121 | + */ | ||
122 | + public function checkedFolder(string $folder = ''):bool|string { | ||
123 | + if($folder){ | ||
124 | + return $folder == $this->checkedFolder; | ||
125 | + } | ||
126 | + return $this->checkedFolder; | ||
127 | + } | ||
128 | + | ||
129 | + /** | ||
130 | + * 设置当前进入那个文件夹 | ||
131 | + * @param string $folder | ||
132 | + * @author:dc | ||
133 | + * @time 2024/9/20 14:00 | ||
134 | + */ | ||
135 | + public function setCheckedFolder(string $folder):void { | ||
136 | + $this->checkedFolder = $folder; | ||
137 | + } | ||
138 | + | ||
139 | + | ||
140 | +} |
lib/Imap/ImapClient.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace Lib\Imap; | ||
4 | + | ||
5 | +class ImapClient { | ||
6 | + | ||
7 | + /** | ||
8 | + * 资源 | ||
9 | + * @var | ||
10 | + */ | ||
11 | + private $socket; | ||
12 | + | ||
13 | + protected bool $debug = false; | ||
14 | + | ||
15 | + protected string $host; | ||
16 | + | ||
17 | + | ||
18 | + /** | ||
19 | + * 每请求一次就会+1 | ||
20 | + * @var int | ||
21 | + */ | ||
22 | + public int $tagNum = 0; | ||
23 | + | ||
24 | + public function __construct(string $host){ | ||
25 | + $this->host = $host; | ||
26 | + } | ||
27 | + | ||
28 | + public function open(){ | ||
29 | + $content = stream_context_create([ | ||
30 | + 'ssl' => [ | ||
31 | + 'verify_peer' => false, // 有的证书和域名不匹配,这里关闭认证 | ||
32 | + 'verify_peer_name' => false,// 有的证书和域名不匹配,这里关闭认证 | ||
33 | + ] | ||
34 | + ]); | ||
35 | + | ||
36 | + $flags = STREAM_CLIENT_CONNECT; | ||
37 | + | ||
38 | + $this->socket = stream_socket_client( | ||
39 | + $this->host, | ||
40 | + $errno, | ||
41 | + $error, | ||
42 | + 30, | ||
43 | + $flags, | ||
44 | + $content | ||
45 | + ); | ||
46 | + | ||
47 | + if($error){ | ||
48 | + throw new \Exception("socket error: {$error}"); | ||
49 | + } | ||
50 | + | ||
51 | + if (!$this->socket) { | ||
52 | + throw new \Exception($this->host." connection fail."); | ||
53 | + } | ||
54 | + } | ||
55 | + | ||
56 | + | ||
57 | + /** | ||
58 | + * @param bool $debug | ||
59 | + * @return $this | ||
60 | + * @author:dc | ||
61 | + * @time 2024/9/18 9:57 | ||
62 | + */ | ||
63 | + public function setDebug(bool $debug = true): static | ||
64 | + { | ||
65 | + $this->debug = $debug; | ||
66 | + return $this; | ||
67 | + } | ||
68 | + | ||
69 | + | ||
70 | + | ||
71 | + /** | ||
72 | + * 写 | ||
73 | + * @param string $cmd | ||
74 | + * @return false|int | ||
75 | + * @author:dc | ||
76 | + * @time 2024/9/13 15:47 | ||
77 | + */ | ||
78 | + protected function write(string $cmd){ | ||
79 | + if($this->debug){ | ||
80 | + echo 'write: '.$cmd; | ||
81 | + } | ||
82 | + return fwrite($this->socket,$cmd); | ||
83 | + } | ||
84 | + | ||
85 | + /** | ||
86 | + * 读 | ||
87 | + * @return false|string | ||
88 | + * @author:dc | ||
89 | + * @time 2024/9/13 15:49 | ||
90 | + */ | ||
91 | + protected function readLine(){ | ||
92 | + $str = fgets($this->socket,2048); | ||
93 | + if($this->debug){ | ||
94 | + echo 'read: '.$str; | ||
95 | + } | ||
96 | + return $str; | ||
97 | + } | ||
98 | + | ||
99 | + /** | ||
100 | + * | ||
101 | + * @param string $cmd 执行的命令 | ||
102 | + * @return array | ||
103 | + * @author:dc | ||
104 | + * @time 2024/9/13 15:57 | ||
105 | + */ | ||
106 | + public function request(string $cmd):array { | ||
107 | + $this->tagNum++; | ||
108 | + $tag = 'TAG'.$this->tagNum; | ||
109 | + // 请求数据 | ||
110 | + $writeNumber = $this->write($tag.' '.$cmd."\r\n"); | ||
111 | + $result = []; | ||
112 | + if($writeNumber){ | ||
113 | + // 读取数据 | ||
114 | + while (1){ | ||
115 | + $line = $this->readLine(); | ||
116 | + $result[] = $line; | ||
117 | + list($token) = explode(' ',$line,2); | ||
118 | + // 结束了 | ||
119 | + if($token == $tag || $line === false){ | ||
120 | + break; | ||
121 | + } | ||
122 | + } | ||
123 | + } | ||
124 | + | ||
125 | + return $result; | ||
126 | + | ||
127 | + } | ||
128 | + | ||
129 | + | ||
130 | + | ||
131 | +} |
lib/Imap/ImapConfig.php
0 → 100644
1 | +<?php | ||
2 | +namespace Lib\Imap; | ||
3 | + | ||
4 | +class ImapConfig { | ||
5 | + | ||
6 | + protected string $host = ''; | ||
7 | + protected string $password = ''; | ||
8 | + protected string $email = ''; | ||
9 | + | ||
10 | + | ||
11 | + /** | ||
12 | + * @param string $email | ||
13 | + */ | ||
14 | + public function setEmail(string $email): static | ||
15 | + { | ||
16 | + $this->email = $email; | ||
17 | + return $this; | ||
18 | + } | ||
19 | + | ||
20 | + | ||
21 | + /** | ||
22 | + * @param string $host | ||
23 | + */ | ||
24 | + public function setHost(string $host): static | ||
25 | + { | ||
26 | + $this->host = $host; | ||
27 | + return $this; | ||
28 | + } | ||
29 | + | ||
30 | + /** | ||
31 | + * @param string $password | ||
32 | + */ | ||
33 | + public function setPassword(string $password): static | ||
34 | + { | ||
35 | + $this->password = $password; | ||
36 | + return $this; | ||
37 | + } | ||
38 | + | ||
39 | + /** | ||
40 | + * @return string | ||
41 | + */ | ||
42 | + public function getEmail(): string | ||
43 | + { | ||
44 | + return $this->email; | ||
45 | + } | ||
46 | + | ||
47 | + /** | ||
48 | + * @return string | ||
49 | + */ | ||
50 | + public function getHost(): string | ||
51 | + { | ||
52 | + return $this->host; | ||
53 | + } | ||
54 | + | ||
55 | + /** | ||
56 | + * @return string | ||
57 | + */ | ||
58 | + public function getPassword(): string | ||
59 | + { | ||
60 | + return $this->password; | ||
61 | + } | ||
62 | + | ||
63 | +} |
lib/Imap/ImapPool.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace Lib\Imap; | ||
4 | + | ||
5 | +class ImapPool { | ||
6 | + | ||
7 | + /** | ||
8 | + * @var Imap[] | ||
9 | + */ | ||
10 | + private static array $pool; | ||
11 | + | ||
12 | + | ||
13 | + /** | ||
14 | + * 获取连接 | ||
15 | + * @param ImapConfig $config | ||
16 | + * @return Imap | ||
17 | + * @author:dc | ||
18 | + * @time 2024/9/14 9:19 | ||
19 | + */ | ||
20 | + public static function get(ImapConfig $config){ | ||
21 | + if(!isset(static::$pool[$config->getEmail()])){ | ||
22 | + static::$pool[$config->getEmail()] = new Imap($config); | ||
23 | + } | ||
24 | + return static::$pool[$config->getEmail()]; | ||
25 | + } | ||
26 | + | ||
27 | + | ||
28 | + public static function release(Imap $imap){ | ||
29 | + | ||
30 | + } | ||
31 | + | ||
32 | + | ||
33 | + | ||
34 | + public function noop(){ | ||
35 | + | ||
36 | + } | ||
37 | + | ||
38 | + | ||
39 | +} |
lib/Imap/ImapSearch.php
0 → 100644
1 | +<?php | ||
2 | +namespace Lib\Imap; | ||
3 | + | ||
4 | +/** | ||
5 | + * 搜索邮箱 | ||
6 | + * TODO:: 有些邮箱服务器并不支持搜索 或者 某些字段搜索 | ||
7 | + * 日期类搜索是没问题的 | ||
8 | + * @author:dc | ||
9 | + * @time 2024/9/14 15:48 | ||
10 | + * Class ImapSearch | ||
11 | + * @package Lib\Imap | ||
12 | + */ | ||
13 | +class ImapSearch { | ||
14 | + | ||
15 | + /** | ||
16 | + * @var array | ||
17 | + */ | ||
18 | + protected array $where = []; | ||
19 | + | ||
20 | + | ||
21 | + | ||
22 | + /** | ||
23 | + * 搜索已标记为 已回复 的邮件 | ||
24 | + * @param bool $a 是否已标记为 已回复 | ||
25 | + * @return $this | ||
26 | + * @author:dc | ||
27 | + * @time 2024/9/14 16:22 | ||
28 | + */ | ||
29 | + public function answered(bool $a = true):static { | ||
30 | + $this->where[$a?'ANSWERED':'UNANSWERED'] = true; | ||
31 | + return $this; | ||
32 | + } | ||
33 | + | ||
34 | + /** | ||
35 | + * 搜索已标记为删除的 | ||
36 | + * Messages with the \Deleted flag set. | ||
37 | + * @param bool $del 是否已删除的 | ||
38 | + * @return $this | ||
39 | + * @author:dc | ||
40 | + * @time 2024/9/14 16:21 | ||
41 | + */ | ||
42 | + public function deleted(bool $del = true):static { | ||
43 | + $this->where[$del?'DELETED':'UNDELETED'] = true; | ||
44 | + return $this; | ||
45 | + } | ||
46 | + | ||
47 | + /** | ||
48 | + * 搜索标记为草稿的 | ||
49 | + * Messages with the \Draft flag set. | ||
50 | + * @param bool $draft 是否是草稿 | ||
51 | + * @return $this | ||
52 | + * @author:dc | ||
53 | + * @time 2024/9/14 16:20 | ||
54 | + */ | ||
55 | + public function draft(bool $draft = true):static { | ||
56 | + $this->where[$draft?'DRAFT':'UNDRAFT'] = true; | ||
57 | + return $this; | ||
58 | + } | ||
59 | + | ||
60 | + /** | ||
61 | + * 搜索标记为星标的邮件 | ||
62 | + * @param bool $flagged 是否带星标 | ||
63 | + * @return $this | ||
64 | + * @author:dc | ||
65 | + * @time 2024/9/14 16:19 | ||
66 | + */ | ||
67 | + public function flagged(bool $flagged = true):static { | ||
68 | + $this->where[$flagged?'FLAGGED':'UNFLAGGED'] = true; | ||
69 | + return $this; | ||
70 | + } | ||
71 | + | ||
72 | + /** | ||
73 | + * 搜索未读 还是已读 | ||
74 | + * @param bool $seen 是否标记为已读 | ||
75 | + * @return $this | ||
76 | + * @author:dc | ||
77 | + * @time 2024/9/14 16:23 | ||
78 | + */ | ||
79 | + public function seen(bool $seen = true):static { | ||
80 | + $this->where[$seen?'SEEN':'UNSEEN'] = true; | ||
81 | + return $this; | ||
82 | + } | ||
83 | + | ||
84 | + | ||
85 | + /** | ||
86 | + * 搜索密送 中有这个邮箱的 | ||
87 | + * @param string $bcc 邮箱 | ||
88 | + * @return $this | ||
89 | + * @author:dc | ||
90 | + * @time 2024/9/14 15:57 | ||
91 | + */ | ||
92 | + public function bcc(string $bcc):static { | ||
93 | + $this->where['BCC'] = $bcc; | ||
94 | + return $this; | ||
95 | + } | ||
96 | + | ||
97 | + /** | ||
98 | + * 搜索抄送 中有这个邮箱的 | ||
99 | + * Messages that contain the specified string in the envelope structure's CC field. | ||
100 | + * @param string $cc 邮箱 | ||
101 | + * @return $this | ||
102 | + * @author:dc | ||
103 | + * @time 2024/9/14 16:06 | ||
104 | + */ | ||
105 | + public function cc(string $cc):static { | ||
106 | + $this->where['CC'] = $cc; | ||
107 | + return $this; | ||
108 | + } | ||
109 | + | ||
110 | + /** | ||
111 | + * 搜索 发件人 | ||
112 | + * @param string $from | ||
113 | + * @return $this | ||
114 | + * @author:dc | ||
115 | + * @time 2024/9/14 16:10 | ||
116 | + */ | ||
117 | + public function from(string $from):static { | ||
118 | + $this->where['FROM'] = $from; | ||
119 | + return $this; | ||
120 | + } | ||
121 | + | ||
122 | + /** | ||
123 | + * 搜索收件人 | ||
124 | + * @param string $to | ||
125 | + * @return $this | ||
126 | + * @author:dc | ||
127 | + * @time 2024/9/14 16:17 | ||
128 | + */ | ||
129 | + public function to(string $to):static { | ||
130 | + $this->where['TO'] = $to; | ||
131 | + return $this; | ||
132 | + } | ||
133 | + | ||
134 | + /** | ||
135 | + * 搜索内容 | ||
136 | + * Messages that contain the specified string in the body of the message. | ||
137 | + * @param string $body | ||
138 | + * @return $this | ||
139 | + * @author:dc | ||
140 | + * @time 2024/9/14 16:05 | ||
141 | + */ | ||
142 | + public function body(string $body):static { | ||
143 | + $this->where['BODY'] = $body; | ||
144 | + return $this; | ||
145 | + } | ||
146 | + | ||
147 | + /** | ||
148 | + * 搜索 日期 小于某个日期的 | ||
149 | + * Messages whose internal date (disregarding time and timezone) is earlier than the specified date. | ||
150 | + * @param string $date 日期 | ||
151 | + * @author:dc | ||
152 | + * @time 2024/9/14 16:03 | ||
153 | + */ | ||
154 | + public function dateLt(string $date):static{ | ||
155 | + $this->where['BEFORE'] = date('d-M-Y',strtotime($date)); | ||
156 | + return $this; | ||
157 | + } | ||
158 | + | ||
159 | + /** | ||
160 | + * 搜索指定日期的邮件 | ||
161 | + * Messages whose internal date (disregarding time and timezone) is within the specified date. | ||
162 | + * @param string $date | ||
163 | + * @author:dc | ||
164 | + * @time 2024/9/14 16:12 | ||
165 | + */ | ||
166 | + public function date(string $date):static{ | ||
167 | + $this->where['ON'] = date('d-M-Y',strtotime($date)); | ||
168 | + return $this; | ||
169 | + } | ||
170 | + | ||
171 | + /** | ||
172 | + * 搜索指定日期后的邮件 如 2024-04-04 显示这个日期过后的 | ||
173 | + * Messages whose internal date (disregarding time and timezone) is within or later than the specified date. | ||
174 | + * @param string $date | ||
175 | + * @author:dc | ||
176 | + * @time 2024/9/14 16:14 | ||
177 | + */ | ||
178 | + public function dateGt(string $date):static{ | ||
179 | + $this->where['SINCE'] = date('d-M-Y',strtotime($date)); | ||
180 | + return $this; | ||
181 | + } | ||
182 | + | ||
183 | + /** | ||
184 | + * 模糊搜索主题 | ||
185 | + * @param string $subject 主题 | ||
186 | + * @author:dc | ||
187 | + * @time 2024/9/14 16:16 | ||
188 | + */ | ||
189 | + public function subject(string $subject):static { | ||
190 | + $this->where['SUBJECT'] = $subject; | ||
191 | + return $this; | ||
192 | + } | ||
193 | + | ||
194 | + | ||
195 | + /** | ||
196 | + * 返回搜索字符串 | ||
197 | + * @author:dc | ||
198 | + * @time 2024/9/14 16:27 | ||
199 | + */ | ||
200 | + public function toString():string { | ||
201 | + $arr = []; | ||
202 | + foreach ($this->where as $k=>$v){ | ||
203 | + if(is_bool($v)){ | ||
204 | + $arr[] = $k; | ||
205 | + }else{ | ||
206 | + $arr[] = $k.' "'.addcslashes($v,'"').'"'; | ||
207 | + } | ||
208 | + } | ||
209 | + return implode(' ',$arr); | ||
210 | + } | ||
211 | + | ||
212 | + | ||
213 | + | ||
214 | +} |
lib/Imap/Parse/Address.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace Lib\Imap\Parse; | ||
4 | + | ||
5 | +/** | ||
6 | + * @author:dc | ||
7 | + * @time 2024/9/11 11:17 | ||
8 | + * Class Address | ||
9 | + * @package Imap\Parse | ||
10 | + */ | ||
11 | +class Address { | ||
12 | + | ||
13 | + public string $name = ''; | ||
14 | + | ||
15 | + public string $email = ''; | ||
16 | + | ||
17 | + private string $raw; | ||
18 | + | ||
19 | + private function __construct(string $address){ | ||
20 | + $this->raw = $address; | ||
21 | + if($this->raw){ | ||
22 | + $this->parse(); | ||
23 | + } | ||
24 | + } | ||
25 | + | ||
26 | + /** | ||
27 | + * 创建一个地址类 | ||
28 | + * @param string $address | ||
29 | + * @return static | ||
30 | + * @author:dc | ||
31 | + * @time 2024/9/11 11:37 | ||
32 | + */ | ||
33 | + public static function make(string $address):self { | ||
34 | + return new self($address); | ||
35 | + } | ||
36 | + | ||
37 | + /** | ||
38 | + * 解析地址 | ||
39 | + * @author:dc | ||
40 | + * @time 2024/9/11 11:39 | ||
41 | + */ | ||
42 | + private function parse(){ | ||
43 | + | ||
44 | + $email = self::pregEmail($this->raw); | ||
45 | + | ||
46 | + if(!empty($email)){ | ||
47 | + $this->email = $email; | ||
48 | + $this->name = trim(str_replace([$email,'"','<','>','>','<'],'',$this->raw)); | ||
49 | + } | ||
50 | + if($this->name){ | ||
51 | + $this->name = DeCode::decode($this->name); | ||
52 | + }else{ | ||
53 | + $this->name = explode('@',$this->email)[0]??''; | ||
54 | + } | ||
55 | + | ||
56 | + } | ||
57 | + | ||
58 | + /** | ||
59 | + * 匹配邮箱 | ||
60 | + * @param $str | ||
61 | + * @return string | ||
62 | + * @author:dc | ||
63 | + * @time 2024/9/11 11:43 | ||
64 | + */ | ||
65 | + private function pregEmail(string $str):string { | ||
66 | + preg_match('/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/',$str,$email); | ||
67 | + if(empty($email[0])){ | ||
68 | + // 邮箱2 | ||
69 | + preg_match('/[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/',$str,$email); | ||
70 | + } | ||
71 | + return str_replace(['<','>'],'',$email[0]??''); | ||
72 | + } | ||
73 | + | ||
74 | + | ||
75 | + /** | ||
76 | + * @return string | ||
77 | + */ | ||
78 | + public function getRaw(): string | ||
79 | + { | ||
80 | + return $this->raw; | ||
81 | + } | ||
82 | + | ||
83 | +} |
lib/Imap/Parse/Body.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace Lib\Imap\Parse; | ||
4 | + | ||
5 | +/** | ||
6 | + * 邮件内容 | ||
7 | + * @author:dc | ||
8 | + * @time 2024/9/20 14:57 | ||
9 | + * Class Body | ||
10 | + * @package Lib\Imap\Parse | ||
11 | + */ | ||
12 | +class Body { | ||
13 | + | ||
14 | + /** | ||
15 | + * 原始数据 | ||
16 | + * @var string | ||
17 | + */ | ||
18 | + protected string $raw = ''; | ||
19 | + | ||
20 | + /** | ||
21 | + * 解析后的数据 | ||
22 | + * @var array | ||
23 | + */ | ||
24 | + protected array $item = []; | ||
25 | + | ||
26 | + | ||
27 | + public function __construct(string $result) | ||
28 | + { | ||
29 | + // 匹配出id和数据 | ||
30 | + if(preg_match("/\* (\d+) FETCH \(/",$result)){ | ||
31 | + | ||
32 | + } | ||
33 | + | ||
34 | + $this->parse(); | ||
35 | + } | ||
36 | + | ||
37 | + | ||
38 | + | ||
39 | + /** | ||
40 | + * 解析 | ||
41 | + * @author:dc | ||
42 | + * @time 2024/9/14 17:35 | ||
43 | + */ | ||
44 | + protected function parse(){ | ||
45 | + | ||
46 | + | ||
47 | + | ||
48 | + } | ||
49 | + | ||
50 | + | ||
51 | + | ||
52 | + | ||
53 | +} | ||
54 | + | ||
55 | + | ||
56 | + | ||
57 | + | ||
58 | + | ||
59 | + | ||
60 | + | ||
61 | + | ||
62 | + | ||
63 | + | ||
64 | + | ||
65 | + | ||
66 | + | ||
67 | + | ||
68 | + | ||
69 | + | ||
70 | + | ||
71 | + |
lib/Imap/Parse/DeCode.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace Lib\Imap\Parse; | ||
4 | + | ||
5 | + | ||
6 | + | ||
7 | +/** | ||
8 | + * 解析邮件 | ||
9 | + * @author:dc | ||
10 | + * @time 2024/9/10 16:54 | ||
11 | + * Class Header | ||
12 | + */ | ||
13 | +class DeCode{ | ||
14 | + | ||
15 | + /** | ||
16 | + * Fallback Encoding | ||
17 | + * | ||
18 | + * @var string | ||
19 | + */ | ||
20 | + public $fallback_encoding = 'UTF-8'; | ||
21 | + | ||
22 | + | ||
23 | + /** | ||
24 | + * 进行解码 | ||
25 | + * @author:dc | ||
26 | + * @time 2024/9/10 16:56 | ||
27 | + */ | ||
28 | + public static function decode(string $value){ | ||
29 | + $obj = new self(); | ||
30 | + $value = trim($value); | ||
31 | + $original_value = $value; | ||
32 | + $is_utf8_base = $obj->is_uft8($value); | ||
33 | + if ($is_utf8_base) { | ||
34 | + $value = mb_decode_mimeheader($value); | ||
35 | + } | ||
36 | + if ($obj->notDecoded($original_value, $value)) { | ||
37 | + $decoded_value = $obj->mime_header_decode($value); | ||
38 | + if (count($decoded_value) > 0) { | ||
39 | + if (property_exists($decoded_value[0], "text")) { | ||
40 | + $value = $decoded_value[0]->text; | ||
41 | + } | ||
42 | + } | ||
43 | + } | ||
44 | + return $value; | ||
45 | + } | ||
46 | + | ||
47 | + /** | ||
48 | + * Decode MIME header elements | ||
49 | + * @link https://php.net/manual/en/function.imap-mime-header-decode.php | ||
50 | + * @param string $text The MIME text | ||
51 | + * | ||
52 | + * @return array The decoded elements are returned in an array of objects, where each | ||
53 | + * object has two properties, charset and text. | ||
54 | + */ | ||
55 | + private function mime_header_decode(string $text): array { | ||
56 | + $charset = $this->getEncoding($text); | ||
57 | + return [(object)[ | ||
58 | + "charset" => $charset, | ||
59 | + "text" => $this->convertEncoding($text, $charset) | ||
60 | + ]]; | ||
61 | + } | ||
62 | + | ||
63 | + /** | ||
64 | + * Convert the encoding | ||
65 | + * @param $str | ||
66 | + * @param string $from | ||
67 | + * @param string $to | ||
68 | + * | ||
69 | + * @return mixed|string | ||
70 | + */ | ||
71 | + public function convertEncoding($str, $from = "ISO-8859-2", $to = "UTF-8") { | ||
72 | + | ||
73 | + $str = mb_decode_mimeheader($str); | ||
74 | + | ||
75 | + $from = EncodingAliases::get($from, $this->fallback_encoding); | ||
76 | + $to = EncodingAliases::get($to, $this->fallback_encoding); | ||
77 | + | ||
78 | + if ($from === $to) { | ||
79 | + return $str; | ||
80 | + } | ||
81 | + | ||
82 | + // We don't need to do convertEncoding() if charset is ASCII (us-ascii): | ||
83 | + // ASCII is a subset of UTF-8, so all ASCII files are already UTF-8 encoded | ||
84 | + // https://stackoverflow.com/a/11303410 | ||
85 | + // | ||
86 | + // us-ascii is the same as ASCII: | ||
87 | + // ASCII is the traditional name for the encoding system; the Internet Assigned Numbers Authority (IANA) | ||
88 | + // prefers the updated name US-ASCII, which clarifies that this system was developed in the US and | ||
89 | + // based on the typographical symbols predominantly in use there. | ||
90 | + // https://en.wikipedia.org/wiki/ASCII | ||
91 | + // | ||
92 | + // convertEncoding() function basically means convertToUtf8(), so when we convert ASCII string into UTF-8 it gets broken. | ||
93 | + if (strtolower($from) == 'us-ascii' && $to == 'UTF-8') { | ||
94 | + return $str; | ||
95 | + } | ||
96 | + | ||
97 | + try { | ||
98 | + | ||
99 | + if(mb_detect_encoding($str)=='UTF-8'){ | ||
100 | + return $str; | ||
101 | + } | ||
102 | + | ||
103 | + if (!$from) { | ||
104 | + return mb_convert_encoding($str, $to); | ||
105 | + } | ||
106 | + | ||
107 | + return mb_convert_encoding($str, $to, $from); | ||
108 | + | ||
109 | + } catch (\Exception $e) { | ||
110 | + if (strstr($from, '-')) { | ||
111 | + $from = str_replace('-', '', $from); | ||
112 | + return $this->convertEncoding($str, $from, $to); | ||
113 | + } else { | ||
114 | + return $str; | ||
115 | + } | ||
116 | + } | ||
117 | + } | ||
118 | + | ||
119 | + | ||
120 | + /** | ||
121 | + * Get the encoding of a given abject | ||
122 | + * @param object|string $structure | ||
123 | + * | ||
124 | + * @return string | ||
125 | + */ | ||
126 | + private function getEncoding($structure): string { | ||
127 | + if (property_exists($structure, 'parameters')) { | ||
128 | + foreach ($structure->parameters as $parameter) { | ||
129 | + if (strtolower($parameter->attribute) == "charset") { | ||
130 | + return EncodingAliases::get($parameter->value, $this->fallback_encoding); | ||
131 | + } | ||
132 | + } | ||
133 | + } elseif (property_exists($structure, 'charset')) { | ||
134 | + return EncodingAliases::get($structure->charset, $this->fallback_encoding); | ||
135 | + } elseif (is_string($structure) === true) { | ||
136 | + | ||
137 | + preg_match("/^=\?([a-z0-9-]{3,})\?/i",$structure,$code); | ||
138 | + if(!empty($code[1])){ | ||
139 | + $code = EncodingAliases::get($code[1],''); | ||
140 | + if($code){ | ||
141 | + return $code; | ||
142 | + } | ||
143 | + } | ||
144 | + | ||
145 | + $result = mb_detect_encoding($structure); | ||
146 | + return $result === false ? $this->fallback_encoding : $result; | ||
147 | + } | ||
148 | + | ||
149 | + return $this->fallback_encoding; | ||
150 | + } | ||
151 | + | ||
152 | + | ||
153 | + /** | ||
154 | + * Check if a given pair of strings has been decoded | ||
155 | + * @param $encoded | ||
156 | + * @param $decoded | ||
157 | + * | ||
158 | + * @return bool | ||
159 | + */ | ||
160 | + private function notDecoded($encoded, $decoded): bool { | ||
161 | + return 0 === strpos($decoded, '=?') | ||
162 | + && strlen($decoded) - 2 === strpos($decoded, '?=') | ||
163 | + && false !== strpos($encoded, $decoded); | ||
164 | + } | ||
165 | + | ||
166 | + | ||
167 | + | ||
168 | + /** | ||
169 | + * Test if a given value is utf-8 encoded | ||
170 | + * @param $value | ||
171 | + * | ||
172 | + * @return bool | ||
173 | + */ | ||
174 | + private function is_uft8($value): bool { | ||
175 | + return strpos(strtolower($value), '=?utf-8?') === 0; | ||
176 | + } | ||
177 | + | ||
178 | + | ||
179 | +} |
lib/Imap/Parse/EncodingAliases.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace Lib\Imap\Parse; | ||
4 | + | ||
5 | +/** | ||
6 | + * Class EncodingAliases | ||
7 | + * | ||
8 | + * @package Webklex\PHPIMAP | ||
9 | + */ | ||
10 | +class EncodingAliases { | ||
11 | + | ||
12 | + /** | ||
13 | + * Contains email encoding mappings | ||
14 | + * | ||
15 | + * @var array | ||
16 | + */ | ||
17 | + private static $aliases = [ | ||
18 | + /* | ||
19 | + |-------------------------------------------------------------------------- | ||
20 | + | Email encoding aliases | ||
21 | + |-------------------------------------------------------------------------- | ||
22 | + | | ||
23 | + | Email encoding aliases used to convert to iconv supported charsets | ||
24 | + | | ||
25 | + | | ||
26 | + | This Source Code Form is subject to the terms of the Mozilla Public | ||
27 | + | License, v. 2.0. If a copy of the MPL was not distributed with this | ||
28 | + | file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
29 | + | | ||
30 | + | This Original Code has been modified by IBM Corporation. | ||
31 | + | Modifications made by IBM described herein are | ||
32 | + | Copyright (c) International Business Machines | ||
33 | + | Corporation, 1999 | ||
34 | + | | ||
35 | + | Modifications to Mozilla code or documentation | ||
36 | + | identified per MPL Section 3.3 | ||
37 | + | | ||
38 | + | Date Modified by Description of modification | ||
39 | + | 12/09/1999 IBM Corp. Support for IBM codepages - 850,852,855,857,862,864 | ||
40 | + | | ||
41 | + | Rule of this file: | ||
42 | + | 1. key should always be in lower case ascii so we can do case insensitive | ||
43 | + | comparison in the code faster. | ||
44 | + | 2. value should be the one used in unicode converter | ||
45 | + | | ||
46 | + | 3. If the charset is not used for document charset, but font charset | ||
47 | + | (e.g. XLFD charset- such as JIS x0201, JIS x0208), don't put here | ||
48 | + | | ||
49 | + */ | ||
50 | + "ascii" => "us-ascii", | ||
51 | + "us-ascii" => "us-ascii", | ||
52 | + "ansi_x3.4-1968" => "us-ascii", | ||
53 | + "646" => "us-ascii", | ||
54 | + "iso-8859-1" => "ISO-8859-1", | ||
55 | + "iso-8859-2" => "ISO-8859-2", | ||
56 | + "iso-8859-3" => "ISO-8859-3", | ||
57 | + "iso-8859-4" => "ISO-8859-4", | ||
58 | + "iso-8859-5" => "ISO-8859-5", | ||
59 | + "iso-8859-6" => "ISO-8859-6", | ||
60 | + "iso-8859-6-i" => "ISO-8859-6-I", | ||
61 | + "iso-8859-6-e" => "ISO-8859-6-E", | ||
62 | + "iso-8859-7" => "ISO-8859-7", | ||
63 | + "iso-8859-8" => "ISO-8859-8", | ||
64 | + "iso-8859-8-i" => "ISO-8859-8-I", | ||
65 | + "iso-8859-8-e" => "ISO-8859-8-E", | ||
66 | + "iso-8859-9" => "ISO-8859-9", | ||
67 | + "iso-8859-10" => "ISO-8859-10", | ||
68 | + "iso-8859-11" => "ISO-8859-11", | ||
69 | + "iso-8859-13" => "ISO-8859-13", | ||
70 | + "iso-8859-14" => "ISO-8859-14", | ||
71 | + "iso-8859-15" => "ISO-8859-15", | ||
72 | + "iso-8859-16" => "ISO-8859-16", | ||
73 | + "iso-ir-111" => "ISO-IR-111", | ||
74 | + "iso-2022-cn" => "ISO-2022-CN", | ||
75 | + "iso-2022-cn-ext" => "ISO-2022-CN", | ||
76 | + "iso-2022-kr" => "ISO-2022-KR", | ||
77 | + "iso-2022-jp" => "ISO-2022-JP", | ||
78 | + "utf-16be" => "UTF-16BE", | ||
79 | + "utf-16le" => "UTF-16LE", | ||
80 | + "utf-16" => "UTF-16", | ||
81 | + "windows-1250" => "windows-1250", | ||
82 | + "windows-1251" => "windows-1251", | ||
83 | + "windows-1252" => "windows-1252", | ||
84 | + "windows-1253" => "windows-1253", | ||
85 | + "windows-1254" => "windows-1254", | ||
86 | + "windows-1255" => "windows-1255", | ||
87 | + "windows-1256" => "windows-1256", | ||
88 | + "windows-1257" => "windows-1257", | ||
89 | + "windows-1258" => "windows-1258", | ||
90 | + "ibm866" => "IBM866", | ||
91 | + "ibm850" => "IBM850", | ||
92 | + "ibm852" => "IBM852", | ||
93 | + "ibm855" => "IBM855", | ||
94 | + "ibm857" => "IBM857", | ||
95 | + "ibm862" => "IBM862", | ||
96 | + "ibm864" => "IBM864", | ||
97 | + "utf-8" => "UTF-8", | ||
98 | + "utf-7" => "UTF-7", | ||
99 | + "shift_jis" => "Shift_JIS", | ||
100 | + "big5" => "Big5", | ||
101 | + "euc-jp" => "EUC-JP", | ||
102 | + "euc-kr" => "EUC-KR", | ||
103 | + "gb2312" => "GB2312", | ||
104 | + "gb18030" => "gb18030", | ||
105 | + "viscii" => "VISCII", | ||
106 | + "koi8-r" => "KOI8-R", | ||
107 | + "koi8_r" => "KOI8-R", | ||
108 | + "cskoi8r" => "KOI8-R", | ||
109 | + "koi" => "KOI8-R", | ||
110 | + "koi8" => "KOI8-R", | ||
111 | + "koi8-u" => "KOI8-U", | ||
112 | + "tis-620" => "TIS-620", | ||
113 | + "t.61-8bit" => "T.61-8bit", | ||
114 | + "hz-gb-2312" => "HZ-GB-2312", | ||
115 | + "big5-hkscs" => "Big5-HKSCS", | ||
116 | + "gbk" => "gbk", | ||
117 | + "cns11643" => "x-euc-tw", | ||
118 | + // | ||
119 | + // Aliases for ISO-8859-1 | ||
120 | + // | ||
121 | + "latin1" => "ISO-8859-1", | ||
122 | + "iso_8859-1" => "ISO-8859-1", | ||
123 | + "iso8859-1" => "ISO-8859-1", | ||
124 | + "iso8859-2" => "ISO-8859-2", | ||
125 | + "iso8859-3" => "ISO-8859-3", | ||
126 | + "iso8859-4" => "ISO-8859-4", | ||
127 | + "iso8859-5" => "ISO-8859-5", | ||
128 | + "iso8859-6" => "ISO-8859-6", | ||
129 | + "iso8859-7" => "ISO-8859-7", | ||
130 | + "iso8859-8" => "ISO-8859-8", | ||
131 | + "iso8859-9" => "ISO-8859-9", | ||
132 | + "iso8859-10" => "ISO-8859-10", | ||
133 | + "iso8859-11" => "ISO-8859-11", | ||
134 | + "iso8859-13" => "ISO-8859-13", | ||
135 | + "iso8859-14" => "ISO-8859-14", | ||
136 | + "iso8859-15" => "ISO-8859-15", | ||
137 | + "iso_8859-1:1987" => "ISO-8859-1", | ||
138 | + "iso-ir-100" => "ISO-8859-1", | ||
139 | + "l1" => "ISO-8859-1", | ||
140 | + "ibm819" => "ISO-8859-1", | ||
141 | + "cp819" => "ISO-8859-1", | ||
142 | + "csisolatin1" => "ISO-8859-1", | ||
143 | + // | ||
144 | + // Aliases for ISO-8859-2 | ||
145 | + // | ||
146 | + "latin2" => "ISO-8859-2", | ||
147 | + "iso_8859-2" => "ISO-8859-2", | ||
148 | + "iso_8859-2:1987" => "ISO-8859-2", | ||
149 | + "iso-ir-101" => "ISO-8859-2", | ||
150 | + "l2" => "ISO-8859-2", | ||
151 | + "csisolatin2" => "ISO-8859-2", | ||
152 | + // | ||
153 | + // Aliases for ISO-8859-3 | ||
154 | + // | ||
155 | + "latin3" => "ISO-8859-3", | ||
156 | + "iso_8859-3" => "ISO-8859-3", | ||
157 | + "iso_8859-3:1988" => "ISO-8859-3", | ||
158 | + "iso-ir-109" => "ISO-8859-3", | ||
159 | + "l3" => "ISO-8859-3", | ||
160 | + "csisolatin3" => "ISO-8859-3", | ||
161 | + // | ||
162 | + // Aliases for ISO-8859-4 | ||
163 | + // | ||
164 | + "latin4" => "ISO-8859-4", | ||
165 | + "iso_8859-4" => "ISO-8859-4", | ||
166 | + "iso_8859-4:1988" => "ISO-8859-4", | ||
167 | + "iso-ir-110" => "ISO-8859-4", | ||
168 | + "l4" => "ISO-8859-4", | ||
169 | + "csisolatin4" => "ISO-8859-4", | ||
170 | + // | ||
171 | + // Aliases for ISO-8859-5 | ||
172 | + // | ||
173 | + "cyrillic" => "ISO-8859-5", | ||
174 | + "iso_8859-5" => "ISO-8859-5", | ||
175 | + "iso_8859-5:1988" => "ISO-8859-5", | ||
176 | + "iso-ir-144" => "ISO-8859-5", | ||
177 | + "csisolatincyrillic" => "ISO-8859-5", | ||
178 | + // | ||
179 | + // Aliases for ISO-8859-6 | ||
180 | + // | ||
181 | + "arabic" => "ISO-8859-6", | ||
182 | + "iso_8859-6" => "ISO-8859-6", | ||
183 | + "iso_8859-6:1987" => "ISO-8859-6", | ||
184 | + "iso-ir-127" => "ISO-8859-6", | ||
185 | + "ecma-114" => "ISO-8859-6", | ||
186 | + "asmo-708" => "ISO-8859-6", | ||
187 | + "csisolatinarabic" => "ISO-8859-6", | ||
188 | + // | ||
189 | + // Aliases for ISO-8859-6-I | ||
190 | + // | ||
191 | + "csiso88596i" => "ISO-8859-6-I", | ||
192 | + // | ||
193 | + // Aliases for ISO-8859-6-E", | ||
194 | + // | ||
195 | + "csiso88596e" => "ISO-8859-6-E", | ||
196 | + // | ||
197 | + // Aliases for ISO-8859-7", | ||
198 | + // | ||
199 | + "greek" => "ISO-8859-7", | ||
200 | + "greek8" => "ISO-8859-7", | ||
201 | + "sun_eu_greek" => "ISO-8859-7", | ||
202 | + "iso_8859-7" => "ISO-8859-7", | ||
203 | + "iso_8859-7:1987" => "ISO-8859-7", | ||
204 | + "iso-ir-126" => "ISO-8859-7", | ||
205 | + "elot_928" => "ISO-8859-7", | ||
206 | + "ecma-118" => "ISO-8859-7", | ||
207 | + "csisolatingreek" => "ISO-8859-7", | ||
208 | + // | ||
209 | + // Aliases for ISO-8859-8", | ||
210 | + // | ||
211 | + "hebrew" => "ISO-8859-8", | ||
212 | + "iso_8859-8" => "ISO-8859-8", | ||
213 | + "visual" => "ISO-8859-8", | ||
214 | + "iso_8859-8:1988" => "ISO-8859-8", | ||
215 | + "iso-ir-138" => "ISO-8859-8", | ||
216 | + "csisolatinhebrew" => "ISO-8859-8", | ||
217 | + // | ||
218 | + // Aliases for ISO-8859-8-I", | ||
219 | + // | ||
220 | + "csiso88598i" => "ISO-8859-8-I", | ||
221 | + "iso-8859-8i" => "ISO-8859-8-I", | ||
222 | + "logical" => "ISO-8859-8-I", | ||
223 | + // | ||
224 | + // Aliases for ISO-8859-8-E", | ||
225 | + // | ||
226 | + "csiso88598e" => "ISO-8859-8-E", | ||
227 | + // | ||
228 | + // Aliases for ISO-8859-9", | ||
229 | + // | ||
230 | + "latin5" => "ISO-8859-9", | ||
231 | + "iso_8859-9" => "ISO-8859-9", | ||
232 | + "iso_8859-9:1989" => "ISO-8859-9", | ||
233 | + "iso-ir-148" => "ISO-8859-9", | ||
234 | + "l5" => "ISO-8859-9", | ||
235 | + "csisolatin5" => "ISO-8859-9", | ||
236 | + // | ||
237 | + // Aliases for UTF-8", | ||
238 | + // | ||
239 | + "unicode-1-1-utf-8" => "UTF-8", | ||
240 | + // nl_langinfo(CODESET) in HP/UX returns 'utf8' under UTF-8 locales", | ||
241 | + "utf8" => "UTF-8", | ||
242 | + // | ||
243 | + // Aliases for Shift_JIS", | ||
244 | + // | ||
245 | + "x-sjis" => "Shift_JIS", | ||
246 | + "shift-jis" => "Shift_JIS", | ||
247 | + "ms_kanji" => "Shift_JIS", | ||
248 | + "csshiftjis" => "Shift_JIS", | ||
249 | + "windows-31j" => "Shift_JIS", | ||
250 | + "cp932" => "Shift_JIS", | ||
251 | + "sjis" => "Shift_JIS", | ||
252 | + // | ||
253 | + // Aliases for EUC_JP", | ||
254 | + // | ||
255 | + "cseucpkdfmtjapanese" => "EUC-JP", | ||
256 | + "x-euc-jp" => "EUC-JP", | ||
257 | + // | ||
258 | + // Aliases for ISO-2022-JP", | ||
259 | + // | ||
260 | + "csiso2022jp" => "ISO-2022-JP", | ||
261 | + // The following are really not aliases ISO-2022-JP, but sharing the same decoder", | ||
262 | + "iso-2022-jp-2" => "ISO-2022-JP", | ||
263 | + "csiso2022jp2" => "ISO-2022-JP", | ||
264 | + // | ||
265 | + // Aliases for Big5", | ||
266 | + // | ||
267 | + "csbig5" => "Big5", | ||
268 | + "cn-big5" => "Big5", | ||
269 | + // x-x-big5 is not really a alias for Big5, add it only for MS FrontPage", | ||
270 | + "x-x-big5" => "Big5", | ||
271 | + // Sun Solaris", | ||
272 | + "zh_tw-big5" => "Big5", | ||
273 | + // | ||
274 | + // Aliases for EUC-KR", | ||
275 | + // | ||
276 | + "cseuckr" => "EUC-KR", | ||
277 | + "ks_c_5601-1987" => "EUC-KR", | ||
278 | + "iso-ir-149" => "EUC-KR", | ||
279 | + "ks_c_5601-1989" => "EUC-KR", | ||
280 | + "ksc_5601" => "EUC-KR", | ||
281 | + "ksc5601" => "EUC-KR", | ||
282 | + "korean" => "EUC-KR", | ||
283 | + "csksc56011987" => "EUC-KR", | ||
284 | + "5601" => "EUC-KR", | ||
285 | + "windows-949" => "EUC-KR", | ||
286 | + // | ||
287 | + // Aliases for GB2312", | ||
288 | + // | ||
289 | + // The following are really not aliases GB2312, add them only for MS FrontPage", | ||
290 | + "gb_2312-80" => "GB2312", | ||
291 | + "iso-ir-58" => "GB2312", | ||
292 | + "chinese" => "GB2312", | ||
293 | + "csiso58gb231280" => "GB2312", | ||
294 | + "csgb2312" => "GB2312", | ||
295 | + "zh_cn.euc" => "GB2312", | ||
296 | + // Sun Solaris", | ||
297 | + "gb_2312" => "GB2312", | ||
298 | + // | ||
299 | + // Aliases for windows-125x ", | ||
300 | + // | ||
301 | + "x-cp1250" => "windows-1250", | ||
302 | + "x-cp1251" => "windows-1251", | ||
303 | + "x-cp1252" => "windows-1252", | ||
304 | + "x-cp1253" => "windows-1253", | ||
305 | + "x-cp1254" => "windows-1254", | ||
306 | + "x-cp1255" => "windows-1255", | ||
307 | + "x-cp1256" => "windows-1256", | ||
308 | + "x-cp1257" => "windows-1257", | ||
309 | + "x-cp1258" => "windows-1258", | ||
310 | + // | ||
311 | + // Aliases for windows-874 ", | ||
312 | + // | ||
313 | + "windows-874" => "windows-874", | ||
314 | + "ibm874" => "windows-874", | ||
315 | + "dos-874" => "windows-874", | ||
316 | + // | ||
317 | + // Aliases for macintosh", | ||
318 | + // | ||
319 | + "macintosh" => "macintosh", | ||
320 | + "x-mac-roman" => "macintosh", | ||
321 | + "mac" => "macintosh", | ||
322 | + "csmacintosh" => "macintosh", | ||
323 | + // | ||
324 | + // Aliases for IBM866", | ||
325 | + // | ||
326 | + "cp866" => "IBM866", | ||
327 | + "cp-866" => "IBM866", | ||
328 | + "866" => "IBM866", | ||
329 | + "csibm866" => "IBM866", | ||
330 | + // | ||
331 | + // Aliases for IBM850", | ||
332 | + // | ||
333 | + "cp850" => "IBM850", | ||
334 | + "850" => "IBM850", | ||
335 | + "csibm850" => "IBM850", | ||
336 | + // | ||
337 | + // Aliases for IBM852", | ||
338 | + // | ||
339 | + "cp852" => "IBM852", | ||
340 | + "852" => "IBM852", | ||
341 | + "csibm852" => "IBM852", | ||
342 | + // | ||
343 | + // Aliases for IBM855", | ||
344 | + // | ||
345 | + "cp855" => "IBM855", | ||
346 | + "855" => "IBM855", | ||
347 | + "csibm855" => "IBM855", | ||
348 | + // | ||
349 | + // Aliases for IBM857", | ||
350 | + // | ||
351 | + "cp857" => "IBM857", | ||
352 | + "857" => "IBM857", | ||
353 | + "csibm857" => "IBM857", | ||
354 | + // | ||
355 | + // Aliases for IBM862", | ||
356 | + // | ||
357 | + "cp862" => "IBM862", | ||
358 | + "862" => "IBM862", | ||
359 | + "csibm862" => "IBM862", | ||
360 | + // | ||
361 | + // Aliases for IBM864", | ||
362 | + // | ||
363 | + "cp864" => "IBM864", | ||
364 | + "864" => "IBM864", | ||
365 | + "csibm864" => "IBM864", | ||
366 | + "ibm-864" => "IBM864", | ||
367 | + // | ||
368 | + // Aliases for T.61-8bit", | ||
369 | + // | ||
370 | + "t.61" => "T.61-8bit", | ||
371 | + "iso-ir-103" => "T.61-8bit", | ||
372 | + "csiso103t618bit" => "T.61-8bit", | ||
373 | + // | ||
374 | + // Aliases for UTF-7", | ||
375 | + // | ||
376 | + "x-unicode-2-0-utf-7" => "UTF-7", | ||
377 | + "unicode-2-0-utf-7" => "UTF-7", | ||
378 | + "unicode-1-1-utf-7" => "UTF-7", | ||
379 | + "csunicode11utf7" => "UTF-7", | ||
380 | + // | ||
381 | + // Aliases for ISO-10646-UCS-2", | ||
382 | + // | ||
383 | + "csunicode" => "UTF-16BE", | ||
384 | + "csunicode11" => "UTF-16BE", | ||
385 | + "iso-10646-ucs-basic" => "UTF-16BE", | ||
386 | + "csunicodeascii" => "UTF-16BE", | ||
387 | + "iso-10646-unicode-latin1" => "UTF-16BE", | ||
388 | + "csunicodelatin1" => "UTF-16BE", | ||
389 | + "iso-10646" => "UTF-16BE", | ||
390 | + "iso-10646-j-1" => "UTF-16BE", | ||
391 | + // | ||
392 | + // Aliases for ISO-8859-10", | ||
393 | + // | ||
394 | + "latin6" => "ISO-8859-10", | ||
395 | + "iso-ir-157" => "ISO-8859-10", | ||
396 | + "l6" => "ISO-8859-10", | ||
397 | + // Currently .properties cannot handle : in key", | ||
398 | + //iso_8859-10:1992" => "ISO-8859-10", | ||
399 | + "csisolatin6" => "ISO-8859-10", | ||
400 | + // | ||
401 | + // Aliases for ISO-8859-15", | ||
402 | + // | ||
403 | + "iso_8859-15" => "ISO-8859-15", | ||
404 | + "csisolatin9" => "ISO-8859-15", | ||
405 | + "l9" => "ISO-8859-15", | ||
406 | + // | ||
407 | + // Aliases for ISO-IR-111", | ||
408 | + // | ||
409 | + "ecma-cyrillic" => "ISO-IR-111", | ||
410 | + "csiso111ecmacyrillic" => "ISO-IR-111", | ||
411 | + // | ||
412 | + // Aliases for ISO-2022-KR", | ||
413 | + // | ||
414 | + "csiso2022kr" => "ISO-2022-KR", | ||
415 | + // | ||
416 | + // Aliases for VISCII", | ||
417 | + // | ||
418 | + "csviscii" => "VISCII", | ||
419 | + // | ||
420 | + // Aliases for x-euc-tw", | ||
421 | + // | ||
422 | + "zh_tw-euc" => "x-euc-tw", | ||
423 | + // | ||
424 | + // Following names appears in unix nl_langinfo(CODESET)", | ||
425 | + // They can be compiled as platform specific if necessary", | ||
426 | + // DONT put things here if it does not look generic enough (like hp15CN)", | ||
427 | + // | ||
428 | + "iso88591" => "ISO-8859-1", | ||
429 | + "iso88592" => "ISO-8859-2", | ||
430 | + "iso88593" => "ISO-8859-3", | ||
431 | + "iso88594" => "ISO-8859-4", | ||
432 | + "iso88595" => "ISO-8859-5", | ||
433 | + "iso88596" => "ISO-8859-6", | ||
434 | + "iso88597" => "ISO-8859-7", | ||
435 | + "iso88598" => "ISO-8859-8", | ||
436 | + "iso88599" => "ISO-8859-9", | ||
437 | + "iso885910" => "ISO-8859-10", | ||
438 | + "iso885911" => "ISO-8859-11", | ||
439 | + "iso885912" => "ISO-8859-12", | ||
440 | + "iso885913" => "ISO-8859-13", | ||
441 | + "iso885914" => "ISO-8859-14", | ||
442 | + "iso885915" => "ISO-8859-15", | ||
443 | + "cp1250" => "windows-1250", | ||
444 | + "cp1251" => "windows-1251", | ||
445 | + "cp1252" => "windows-1252", | ||
446 | + "cp1253" => "windows-1253", | ||
447 | + "cp1254" => "windows-1254", | ||
448 | + "cp1255" => "windows-1255", | ||
449 | + "cp1256" => "windows-1256", | ||
450 | + "cp1257" => "windows-1257", | ||
451 | + "cp1258" => "windows-1258", | ||
452 | + "x-gbk" => "gbk", | ||
453 | + "windows-936" => "gbk", | ||
454 | + "ansi-1251" => "windows-1251", | ||
455 | + ]; | ||
456 | + | ||
457 | + /** | ||
458 | + * Returns proper encoding mapping, if exsists. If it doesn't, return unchanged $encoding | ||
459 | + * @param string|null $encoding | ||
460 | + * @param string|null $fallback | ||
461 | + * | ||
462 | + * @return string | ||
463 | + */ | ||
464 | + public static function get($encoding, string $fallback = null): string { | ||
465 | + if (isset(self::$aliases[strtolower($encoding ?? '')])) { | ||
466 | + return self::$aliases[strtolower($encoding ?? '')]; | ||
467 | + } | ||
468 | + return $fallback !== null ? $fallback : $encoding; | ||
469 | + } | ||
470 | + | ||
471 | +} |
lib/Imap/Parse/Fetch.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace Lib\Imap\Parse; | ||
4 | + | ||
5 | + | ||
6 | +/** | ||
7 | + * 解析邮件 | ||
8 | + * @author:dc | ||
9 | + * @time 2024/9/10 16:54 | ||
10 | + * Class Header | ||
11 | + */ | ||
12 | +class Fetch{ | ||
13 | + | ||
14 | + /** | ||
15 | + * @var array | ||
16 | + */ | ||
17 | + private array $attributes = []; | ||
18 | + | ||
19 | + /** | ||
20 | + * 原始头信息 | ||
21 | + * @var string | ||
22 | + */ | ||
23 | + protected string $raw_header; | ||
24 | + | ||
25 | + /** | ||
26 | + * Header constructor. | ||
27 | + * @param string $raw_header | ||
28 | + */ | ||
29 | + public function __construct(string $raw_header) | ||
30 | + { | ||
31 | + $this->raw_header = $raw_header; | ||
32 | + | ||
33 | + $this->rfc822_parse_headers(); | ||
34 | + } | ||
35 | + | ||
36 | + | ||
37 | + /** | ||
38 | + * @param string $name | ||
39 | + * @param string $value | ||
40 | + * @param bool $append | ||
41 | + * @author:dc | ||
42 | + * @time 2024/9/11 16:24 | ||
43 | + */ | ||
44 | + public function setAttribute(string $name, string $value, bool $append = true){ | ||
45 | + $name = strtolower($name); | ||
46 | + | ||
47 | + if(!$append){ | ||
48 | + $this->attributes[$name] = $value; | ||
49 | + }else{ | ||
50 | + if(isset($this->attributes[$name])) | ||
51 | + $this->attributes[$name] .= "\r\n".$value; | ||
52 | + else | ||
53 | + $this->attributes[$name] = $value; | ||
54 | + } | ||
55 | + } | ||
56 | + | ||
57 | + | ||
58 | + /** | ||
59 | + * 解析邮件头部 | ||
60 | + * @author:dc | ||
61 | + * @time 2024/9/10 16:29 | ||
62 | + */ | ||
63 | + private function rfc822_parse_headers(){ | ||
64 | + $header = explode("\r\n",$this->raw_header); | ||
65 | + $name = ''; | ||
66 | + foreach ($header as $str){ | ||
67 | + // 判断是否是上一行的 | ||
68 | + if(str_starts_with($str,' ') || str_starts_with($str,"\t")){ | ||
69 | + $this->setAttribute($name,$str); | ||
70 | + }else{ | ||
71 | + $str = explode(":",$str); | ||
72 | + $name = $str[0]; | ||
73 | + unset($str[0]); | ||
74 | + $this->setAttribute($name,implode(':',$str)); | ||
75 | + } | ||
76 | + | ||
77 | + } | ||
78 | + | ||
79 | + foreach ($this->attributes as $name => $attribute){ | ||
80 | + $attribute = trim($attribute); | ||
81 | + $this->attributes[$name] = $attribute; | ||
82 | + } | ||
83 | + } | ||
84 | + | ||
85 | + | ||
86 | + public function __get(string $name) | ||
87 | + { | ||
88 | + return $this->get($name); | ||
89 | + } | ||
90 | + | ||
91 | + /** | ||
92 | + * 读取字段 | ||
93 | + * @param string $name | ||
94 | + * @return mixed | ||
95 | + * @author:dc | ||
96 | + * @time 2024/9/11 15:06 | ||
97 | + */ | ||
98 | + public function get(string $name):mixed { | ||
99 | + $name = strtolower($name); | ||
100 | + | ||
101 | + $m = 'get'.str_replace(' ','',ucwords(str_replace('-',' ',$name))); | ||
102 | + | ||
103 | + if(method_exists($this,$m)){ | ||
104 | + return $this->{$m}(); | ||
105 | + } | ||
106 | + | ||
107 | + return $this->attributes[$name]??''; | ||
108 | + } | ||
109 | + | ||
110 | + | ||
111 | + /** | ||
112 | + * 获取收件地址 可能是假地址 | ||
113 | + * @param bool $is_obj 是否解析成对象 Address | ||
114 | + * @return Address[] | ||
115 | + * @author:dc | ||
116 | + * @time 2024/9/11 15:03 | ||
117 | + */ | ||
118 | + public function getTo() { | ||
119 | + // 如果有这个字段,就用这个字段 to字段的邮箱可能被伪造 | ||
120 | + if(!empty($this->attributes['delivered-to'])){ | ||
121 | + $to = $this->attributes['delivered-to']; | ||
122 | + }else{ | ||
123 | + $to = $this->attributes['to']??''; | ||
124 | + } | ||
125 | + | ||
126 | + return $this->parseAddress($to); | ||
127 | + } | ||
128 | + | ||
129 | + /** | ||
130 | + * 解析地址 | ||
131 | + * @param string $address | ||
132 | + * @return array | ||
133 | + * @author:dc | ||
134 | + * @time 2024/9/11 15:53 | ||
135 | + */ | ||
136 | + private function parseAddress(string $address){ | ||
137 | + $arr = []; | ||
138 | + foreach (explode(',',$address) as $str){ | ||
139 | + $arr[] = Address::make($str); | ||
140 | + } | ||
141 | + return $arr; | ||
142 | + } | ||
143 | + | ||
144 | + /** | ||
145 | + * 抄送人 | ||
146 | + * @return array | ||
147 | + * @author:dc | ||
148 | + * @time 2024/9/11 15:53 | ||
149 | + */ | ||
150 | + public function getCc() | ||
151 | + { | ||
152 | + return $this->parseAddress($this->attributes['cc']??''); | ||
153 | + } | ||
154 | + | ||
155 | + /** | ||
156 | + * 密送人 | ||
157 | + * @return array | ||
158 | + * @author:dc | ||
159 | + * @time 2024/9/11 15:54 | ||
160 | + */ | ||
161 | + public function getBcc() | ||
162 | + { | ||
163 | + return $this->parseAddress($this->attributes['bcc']??''); | ||
164 | + } | ||
165 | + | ||
166 | + | ||
167 | + | ||
168 | + /** | ||
169 | + * 发件人 可能是欺炸 发件人 | ||
170 | + * @return Address | ||
171 | + * @author:dc | ||
172 | + * @time 2024/9/11 15:19 | ||
173 | + */ | ||
174 | + public function getFrom():Address { | ||
175 | + return Address::make($this->attributes['from']??''); | ||
176 | + } | ||
177 | + | ||
178 | + /** | ||
179 | + * 获取解码后的主题 | ||
180 | + * @return string | ||
181 | + * @author:dc | ||
182 | + * @time 2024/9/11 15:22 | ||
183 | + */ | ||
184 | + public function getSubject():string { | ||
185 | + $subject = explode("\n",$this->attributes['subject']??''); | ||
186 | + foreach ($subject as $ak=>$str){ | ||
187 | + $subject[$ak] = DeCode::decode($str); | ||
188 | + } | ||
189 | + | ||
190 | + return implode("\n",$subject); | ||
191 | + } | ||
192 | + | ||
193 | + | ||
194 | + /** | ||
195 | + * Boundary | ||
196 | + * @param string $str | ||
197 | + * @return string | ||
198 | + * @author:dc | ||
199 | + * @time 2024/9/11 16:05 | ||
200 | + */ | ||
201 | + public function getBoundary(string $str = ''): string | ||
202 | + { | ||
203 | + $pre = "/boundary=(.*?(?=;)|(.*))/i"; | ||
204 | + // 在内容类型中读取 大多少情况都在这里面 | ||
205 | + $contentType = $str ? $str: ($this->attributes['content-type']??''); | ||
206 | + if($contentType){ | ||
207 | + preg_match($pre,$contentType,$b); | ||
208 | + if(!empty($b[1])){ | ||
209 | + return trim(str_replace(['"',';'],'',$b[1])); | ||
210 | + } | ||
211 | + } | ||
212 | + if($this->raw_header && !$str){ | ||
213 | + return $this->getBoundary($this->raw_header); | ||
214 | + } | ||
215 | + | ||
216 | + return ''; | ||
217 | + } | ||
218 | + | ||
219 | + /** | ||
220 | + * 这个是是否加急 1加急 3普通 5不急 | ||
221 | + * @return string | ||
222 | + */ | ||
223 | + public function getPriority(): string | ||
224 | + { | ||
225 | + if(!empty($this->attributes['priority'])){ | ||
226 | + return $this->attributes['priority']; | ||
227 | + } | ||
228 | + | ||
229 | + if(!empty($this->attributes['x-priority'])){ | ||
230 | + return $this->attributes['x-priority']; | ||
231 | + } | ||
232 | + | ||
233 | + return '3'; | ||
234 | + } | ||
235 | + | ||
236 | + | ||
237 | + | ||
238 | + /** | ||
239 | + * @return array | ||
240 | + */ | ||
241 | + public function getAttributes(): array | ||
242 | + { | ||
243 | + return $this->attributes; | ||
244 | + } | ||
245 | + | ||
246 | + /** | ||
247 | + * @return array | ||
248 | + * @author:dc | ||
249 | + * @time 2024/9/11 16:15 | ||
250 | + */ | ||
251 | + public function toArray():array { | ||
252 | + $arr = []; | ||
253 | + foreach ($this->attributes as $key=>$attr){ | ||
254 | + $arr[$key] = $this->get($key); | ||
255 | + } | ||
256 | + | ||
257 | + return $arr; | ||
258 | + } | ||
259 | + | ||
260 | +} |
lib/Imap/Parse/Folder/Folder.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace Lib\Imap\Parse\Folder; | ||
4 | + | ||
5 | + | ||
6 | +/** | ||
7 | + * 文件夹 | ||
8 | + * @author:dc | ||
9 | + * @time 2024/9/20 9:33 | ||
10 | + * Class Folder | ||
11 | + * @package Lib\Imap\Parse | ||
12 | + */ | ||
13 | +class Folder{ | ||
14 | + | ||
15 | + /** | ||
16 | + * 文件夹名称 utf-7编码 全路径 | ||
17 | + * @var string | ||
18 | + */ | ||
19 | + public string $folder; | ||
20 | + | ||
21 | + /** | ||
22 | + * 是否可以选择 | ||
23 | + * @var bool | ||
24 | + */ | ||
25 | + public bool $isSelect; | ||
26 | + | ||
27 | + | ||
28 | + /** | ||
29 | + * 上级解析结构 | ||
30 | + * @var Folders | ||
31 | + */ | ||
32 | + protected Folders $parent; | ||
33 | + | ||
34 | + /** | ||
35 | + * 标签 | ||
36 | + * 有些邮箱把发件箱 send 改成了 send message 这里就有send标签,代表这个文件夹是发件箱 | ||
37 | + * 草稿,垃圾箱。回收站,等默认几个文件夹都有可能被更改 | ||
38 | + * @var array | ||
39 | + */ | ||
40 | + public array $flags; | ||
41 | + | ||
42 | + public function __construct(string $folder,array $flags,bool $isSelect,Folders $parent) | ||
43 | + { | ||
44 | + $this->folder = $folder; | ||
45 | + $this->flags = $flags; | ||
46 | + $this->isSelect = $isSelect; | ||
47 | + } | ||
48 | + | ||
49 | + /** | ||
50 | + * 把文件夹编码转换成utf8的 | ||
51 | + * @author:dc | ||
52 | + * @time 2024/9/20 10:00 | ||
53 | + */ | ||
54 | + public function getParseFolder(){ | ||
55 | + return mb_convert_encoding($this->folder, 'UTF-8', 'UTF7-IMAP'); | ||
56 | + } | ||
57 | + | ||
58 | + /** | ||
59 | + * 获取子目录 | ||
60 | + * @return static[] | ||
61 | + * @author:dc | ||
62 | + * @time 2024/9/20 11:55 | ||
63 | + */ | ||
64 | + public function getChild():array { | ||
65 | + return $this->parent->findChild($this); | ||
66 | + } | ||
67 | + | ||
68 | + | ||
69 | + | ||
70 | +} |
lib/Imap/Parse/Folder/Folders.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace Lib\Imap\Parse\Folder; | ||
4 | + | ||
5 | + | ||
6 | +/** | ||
7 | + * 文件夹 | ||
8 | + * @author:dc | ||
9 | + * @time 2024/9/20 9:33 | ||
10 | + * Class Folder | ||
11 | + * @package Lib\Imap\Parse | ||
12 | + */ | ||
13 | +class Folders{ | ||
14 | + | ||
15 | + /** | ||
16 | + * @var Folder[] | ||
17 | + */ | ||
18 | + protected array $item = []; | ||
19 | + | ||
20 | + | ||
21 | + /** | ||
22 | + * 原始头信息 | ||
23 | + * @var string | ||
24 | + */ | ||
25 | + protected array $raw = []; | ||
26 | + | ||
27 | + /** | ||
28 | + * Header constructor. | ||
29 | + * @param array $raw | ||
30 | + */ | ||
31 | + public function __construct(array $raw) | ||
32 | + { | ||
33 | + $this->raw = $raw; | ||
34 | + | ||
35 | + $this->parse(); | ||
36 | + } | ||
37 | + | ||
38 | + /** | ||
39 | + * 解析目录 | ||
40 | + * @author:dc | ||
41 | + * @time 2024/9/20 10:07 | ||
42 | + */ | ||
43 | + private function parse(){ | ||
44 | + foreach ($this->raw as $item){ | ||
45 | + // 解析源数据 | ||
46 | + if(preg_match('/^\*\sLIST\s\(([\\a-z\s]{0,})\)\s\"(.*)\"\s\"?(.*)\"?$/Ui',$item,$m)){ | ||
47 | + if($m[1]){ | ||
48 | + $flags = explode(' ',$m[1]); | ||
49 | + $flags = array_map(function ($v){ | ||
50 | + $v = trim($v); | ||
51 | + $v = trim($v,'\\'); | ||
52 | + return $v; | ||
53 | + },$flags); | ||
54 | + }else{ | ||
55 | + $flags = []; | ||
56 | + } | ||
57 | + | ||
58 | + | ||
59 | + $this->item[] = new Folder(trim(trim($m[3]),'"'),$flags, !str_contains($m[1], 'NoSelect'), $this); | ||
60 | + } | ||
61 | + } | ||
62 | + | ||
63 | + } | ||
64 | + | ||
65 | + /** | ||
66 | + * 获取顶级栏目 一级目录 | ||
67 | + * @return Folder[] | ||
68 | + * @author:dc | ||
69 | + * @time 2024/9/20 11:39 | ||
70 | + */ | ||
71 | + public function getTopFolder():array { | ||
72 | + return array_filter($this->item,function ($folder){ | ||
73 | + return substr_count($folder->folder,'/') == 0; | ||
74 | + }); | ||
75 | + } | ||
76 | + | ||
77 | + | ||
78 | + /** | ||
79 | + * 查找当前目录的下级目录 | ||
80 | + * @param Folder $folder | ||
81 | + * @return Folder[] | ||
82 | + * @author:dc | ||
83 | + * @time 2024/9/19 17:54 | ||
84 | + */ | ||
85 | + public function findChild(Folder $folder):array { | ||
86 | + $fs = []; | ||
87 | + foreach ($this->item as $item){ | ||
88 | + // 跳过相同目录 | ||
89 | + if($folder->folder == $item->folder) continue; | ||
90 | + if(strpos($item->folder,$folder->folder) !== 0) continue; | ||
91 | + | ||
92 | + $f = substr($item->folder,strlen($folder->folder),999); | ||
93 | + $f = ltrim($f,'/'); | ||
94 | + if(substr_count($f,'/') == 0){ | ||
95 | +// $item['child'] = $this->findChild($item['folder']); | ||
96 | + $fs[] = $item; | ||
97 | + } | ||
98 | + } | ||
99 | + return $fs; | ||
100 | + } | ||
101 | + | ||
102 | + | ||
103 | + /** | ||
104 | + * 获取所有目录 list 不是tree结构 | ||
105 | + * @return Folder[] | ||
106 | + * @author:dc | ||
107 | + * @time 2024/9/20 11:43 | ||
108 | + */ | ||
109 | + public function all():array{ | ||
110 | + return $this->item; | ||
111 | + } | ||
112 | + | ||
113 | + | ||
114 | +} |
lib/Imap/Parse/Header.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace Lib\Imap\Parse; | ||
4 | + | ||
5 | + | ||
6 | +/** | ||
7 | + * 解析邮件 | ||
8 | + * @author:dc | ||
9 | + * @time 2024/9/10 16:54 | ||
10 | + * Class Header | ||
11 | + */ | ||
12 | +class Header{ | ||
13 | + | ||
14 | + /** | ||
15 | + * @var array | ||
16 | + */ | ||
17 | + private array $attributes = []; | ||
18 | + | ||
19 | + /** | ||
20 | + * 原始头信息 | ||
21 | + * @var string | ||
22 | + */ | ||
23 | + protected string $raw_header; | ||
24 | + | ||
25 | + /** | ||
26 | + * Header constructor. | ||
27 | + * @param string $raw_header | ||
28 | + */ | ||
29 | + public function __construct(string $raw_header) | ||
30 | + { | ||
31 | + $this->raw_header = $raw_header; | ||
32 | + | ||
33 | + $this->rfc822_parse_headers(); | ||
34 | + } | ||
35 | + | ||
36 | + | ||
37 | + /** | ||
38 | + * @param string $name | ||
39 | + * @param string $value | ||
40 | + * @param bool $append | ||
41 | + * @author:dc | ||
42 | + * @time 2024/9/11 16:24 | ||
43 | + */ | ||
44 | + public function setAttribute(string $name, string $value, bool $append = true){ | ||
45 | + $name = strtolower($name); | ||
46 | + | ||
47 | + if(!$append){ | ||
48 | + $this->attributes[$name] = $value; | ||
49 | + }else{ | ||
50 | + if(isset($this->attributes[$name])) | ||
51 | + $this->attributes[$name] .= "\r\n".$value; | ||
52 | + else | ||
53 | + $this->attributes[$name] = $value; | ||
54 | + } | ||
55 | + } | ||
56 | + | ||
57 | + | ||
58 | + /** | ||
59 | + * 解析邮件头部 | ||
60 | + * @author:dc | ||
61 | + * @time 2024/9/10 16:29 | ||
62 | + */ | ||
63 | + private function rfc822_parse_headers(){ | ||
64 | + $header = explode("\r\n",$this->raw_header); | ||
65 | + $name = ''; | ||
66 | + foreach ($header as $str){ | ||
67 | + // 判断是否是上一行的 | ||
68 | + if(str_starts_with($str,' ') || str_starts_with($str,"\t")){ | ||
69 | + $this->setAttribute($name,$str); | ||
70 | + }else{ | ||
71 | + $str = explode(":",$str); | ||
72 | + $name = $str[0]; | ||
73 | + unset($str[0]); | ||
74 | + $this->setAttribute($name,implode(':',$str)); | ||
75 | + } | ||
76 | + | ||
77 | + } | ||
78 | + | ||
79 | + foreach ($this->attributes as $name => $attribute){ | ||
80 | + $attribute = trim($attribute); | ||
81 | + $this->attributes[$name] = $attribute; | ||
82 | + } | ||
83 | + } | ||
84 | + | ||
85 | + | ||
86 | + public function __get(string $name) | ||
87 | + { | ||
88 | + return $this->get($name); | ||
89 | + } | ||
90 | + | ||
91 | + /** | ||
92 | + * 读取字段 | ||
93 | + * @param string $name | ||
94 | + * @return mixed | ||
95 | + * @author:dc | ||
96 | + * @time 2024/9/11 15:06 | ||
97 | + */ | ||
98 | + public function get(string $name):mixed { | ||
99 | + $name = strtolower($name); | ||
100 | + | ||
101 | + $m = 'get'.str_replace(' ','',ucwords(str_replace('-',' ',$name))); | ||
102 | + | ||
103 | + if(method_exists($this,$m)){ | ||
104 | + return $this->{$m}(); | ||
105 | + } | ||
106 | + | ||
107 | + return $this->attributes[$name]??''; | ||
108 | + } | ||
109 | + | ||
110 | + | ||
111 | + /** | ||
112 | + * 获取收件地址 可能是假地址 | ||
113 | + * @param bool $is_obj 是否解析成对象 Address | ||
114 | + * @return Address[] | ||
115 | + * @author:dc | ||
116 | + * @time 2024/9/11 15:03 | ||
117 | + */ | ||
118 | + public function getTo() { | ||
119 | + // 如果有这个字段,就用这个字段 to字段的邮箱可能被伪造 | ||
120 | + if(!empty($this->attributes['delivered-to'])){ | ||
121 | + $to = $this->attributes['delivered-to']; | ||
122 | + }else{ | ||
123 | + $to = $this->attributes['to']??''; | ||
124 | + } | ||
125 | + | ||
126 | + return $this->parseAddress($to); | ||
127 | + } | ||
128 | + | ||
129 | + /** | ||
130 | + * 解析地址 | ||
131 | + * @param string $address | ||
132 | + * @return array | ||
133 | + * @author:dc | ||
134 | + * @time 2024/9/11 15:53 | ||
135 | + */ | ||
136 | + private function parseAddress(string $address){ | ||
137 | + $arr = []; | ||
138 | + foreach (explode(',',$address) as $str){ | ||
139 | + $arr[] = Address::make($str); | ||
140 | + } | ||
141 | + return $arr; | ||
142 | + } | ||
143 | + | ||
144 | + /** | ||
145 | + * 抄送人 | ||
146 | + * @return array | ||
147 | + * @author:dc | ||
148 | + * @time 2024/9/11 15:53 | ||
149 | + */ | ||
150 | + public function getCc() | ||
151 | + { | ||
152 | + return $this->parseAddress($this->attributes['cc']??''); | ||
153 | + } | ||
154 | + | ||
155 | + /** | ||
156 | + * 密送人 | ||
157 | + * @return array | ||
158 | + * @author:dc | ||
159 | + * @time 2024/9/11 15:54 | ||
160 | + */ | ||
161 | + public function getBcc() | ||
162 | + { | ||
163 | + return $this->parseAddress($this->attributes['bcc']??''); | ||
164 | + } | ||
165 | + | ||
166 | + | ||
167 | + | ||
168 | + /** | ||
169 | + * 发件人 可能是欺炸 发件人 | ||
170 | + * @return Address | ||
171 | + * @author:dc | ||
172 | + * @time 2024/9/11 15:19 | ||
173 | + */ | ||
174 | + public function getFrom():Address { | ||
175 | + return Address::make($this->attributes['from']??''); | ||
176 | + } | ||
177 | + | ||
178 | + /** | ||
179 | + * 获取解码后的主题 | ||
180 | + * @return string | ||
181 | + * @author:dc | ||
182 | + * @time 2024/9/11 15:22 | ||
183 | + */ | ||
184 | + public function getSubject():string { | ||
185 | + $subject = explode("\n",$this->attributes['subject']??''); | ||
186 | + foreach ($subject as $ak=>$str){ | ||
187 | + $subject[$ak] = DeCode::decode($str); | ||
188 | + } | ||
189 | + | ||
190 | + return implode("\n",$subject); | ||
191 | + } | ||
192 | + | ||
193 | + | ||
194 | + /** | ||
195 | + * Boundary | ||
196 | + * @param string $str | ||
197 | + * @return string | ||
198 | + * @author:dc | ||
199 | + * @time 2024/9/11 16:05 | ||
200 | + */ | ||
201 | + public function getBoundary(string $str = ''): string | ||
202 | + { | ||
203 | + $pre = "/boundary=(.*?(?=;)|(.*))/i"; | ||
204 | + // 在内容类型中读取 大多少情况都在这里面 | ||
205 | + $contentType = $str ? $str: ($this->attributes['content-type']??''); | ||
206 | + if($contentType){ | ||
207 | + preg_match($pre,$contentType,$b); | ||
208 | + if(!empty($b[1])){ | ||
209 | + return trim(str_replace(['"',';'],'',$b[1])); | ||
210 | + } | ||
211 | + } | ||
212 | + if($this->raw_header && !$str){ | ||
213 | + return $this->getBoundary($this->raw_header); | ||
214 | + } | ||
215 | + | ||
216 | + return ''; | ||
217 | + } | ||
218 | + | ||
219 | + /** | ||
220 | + * 这个是是否加急 1加急 3普通 5不急 | ||
221 | + * @return string | ||
222 | + */ | ||
223 | + public function getPriority(): string | ||
224 | + { | ||
225 | + if(!empty($this->attributes['priority'])){ | ||
226 | + return $this->attributes['priority']; | ||
227 | + } | ||
228 | + | ||
229 | + if(!empty($this->attributes['x-priority'])){ | ||
230 | + return $this->attributes['x-priority']; | ||
231 | + } | ||
232 | + | ||
233 | + return '3'; | ||
234 | + } | ||
235 | + | ||
236 | + | ||
237 | + | ||
238 | + /** | ||
239 | + * @return array | ||
240 | + */ | ||
241 | + public function getAttributes(): array | ||
242 | + { | ||
243 | + return $this->attributes; | ||
244 | + } | ||
245 | + | ||
246 | + /** | ||
247 | + * @return array | ||
248 | + * @author:dc | ||
249 | + * @time 2024/9/11 16:15 | ||
250 | + */ | ||
251 | + public function toArray():array { | ||
252 | + $arr = []; | ||
253 | + foreach ($this->attributes as $key=>$attr){ | ||
254 | + $arr[$key] = $this->get($key); | ||
255 | + } | ||
256 | + | ||
257 | + return $arr; | ||
258 | + } | ||
259 | + | ||
260 | +} |
lib/Imap/Parse/MessageItem.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace Lib\Imap\Parse; | ||
4 | + | ||
5 | +use Lib\Imap\Request\Msg; | ||
6 | + | ||
7 | +/** | ||
8 | + * 邮件 | ||
9 | + * @author:dc | ||
10 | + * @time 2024/9/18 11:30 | ||
11 | + * @property Body $body | ||
12 | + * Class MessageItem | ||
13 | + * @package Lib\Imap\Parse | ||
14 | + */ | ||
15 | +class MessageItem { | ||
16 | + | ||
17 | + /** | ||
18 | + * 邮件的唯一id 文件夹下面唯一 | ||
19 | + * @var int | ||
20 | + */ | ||
21 | + public int $uid = 0; | ||
22 | + | ||
23 | + /** | ||
24 | + * 邮件编号 | ||
25 | + * @var int | ||
26 | + */ | ||
27 | + public int $msgno = 0; | ||
28 | + | ||
29 | + /** | ||
30 | + * 这个是邮件收到的时间 | ||
31 | + * @var string | ||
32 | + */ | ||
33 | + public string $date = ''; | ||
34 | + | ||
35 | + /** | ||
36 | + * 邮件大小 | ||
37 | + * @var int | ||
38 | + */ | ||
39 | + public int $size = 0; | ||
40 | + | ||
41 | + /** | ||
42 | + * 标记 | ||
43 | + * @var array | ||
44 | + */ | ||
45 | + public array $flags = []; | ||
46 | + | ||
47 | + /** | ||
48 | + * 头部信息 主题 发件 收件 发送时间等消息 | ||
49 | + * @var Header | ||
50 | + */ | ||
51 | + public Header $header; | ||
52 | + | ||
53 | + | ||
54 | + /** | ||
55 | + * @var Msg | ||
56 | + */ | ||
57 | + protected Msg $msg; | ||
58 | + | ||
59 | + /** | ||
60 | + * MessageItem constructor. | ||
61 | + * @param int $msgno | ||
62 | + */ | ||
63 | + public function __construct(int $msgno, Msg $msg) | ||
64 | + { | ||
65 | + | ||
66 | + $this->msg = $msg; | ||
67 | + | ||
68 | + $this->msgno = $msgno; | ||
69 | + } | ||
70 | + | ||
71 | + /** | ||
72 | + * 是否已被标记为删除了 | ||
73 | + * @return bool | ||
74 | + * @author:dc | ||
75 | + * @time 2024/9/18 11:52 | ||
76 | + */ | ||
77 | + public function isDeleted():bool { | ||
78 | + return in_array('deleted',$this->flags); | ||
79 | + } | ||
80 | + | ||
81 | + /** | ||
82 | + * 已读 | ||
83 | + * @return bool | ||
84 | + * @author:dc | ||
85 | + * @time 2024/9/18 17:53 | ||
86 | + */ | ||
87 | + public function isSeen():bool { | ||
88 | + return in_array('seen',$this->flags); | ||
89 | + } | ||
90 | + | ||
91 | + /** | ||
92 | + * 草稿 | ||
93 | + * @return bool | ||
94 | + * @author:dc | ||
95 | + * @time 2024/9/18 17:53 | ||
96 | + */ | ||
97 | + public function isDraft():bool { | ||
98 | + return in_array('seen',$this->flags); | ||
99 | + } | ||
100 | + | ||
101 | + /** | ||
102 | + * 星标 | ||
103 | + * @return bool | ||
104 | + * @author:dc | ||
105 | + * @time 2024/9/18 17:53 | ||
106 | + */ | ||
107 | + public function isFlagged():bool { | ||
108 | + return in_array('flagged',$this->flags); | ||
109 | + } | ||
110 | + | ||
111 | + /** | ||
112 | + * 已回复 | ||
113 | + * @return bool | ||
114 | + * @author:dc | ||
115 | + * @time 2024/9/18 17:52 | ||
116 | + */ | ||
117 | + public function isAnswered():bool { | ||
118 | + return in_array('answered',$this->flags); | ||
119 | + } | ||
120 | + | ||
121 | + /** | ||
122 | + * 最近 | ||
123 | + * @return bool | ||
124 | + * @author:dc | ||
125 | + * @time 2024/9/18 17:54 | ||
126 | + */ | ||
127 | + public function isRecent():bool { | ||
128 | + return in_array('recent',$this->flags); | ||
129 | + } | ||
130 | + | ||
131 | + /** | ||
132 | + * 获取邮件体 字段 内容 | ||
133 | + * @param string $name | ||
134 | + * @return mixed | ||
135 | + * @author:dc | ||
136 | + * @time 2024/9/20 15:13 | ||
137 | + */ | ||
138 | + public function get(string $name){ | ||
139 | + return $this->header->get($name); | ||
140 | + } | ||
141 | + | ||
142 | + /** | ||
143 | + * 获取body体 | ||
144 | + * @return Body | ||
145 | + * @author:dc | ||
146 | + * @time 2024/9/20 15:14 | ||
147 | + */ | ||
148 | + public function getBody():Body { | ||
149 | + return $this->msg->folder->getBody($this->uid,true); | ||
150 | + } | ||
151 | + | ||
152 | + | ||
153 | + public function __get(string $name) | ||
154 | + { | ||
155 | + if($name=='body'){ | ||
156 | + return $this->getBody(); | ||
157 | + }else{ | ||
158 | + $this->get($name); | ||
159 | + } | ||
160 | + } | ||
161 | + | ||
162 | +} | ||
163 | + | ||
164 | + | ||
165 | + | ||
166 | + | ||
167 | + | ||
168 | + | ||
169 | + | ||
170 | + | ||
171 | + | ||
172 | + | ||
173 | + | ||
174 | + | ||
175 | + | ||
176 | + | ||
177 | + | ||
178 | + | ||
179 | + | ||
180 | + |
lib/Imap/Parse/Messager.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace Lib\Imap\Parse; | ||
4 | + | ||
5 | +use Lib\Imap\Request\Msg; | ||
6 | + | ||
7 | +/** | ||
8 | + * 邮件列表 | ||
9 | + * @author:dc | ||
10 | + * @time 2024/9/14 15:33 | ||
11 | + * Class Messager | ||
12 | + * @package Lib\Imap\Parse | ||
13 | + */ | ||
14 | +class Messager implements \IteratorAggregate { | ||
15 | + | ||
16 | + /** | ||
17 | + * 原始数据 | ||
18 | + * @var array | ||
19 | + */ | ||
20 | + protected array $result = []; | ||
21 | + | ||
22 | + /** | ||
23 | + * 解析后的数据 | ||
24 | + * @var array | ||
25 | + */ | ||
26 | + protected array $item = []; | ||
27 | + | ||
28 | + /** | ||
29 | + * 读取消息的 | ||
30 | + * @var Msg | ||
31 | + */ | ||
32 | + protected Msg $msg; | ||
33 | + | ||
34 | + | ||
35 | + public function __construct(array $result, Msg $msg) | ||
36 | + { | ||
37 | + $this->msg = $msg; | ||
38 | + | ||
39 | + $k = 0; | ||
40 | + foreach ($result as $item){ | ||
41 | + // 匹配出id和数据 | ||
42 | + if(preg_match("/\* (\d+) FETCH \(/",$item)){ | ||
43 | + $k++; | ||
44 | + $this->result[$k] = $item; | ||
45 | + }else{ | ||
46 | + if(isset($this->result[$k])){ | ||
47 | + $this->result[$k] .= $item; | ||
48 | + } | ||
49 | + } | ||
50 | + } | ||
51 | + | ||
52 | + $this->parse(); | ||
53 | + } | ||
54 | + | ||
55 | + /** | ||
56 | + * 实现 使用 foreach功能 | ||
57 | + * @return \ArrayIterator | ||
58 | + * @author:dc | ||
59 | + * @time 2024/9/18 10:43 | ||
60 | + */ | ||
61 | + public function getIterator() { | ||
62 | + return new \ArrayIterator($this->item); | ||
63 | + } | ||
64 | + | ||
65 | + /** | ||
66 | + * 解析 | ||
67 | + * @author:dc | ||
68 | + * @time 2024/9/14 17:35 | ||
69 | + */ | ||
70 | + protected function parse(){ | ||
71 | + foreach ($this->result as $item){ | ||
72 | + $item = trim($item); | ||
73 | + // 匹配出id和数据 | ||
74 | + if(preg_match("/\* (\d+) FETCH \(([\w\s\d\r\n\W]{1,})\)$/i",$item,$line)){ | ||
75 | + $this->parseFetch($line[1],$line[2]); | ||
76 | + } | ||
77 | + $line = null; | ||
78 | + } | ||
79 | + | ||
80 | + | ||
81 | + } | ||
82 | + | ||
83 | + | ||
84 | + /** | ||
85 | + * 解析头部信息,只能解析固定字段的属性 | ||
86 | + * @param int $msgno | ||
87 | + * @param string $line | ||
88 | + * UID 1568602720 INTERNALDATE "16-Sep-2019 10:58:40 +0800" FLAGS (\Seen) RFC822.HEADER {816} | ||
89 | + Received: from localhost (unknown [10.110.4.165]) | ||
90 | + by mfast8 (Coremail) with SMTP id t8CowAA3Za5g+n5d1b4tCA--.53220S2; | ||
91 | + Mon, 16 Sep 2019 10:58:40 +0800 (CST) | ||
92 | + Date: Mon, 16 Sep 2019 10:58:40 +0800 (GMT+08:00) | ||
93 | + From: =?utf-8?B?572R5piT6YKu566x5aSn5biI5Zui6Zif?= <developer.MailMaster@service.netease.com> | ||
94 | + To: zhlong0616@163.com | ||
95 | + Message-ID: <1803973093.340445.1568602720140.JavaMail.developer.MailMaster@service.netease.com> | ||
96 | + Subject: =?utf-8?B?5qyi6L+O5L2/55So572R5piT6YKu566x5aSn5biI?= | ||
97 | + MIME-Version: 1.0 | ||
98 | + Content-Type: multipart/mixed; | ||
99 | + boundary="----=_Part_340444_1698792253.1568602720140" | ||
100 | + X-CM-TRANSID:t8CowAA3Za5g+n5d1b4tCA--.53220S2 | ||
101 | + X-Coremail-Antispam: 1Uf129KBjDUn29KB7ZKAUJUUBvt529EdanIXcx71UUUUU7v73 | ||
102 | + VFW2AGmfu7bjvjm3AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvj4RJUUUUUUUU | ||
103 | + X-Originating-IP: [10.110.4.165] | ||
104 | + | ||
105 | + RFC822.SIZE 5111 | ||
106 | + * @author:dc | ||
107 | + * @time 2024/9/18 11:46 | ||
108 | + */ | ||
109 | + protected function parseFetch(int $msgno, string $line) { | ||
110 | + $this->item[$msgno] = new MessageItem($msgno, $this->msg); | ||
111 | + $tokens = explode(' ',$line); | ||
112 | + while ($tokens){ | ||
113 | + $key = trim(array_shift($tokens)); | ||
114 | + switch ($key){ | ||
115 | + case 'UID':{ | ||
116 | + $this->item[$msgno]->uid = array_shift($tokens); | ||
117 | + break; | ||
118 | + } | ||
119 | + case 'FLAGS':{ | ||
120 | + $flags = []; | ||
121 | + while (1){ | ||
122 | + $flags[] = array_shift($tokens); | ||
123 | + if(substr(end($flags),-1)==')') break; | ||
124 | + } | ||
125 | + $this->item[$msgno]->flags = array_map(function ($v){ | ||
126 | + return strtolower(str_replace(['\\','(',')'],'',$v)); | ||
127 | + },$flags); | ||
128 | + break; | ||
129 | + } | ||
130 | + case 'INTERNALDATE':{ | ||
131 | + // 内部时间 | ||
132 | + $date = ''; | ||
133 | + while (1){ | ||
134 | + $date .= array_shift($tokens); | ||
135 | + if(substr($date,-1)=='"') break; | ||
136 | + $date.= ' '; | ||
137 | + } | ||
138 | + $this->item[$msgno]->date = trim($date,'"'); | ||
139 | + break; | ||
140 | + } | ||
141 | + case 'RFC822.SIZE':{ | ||
142 | + $this->item[$msgno]->size = array_shift($tokens); | ||
143 | + break; | ||
144 | + } | ||
145 | + case 'RFC822.HEADER':{ | ||
146 | + $str = trim(implode(' ',$tokens)); | ||
147 | + // 是否带有字符数数量 | ||
148 | + if(preg_match("/^\{(\d+)\}/",$str,$num)){ | ||
149 | + $len = $num[1]+strlen($num[0]); | ||
150 | + $header = substr($str,0,$len); | ||
151 | + // 剩下的字符串 | ||
152 | + $tokens = explode(' ',substr($str,$len,9999)); | ||
153 | + // 踢出长度的头 | ||
154 | + $header = substr($header,strlen($num[0]),strlen($header)); | ||
155 | + | ||
156 | + }else{ | ||
157 | + // 以3个\r\n为结束 | ||
158 | + $str = explode("\r\n\r\n\r\n",trim($str)); | ||
159 | + $header = array_shift($str); | ||
160 | + // 剩余的字符串 | ||
161 | + $tokens = explode(' ',implode("\r\n\r\n\r\n",$str)); | ||
162 | + } | ||
163 | + | ||
164 | + $this->item[$msgno]->header = new Header($header); | ||
165 | + break; | ||
166 | + } | ||
167 | + default:{break;} | ||
168 | + } | ||
169 | + | ||
170 | + } | ||
171 | + | ||
172 | + } | ||
173 | + | ||
174 | + | ||
175 | + /** | ||
176 | + * 循环结果 | ||
177 | + * @param \Closure $closure | ||
178 | + * @author:dc | ||
179 | + * @time 2024/9/18 11:41 | ||
180 | + */ | ||
181 | + public function each(\Closure $closure):void { | ||
182 | + array_map($closure,$this->item); | ||
183 | + } | ||
184 | + | ||
185 | + | ||
186 | +} | ||
187 | + | ||
188 | + | ||
189 | + | ||
190 | + | ||
191 | + | ||
192 | + | ||
193 | + | ||
194 | + | ||
195 | + | ||
196 | + | ||
197 | + | ||
198 | + | ||
199 | + | ||
200 | + | ||
201 | + | ||
202 | + | ||
203 | + | ||
204 | + |
lib/Imap/Request/Body.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | + | ||
4 | +namespace Lib\Imap\Request; | ||
5 | + | ||
6 | +use Lib\Imap\Imap; | ||
7 | + | ||
8 | +use Lib\Imap\Parse\Body as ParseBody; | ||
9 | + | ||
10 | +/** | ||
11 | + * body邮件内容 | ||
12 | + * @author:dc | ||
13 | + * @time 2024/9/20 14:53 | ||
14 | + * Class Body | ||
15 | + * @package Lib\Imap\Request | ||
16 | + */ | ||
17 | +class Body extends Request { | ||
18 | + | ||
19 | + /** | ||
20 | + * @var Folder | ||
21 | + */ | ||
22 | + protected Folder $folder; | ||
23 | + | ||
24 | + | ||
25 | + | ||
26 | + public function __construct(Imap $imap, Folder $folder) | ||
27 | + { | ||
28 | + parent::__construct($imap); | ||
29 | + | ||
30 | + $this->folder = $folder; | ||
31 | + } | ||
32 | + | ||
33 | + | ||
34 | + public function exec(): static{return $this;} | ||
35 | + | ||
36 | + | ||
37 | + /** | ||
38 | + * 读取邮件body | ||
39 | + * @param int $num | ||
40 | + * @param bool $is_uid | ||
41 | + * @return ParseBody | ||
42 | + * @author:dc | ||
43 | + * @time 2024/9/20 15:40 | ||
44 | + */ | ||
45 | + public function get(int $num, bool $is_uid = false):ParseBody { | ||
46 | + $this->folder->exec(); // 防止在其他文件夹下面 | ||
47 | + $this->cmd("%sFETCH %s (RFC822.BODY)", $is_uid ? 'UID ' : '', $num); | ||
48 | + | ||
49 | + return (new ParseBody($this->result ? $this->result[0] : '')); | ||
50 | + } | ||
51 | + | ||
52 | + | ||
53 | + | ||
54 | + | ||
55 | +} |
lib/Imap/Request/Folder.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | + | ||
4 | +namespace Lib\Imap\Request; | ||
5 | + | ||
6 | +use Lib\Imap\Imap; | ||
7 | + | ||
8 | +/** | ||
9 | + * 登录 | ||
10 | + * @author:dc | ||
11 | + * @time 2024/9/13 17:09 | ||
12 | + * Class Login | ||
13 | + * @package Lib\Imap\Request | ||
14 | + */ | ||
15 | +class Folder extends Request{ | ||
16 | + | ||
17 | + protected string $folder; | ||
18 | + | ||
19 | + /** | ||
20 | + * 未读数量 有的邮箱是不会返回未读数量的 | ||
21 | + * @var int | ||
22 | + */ | ||
23 | + protected int $seen = 0; | ||
24 | + | ||
25 | + /** | ||
26 | + * 总数 | ||
27 | + * @var int | ||
28 | + */ | ||
29 | + protected int $total = 0; | ||
30 | + | ||
31 | + /** | ||
32 | + * 最近邮件的数量 | ||
33 | + * @var int | ||
34 | + */ | ||
35 | + protected int $recent = 0; | ||
36 | + | ||
37 | + | ||
38 | + | ||
39 | + /** | ||
40 | + * 标签 | ||
41 | + * @var array | ||
42 | + */ | ||
43 | + protected array $flags = []; | ||
44 | + | ||
45 | + /** | ||
46 | + * 是否初始了,就是是否select 文件夹 | ||
47 | + * @var bool | ||
48 | + */ | ||
49 | + private bool $isInit = false; | ||
50 | + | ||
51 | + | ||
52 | + /** | ||
53 | + * Folder constructor. | ||
54 | + * @param Imap $imap | ||
55 | + * @param string $folder | ||
56 | + */ | ||
57 | + public function __construct(Imap $imap, string $folder) | ||
58 | + { | ||
59 | + parent::__construct($imap); | ||
60 | + | ||
61 | + $this->folder = $folder; | ||
62 | + } | ||
63 | + | ||
64 | + | ||
65 | + public function exec(): static | ||
66 | + { | ||
67 | + | ||
68 | + if(!$this->imap->checkedFolder($this->folder)){ | ||
69 | + $this->cmd('SELECT "%s"',$this->folder); | ||
70 | + | ||
71 | +// ["* 128 EXISTS\r\n","* 2 RECENT\r\n","* OK [UIDVALIDITY 1] UIDs valid\r\n","* FLAGS (\\Answered \\Seen \\Deleted \\Draft \\Flagged)\r\n","* OK [PERMANENTFLAGS (\\Answered \\Seen \\Deleted \\Draft \\Flagged)] Limited\r\n","TAG3 OK [READ-WRITE] SELECT completed\r\n"] | ||
72 | + if($this->isOk()){ | ||
73 | + $this->isInit = true; | ||
74 | + | ||
75 | + foreach ($this->result as $item){ | ||
76 | + $item = trim($item); | ||
77 | + // 总数量 | ||
78 | + if(preg_match("/^\* (\d+) EXISTS$/i",$item,$m)){ | ||
79 | + $this->total = (int) $m[1]; | ||
80 | + } | ||
81 | + // 最近的 | ||
82 | + elseif (preg_match("/^\* (\d+) RECENT$/i",$item,$m)){ | ||
83 | + $this->total = (int) $m[1]; | ||
84 | + } | ||
85 | + // 未读 | ||
86 | + elseif (preg_match("/^\*.*\[UNSEEN (\d+)\]/i",$item,$m)){ | ||
87 | + $this->seen = (int) $m[1]; | ||
88 | + } | ||
89 | + // tag | ||
90 | + elseif (preg_match("/^\* FLAGS \((.*)\)$/i",$item,$m)){ | ||
91 | + $this->flags = explode(' ',$m[1]); | ||
92 | + } | ||
93 | + | ||
94 | + } | ||
95 | + // 设置 | ||
96 | + $this->imap->setCheckedFolder($this->folder); | ||
97 | + } | ||
98 | + | ||
99 | + } | ||
100 | + | ||
101 | + | ||
102 | + return $this; | ||
103 | + } | ||
104 | + | ||
105 | + | ||
106 | + /** | ||
107 | + * 邮件列表 | ||
108 | + * @return Msg | ||
109 | + * @author:dc | ||
110 | + * @time 2024/9/14 11:36 | ||
111 | + */ | ||
112 | + public function msg():Msg { | ||
113 | + return new Msg($this->imap,$this); | ||
114 | + } | ||
115 | + | ||
116 | + | ||
117 | + /** | ||
118 | + * @return array | ||
119 | + */ | ||
120 | + public function getFlags(): array | ||
121 | + { | ||
122 | + if(!$this->isInit){ | ||
123 | + $this->exec(); | ||
124 | + } | ||
125 | + return $this->flags; | ||
126 | + } | ||
127 | + | ||
128 | + /** | ||
129 | + * @return int | ||
130 | + */ | ||
131 | + public function getRecent(): int | ||
132 | + { | ||
133 | + if(!$this->isInit){ | ||
134 | + $this->exec(); | ||
135 | + } | ||
136 | + return $this->recent; | ||
137 | + } | ||
138 | + | ||
139 | + /** | ||
140 | + * @return int | ||
141 | + */ | ||
142 | + public function getSeen(): int | ||
143 | + { | ||
144 | + if(!$this->isInit){ | ||
145 | + $this->exec(); | ||
146 | + } | ||
147 | + return $this->seen; | ||
148 | + } | ||
149 | + | ||
150 | + | ||
151 | + /** | ||
152 | + * @return int | ||
153 | + */ | ||
154 | + public function getTotal(): int | ||
155 | + { | ||
156 | + if(!$this->isInit){ | ||
157 | + $this->exec(); | ||
158 | + } | ||
159 | + return $this->total; | ||
160 | + } | ||
161 | + | ||
162 | + | ||
163 | + /** | ||
164 | + * 关闭当前目录 一般情况用不到 | ||
165 | + * @return bool | ||
166 | + * @author:dc | ||
167 | + * @time 2024/9/19 14:04 | ||
168 | + */ | ||
169 | + public function close():bool { | ||
170 | + // 必须初始了 并且在当前文件夹下面 | ||
171 | + if($this->isInit && $this->imap->checkedFolder($this->folder)){ | ||
172 | + $this->cmd('CLOSE'); | ||
173 | + return $this->isOk(); | ||
174 | + } | ||
175 | + return true; | ||
176 | + } | ||
177 | + | ||
178 | + /** | ||
179 | + * 创建目录 | ||
180 | + * @return bool | ||
181 | + * @author:dc | ||
182 | + * @time 2024/9/20 13:43 | ||
183 | + */ | ||
184 | + public function create():bool { | ||
185 | + $this->cmd('CREATE "%s"',$this->folder); | ||
186 | + return $this->isOk(); | ||
187 | + } | ||
188 | + | ||
189 | + /** | ||
190 | + * 修改目录 | ||
191 | + * @param string $newFolder 新的文件夹名称 | ||
192 | + * @param bool $is_utf7 是否是utf7的编码 | ||
193 | + * @return bool | ||
194 | + * @author:dc | ||
195 | + * @time 2024/9/20 13:47 | ||
196 | + */ | ||
197 | + public function rename(string $newFolder, bool $is_utf7 = true):bool { | ||
198 | + // 需要转码 | ||
199 | + if(!$is_utf7) $newFolder = mb_convert_encoding($newFolder,'UTF7-IMAP','UTF-8'); | ||
200 | + | ||
201 | +// RENAME oldfolder newfolder | ||
202 | + $this->cmd('RENAME "%s" "%s"',$this->folder,$newFolder); | ||
203 | + | ||
204 | + return $this->isOk(); | ||
205 | + } | ||
206 | + | ||
207 | + /** | ||
208 | + * 删除 目录 | ||
209 | + * @return bool | ||
210 | + * @author:dc | ||
211 | + * @time 2024/9/20 13:50 | ||
212 | + */ | ||
213 | + public function delete():bool { | ||
214 | + | ||
215 | + $this->cmd('DELETE "%s"',$this->folder); | ||
216 | + | ||
217 | + return $this->isOk(); | ||
218 | + } | ||
219 | + | ||
220 | + | ||
221 | + /** | ||
222 | + * 读取body体 | ||
223 | + * @param int $num | ||
224 | + * @param false $is_uid | ||
225 | + * @return \Lib\Imap\Parse\Body | ||
226 | + * @author:dc | ||
227 | + * @time 2024/9/20 15:37 | ||
228 | + */ | ||
229 | + public function getBody(int $num, bool $is_uid = false):\Lib\Imap\Parse\Body{ | ||
230 | + $body = (new Body($this->imap,$this)); | ||
231 | + return $body->get($num, $is_uid = false); | ||
232 | + } | ||
233 | + | ||
234 | + | ||
235 | + | ||
236 | + | ||
237 | +} |
lib/Imap/Request/Folders.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | + | ||
4 | +namespace Lib\Imap\Request; | ||
5 | + | ||
6 | +use Lib\Imap\Imap; | ||
7 | + | ||
8 | +/** | ||
9 | + * 文件夹列表 | ||
10 | + * @author:dc | ||
11 | + * @time 2024/9/19 13:56 | ||
12 | + * Class Folders | ||
13 | + * @package Lib\Imap\Request | ||
14 | + */ | ||
15 | +class Folders extends Request{ | ||
16 | + | ||
17 | + public function __construct(Imap $imap) | ||
18 | + { | ||
19 | + parent::__construct($imap); | ||
20 | + $this->exec(); | ||
21 | + } | ||
22 | + | ||
23 | + public function exec(): static | ||
24 | + { | ||
25 | + | ||
26 | + $this->cmd('LIST "" *'); | ||
27 | + | ||
28 | + return $this; | ||
29 | + } | ||
30 | + | ||
31 | + /** | ||
32 | + * @return \Lib\Imap\Parse\Folder\Folders | ||
33 | + * @author:dc | ||
34 | + * @time 2024/9/20 10:24 | ||
35 | + */ | ||
36 | + public function get():\Lib\Imap\Parse\Folder\Folders { | ||
37 | + return new \Lib\Imap\Parse\Folder\Folders($this->result); | ||
38 | + } | ||
39 | + | ||
40 | +} |
lib/Imap/Request/Login.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | + | ||
4 | +namespace Lib\Imap\Request; | ||
5 | + | ||
6 | +/** | ||
7 | + * 登录 | ||
8 | + * @author:dc | ||
9 | + * @time 2024/9/13 17:09 | ||
10 | + * Class Login | ||
11 | + * @package Lib\Imap\Request | ||
12 | + */ | ||
13 | +class Login extends Request{ | ||
14 | + | ||
15 | + | ||
16 | + public function exec(): static | ||
17 | + { | ||
18 | + | ||
19 | + $pwd = addcslashes($this->imap->config->getPassword(),'"'); | ||
20 | + | ||
21 | + $this->cmd('LOGIN "%s" "%s"',$this->imap->config->getEmail(),$pwd); | ||
22 | + | ||
23 | + return $this; | ||
24 | + } | ||
25 | + | ||
26 | + | ||
27 | + | ||
28 | + | ||
29 | +} |
lib/Imap/Request/Msg.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | + | ||
4 | +namespace Lib\Imap\Request; | ||
5 | + | ||
6 | +use Lib\Imap\Imap; | ||
7 | +use Lib\Imap\ImapSearch; | ||
8 | +use Lib\Imap\Parse\Messager; | ||
9 | + | ||
10 | +/** | ||
11 | + * 登录 | ||
12 | + * @author:dc | ||
13 | + * @time 2024/9/13 17:09 | ||
14 | + * Class Login | ||
15 | + * @package Lib\Imap\Request | ||
16 | + */ | ||
17 | +class Msg extends Request{ | ||
18 | + | ||
19 | + /** | ||
20 | + * @var Folder | ||
21 | + */ | ||
22 | + public Folder $folder; | ||
23 | + | ||
24 | + /** | ||
25 | + * 读取列表条件 | ||
26 | + * @var array | ||
27 | + */ | ||
28 | + protected array $number = []; | ||
29 | + | ||
30 | + | ||
31 | + /** | ||
32 | + * 是否是uid | ||
33 | + * @var bool | ||
34 | + */ | ||
35 | + private bool $isUid = false; | ||
36 | + | ||
37 | + | ||
38 | + public function __construct(Imap $imap, Folder $folder) | ||
39 | + { | ||
40 | + parent::__construct($imap); | ||
41 | + | ||
42 | + $this->folder = $folder; | ||
43 | + } | ||
44 | + | ||
45 | + | ||
46 | + public function exec(): static{return $this;} | ||
47 | + | ||
48 | + | ||
49 | + | ||
50 | + /** | ||
51 | + * 分页读取 | ||
52 | + * @param int $p | ||
53 | + * @param int $limit | ||
54 | + * @return $this | ||
55 | + * @author:dc | ||
56 | + * @time 2024/9/14 14:06 | ||
57 | + */ | ||
58 | + public function forPage(int $p=1, int $limit=20):static { | ||
59 | + $this->msgno(range(($p-1)*$limit+1,$p*$limit)); | ||
60 | + return $this; | ||
61 | + } | ||
62 | + | ||
63 | + /** | ||
64 | + * 使用uid进行查询 | ||
65 | + * @param array $uids | ||
66 | + * @return $this | ||
67 | + * @author:dc | ||
68 | + * @time 2024/9/14 14:06 | ||
69 | + */ | ||
70 | + public function uid(array $uids):static { | ||
71 | + $this->isUid = true; | ||
72 | + $this->number = $uids; | ||
73 | + return $this; | ||
74 | + } | ||
75 | + | ||
76 | + /** | ||
77 | + * 使用编号进行查询 | ||
78 | + * @param array $msgno | ||
79 | + * @return $this | ||
80 | + * @author:dc | ||
81 | + * @time 2024/9/14 14:06 | ||
82 | + */ | ||
83 | + public function msgno(array $msgno):static { | ||
84 | + $this->isUid = false; | ||
85 | + $this->number = $msgno; | ||
86 | + return $this; | ||
87 | + } | ||
88 | + | ||
89 | + /** | ||
90 | + * 搜索 | ||
91 | + * @param ImapSearch $search | ||
92 | + * @return $this | ||
93 | + * @author:dc | ||
94 | + * @time 2024/9/14 15:48 | ||
95 | + */ | ||
96 | + public function search(ImapSearch $search):static { | ||
97 | + $this->folder->exec(); // 防止在其他文件夹下面 | ||
98 | + $this->cmd("UID SEARCH ",$search->toString()); | ||
99 | + $uids = []; | ||
100 | + foreach ($this->result as $item){ | ||
101 | + // 匹配uid * SEARCH 2 84 882 | ||
102 | + if(preg_match("/^\* SEARCH ([0-9\s]{1,})$/i",$item,$m)){ | ||
103 | + $uids = array_merge($uids,explode(' ',$m[1])); | ||
104 | + } | ||
105 | + } | ||
106 | + $this->uid($uids); | ||
107 | + return $this; | ||
108 | + } | ||
109 | + | ||
110 | + | ||
111 | + /** | ||
112 | + * 读取邮件列表 | ||
113 | + * @return Messager | ||
114 | + * @author:dc | ||
115 | + * @time 2024/9/14 15:34 | ||
116 | + */ | ||
117 | + public function get():Messager{ | ||
118 | + $this->folder->exec(); // 防止在其他文件夹下面 | ||
119 | + $this->cmd( | ||
120 | + "%sFETCH %s (UID FLAGS INTERNALDATE RFC822.SIZE RFC822.HEADER)", | ||
121 | + $this->isUid?'UID ':'', | ||
122 | + implode(',',$this->number) | ||
123 | + ); | ||
124 | + | ||
125 | + return new Messager($this->result, $this); | ||
126 | + } | ||
127 | + | ||
128 | + | ||
129 | + public function getBody(){ | ||
130 | + $this->folder->exec(); // 防止在其他文件夹下面 | ||
131 | + $this->cmd( | ||
132 | + "%sFETCH %s (UID FLAGS INTERNALDATE RFC822.SIZE RFC822.HEADER)", | ||
133 | + $this->isUid?'UID ':'', | ||
134 | + implode(',',$this->number) | ||
135 | + ); | ||
136 | + | ||
137 | + return new Messager($this->result, $this); | ||
138 | + } | ||
139 | + | ||
140 | + | ||
141 | + | ||
142 | +} |
lib/Imap/Request/Noop.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | + | ||
4 | +namespace Lib\Imap\Request; | ||
5 | + | ||
6 | +/** | ||
7 | + * 类似 ping 命令 | ||
8 | + * 保持通讯用的 | ||
9 | + * @author:dc | ||
10 | + * @time 2024/9/20 14:19 | ||
11 | + * Class Noop | ||
12 | + * @package Lib\Imap\Request | ||
13 | + */ | ||
14 | +class Noop extends Request{ | ||
15 | + | ||
16 | + | ||
17 | + public function exec(): static | ||
18 | + { | ||
19 | + | ||
20 | + $this->cmd('NOOP'); | ||
21 | + | ||
22 | + return $this; | ||
23 | + } | ||
24 | + | ||
25 | + | ||
26 | + | ||
27 | + | ||
28 | +} |
lib/Imap/Request/Request.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace Lib\Imap\Request; | ||
4 | + | ||
5 | + | ||
6 | +use Lib\Imap\Imap; | ||
7 | + | ||
8 | +/** | ||
9 | + * 命令组装 请求 返回处理 | ||
10 | + * @author:dc | ||
11 | + * @time 2024/9/18 10:12 | ||
12 | + * Class Request | ||
13 | + * @package Lib\Imap\Request | ||
14 | + */ | ||
15 | +abstract class Request { | ||
16 | + | ||
17 | + | ||
18 | + public Imap $imap; | ||
19 | + | ||
20 | + | ||
21 | + /** | ||
22 | + * 命令执行结果 | ||
23 | + * @var array | ||
24 | + */ | ||
25 | + protected array $result = []; | ||
26 | + | ||
27 | + /** | ||
28 | + * 执行结果状态 YES OK NO BAD | ||
29 | + * @var string | ||
30 | + */ | ||
31 | + protected string $status = ''; | ||
32 | + | ||
33 | + /** | ||
34 | + * 这条命令执行的tag | ||
35 | + * @var string | ||
36 | + */ | ||
37 | + protected string $tag = ''; | ||
38 | + | ||
39 | + /** | ||
40 | + * 最后的消息 | ||
41 | + * @var string | ||
42 | + */ | ||
43 | + protected string $message = ''; | ||
44 | + | ||
45 | + | ||
46 | + public function __construct(Imap $imap) | ||
47 | + { | ||
48 | + $this->imap = $imap; | ||
49 | + } | ||
50 | + | ||
51 | + /** | ||
52 | + * 执行命令 | ||
53 | + * @param string $cmd 命令 | ||
54 | + * @param mixed ...$params 命令参数 | ||
55 | + * @author:dc | ||
56 | + * @time 2024/9/13 17:36 | ||
57 | + */ | ||
58 | + protected final function cmd(string $cmd,...$params){ | ||
59 | + $this->result = $this->imap->client->request($params?sprintf($cmd,...$params):$cmd); | ||
60 | + // 删除最后一行的状态 | ||
61 | + $end = array_pop($this->result); | ||
62 | + // 状态处理 | ||
63 | + list($this->tag,$this->status,$this->message) = explode(' ',$end.' ',3); | ||
64 | + $this->message = trim($this->message); | ||
65 | + | ||
66 | + } | ||
67 | + | ||
68 | + | ||
69 | + abstract public function exec():static ; | ||
70 | + | ||
71 | + | ||
72 | + /** | ||
73 | + * @return string | ||
74 | + */ | ||
75 | + public function getMessage(): string | ||
76 | + { | ||
77 | + return $this->message; | ||
78 | + } | ||
79 | + | ||
80 | + | ||
81 | + /** | ||
82 | + * @return string | ||
83 | + */ | ||
84 | + public function getStatus(): string | ||
85 | + { | ||
86 | + return $this->status; | ||
87 | + } | ||
88 | + | ||
89 | + | ||
90 | + /** | ||
91 | + * @return string | ||
92 | + */ | ||
93 | + public function getTag(): string | ||
94 | + { | ||
95 | + return $this->tag; | ||
96 | + } | ||
97 | + | ||
98 | + | ||
99 | + /** | ||
100 | + * 是否成功 | ||
101 | + * @return bool | ||
102 | + * @author:dc | ||
103 | + * @time 2024/9/14 9:10 | ||
104 | + */ | ||
105 | + public function isOk(){ | ||
106 | + return $this->status == 'OK'; | ||
107 | + } | ||
108 | + | ||
109 | +} |
lib/Imap/readme.md
0 → 100644
lib/Imap/test.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +// 实例一个配置 | ||
4 | +$config = (new \Lib\Imap\ImapConfig()) | ||
5 | + // 邮箱 | ||
6 | + ->setEmail('zhlong0616@163.com') | ||
7 | + // 密码 | ||
8 | + ->setPassword(base64_decode('VEVKSVdPTUJQS09TSFJHSg==')) | ||
9 | + // 必须是 协议+域名+端口 | ||
10 | + ->setHost('ssl://imap.163.com:993'); | ||
11 | + | ||
12 | +// 获取一个imap连接 必须传入 \Lib\Imap\ImapConfig类 | ||
13 | +// 返回的是 Lib\Imap\Imap 类 | ||
14 | +$imap = \Lib\Imap\ImapPool::get($config); | ||
15 | + | ||
16 | +// 登录 必须先登录 | ||
17 | +$login = $imap->login(); | ||
18 | +if($login->isOk()){// 登录成功 | ||
19 | + | ||
20 | + | ||
21 | + /******************** start 文件夹 ************************/ | ||
22 | + // 获取文件夹对象 | ||
23 | + $folders = $imap->getFolders(); | ||
24 | + | ||
25 | + // 获取 所有目录 | ||
26 | + foreach ($folders->all() as $folder){ | ||
27 | + $folder->getParseFolder(); // 获取解析过后的目录名称 | ||
28 | + $folder->folder; // 获取没有解析的目录名称 | ||
29 | + } | ||
30 | + // 获取顶级目录 | ||
31 | + foreach ($folders->getTopFolder() as $folder){ | ||
32 | + $childs = $folder->getChild(); // 获取 子级目录 | ||
33 | + foreach ($childs as $child){ | ||
34 | + $child->getChild(); | ||
35 | + } | ||
36 | + } | ||
37 | + | ||
38 | + // 创建一级文件夹 | ||
39 | + $imap->folder('你好',false)->create(); | ||
40 | + // 创建 二级文件夹 | ||
41 | + $imap->folder('你好/大家好',false)->create(); | ||
42 | + // 删除 你好 文件夹 | ||
43 | + $imap->folder('你好',false)->delete(); | ||
44 | + // 把 你好 改成 我好 | ||
45 | + $imap->folder('你好',false)->rename('我好',false); | ||
46 | + | ||
47 | + /******************** end 文件夹 ************************/ | ||
48 | + | ||
49 | + | ||
50 | + | ||
51 | + /************************ start 获取某个文件夹下面的邮件 *******************************/ | ||
52 | + // 选择文件夹 预备读取 使用分页 查询 | ||
53 | + $msgs = $imap->folder('INBOX')->msg()->forPage()->get(); | ||
54 | + // 1.使用foreach | ||
55 | + foreach ($msgs as $msg){ | ||
56 | + /** @var \Lib\Imap\Parse\MessageItem $msg **/ | ||
57 | + } | ||
58 | + // 2.使用 each | ||
59 | + $msgs->each(function (\Lib\Imap\Parse\MessageItem $msg){ | ||
60 | + $msg->msgno;// 邮件编号 | ||
61 | + $msg->uid;// 邮件唯一id | ||
62 | + $msg->size;// 邮件大小 | ||
63 | + $msg->date;// 邮件内部时间,也就是服务器收到邮件的时间 | ||
64 | + $msg->flags;// 邮件标签 已读,已回,星标,删除,草稿 最近 | ||
65 | + $msg->isAnswered();//已回 | ||
66 | + $msg->isDeleted();//删除 | ||
67 | + $msg->isDraft();//草稿 | ||
68 | + $msg->isFlagged();//星标 | ||
69 | + $msg->isRecent();//最近 | ||
70 | + $msg->isSeen();// 已读 | ||
71 | + /** 头部信息 @see \Lib\Imap\Parse\Header */ | ||
72 | + $msg->header->getSubject();// 已解析的主题 | ||
73 | + $msg->header->getFrom();// 已解析的发件人 | ||
74 | + $msg->header->getTo();// 已解析的收件人 | ||
75 | + // 获取某个字段 | ||
76 | + $msg->header->get('subject'); | ||
77 | + | ||
78 | + }); | ||
79 | + | ||
80 | + | ||
81 | + // 搜索邮箱 | ||
82 | + $msg = $imap->folder('INBOX')->msg()->search( | ||
83 | + // 搜索未读 并且 主题有 你好 的 | ||
84 | + (new \Lib\Imap\ImapSearch()) | ||
85 | + ->seen(false) | ||
86 | + ->subject('你好') | ||
87 | + )->get(); | ||
88 | + | ||
89 | + /*******************************************************/ | ||
90 | + | ||
91 | + | ||
92 | + | ||
93 | + | ||
94 | +}else{ | ||
95 | + // 登录失败 获取错误消息 $login->getMessage() | ||
96 | + throw new Exception($login->getMessage()); | ||
97 | +} | ||
98 | + | ||
99 | + | ||
100 | + | ||
101 | + | ||
102 | + |
-
请 注册 或 登录 后发表评论