imap_thread

(PHP 4 >= 4.0.7, PHP 5, PHP 7, PHP 8)

imap_thread返回线程消息树

描述

imap_thread(IMAP\Connection $imap, int $flags = SE_FREE): array|false

获取线程消息的树结构。

参数

imap

一个 IMAP\Connection 实例。

flags

返回值

imap_thread() 返回一个关联数组,其中包含通过 REFERENCES 线程化的消息树,如果发生错误,则返回 false

当前邮箱中的每条消息都将在结果数组中用三个条目表示

  • $thread["XX.num"] - 当前消息编号

  • $thread["XX.next"]

  • $thread["XX.branch"]

变更日志

版本 描述
8.1.0 imap 参数现在期望一个 IMAP\Connection 实例;之前,它期望一个有效的 imap 资源

示例

示例 #1 imap_thread() 示例

<?php

// 这里我们以 HTML 格式输出新闻组的线程

$nntp = imap_open('{news.example.com:119/nntp}some.newsgroup', '', '');
$threads = imap_thread($nntp);

foreach (
$threads as $key => $val) {
$tree = explode('.', $key);
if (
$tree[1] == 'num') {
$header = imap_headerinfo($nntp, $val);
echo
"<ul>\n\t<li>" . $header->fromaddress . "\n";
} elseif (
$tree[1] == 'branch') {
echo
"\t</li>\n</ul>\n";
}
}

imap_close($nntp);

?>

添加笔记

用户贡献的笔记 5 notes

18
cblanquera at gmail dot com
13 年前
imap_thread() 返回线程,但仅限于您在 imap_open() 中定义的当前打开的邮箱。这对例如获取完整线程(来自“已发送邮件”和“收件箱”[我花了一天才弄明白])没有用。

如果您将 Outlook 和 gmail.com 上的线程进行比较,您会发现 Outlook 通过主题标题来确定线程,而不是实际的父 > 子关系。

然而,Gmail 似乎正确地获得了线程,但不会在 {imap.google.com:993/imap/ssl}Sent Messages 中包含您使用其 Web 界面发送的邮件。这意味着使用 php imap 的线程对于 Gmail 来说并不完美。

如果您使用 Outlook(或任何邮件客户端)发送邮件,gmail.com 确实会将其放入其“已发送邮件”中。

总而言之,PHP imap 的线程并不完美。但我更责怪 imap 规范(亲爱的 IMAP 人员,请添加更好的 uid 和父 id。谢谢 Chris),而不是 PHP

因此,我在下面创建了 Outlook 线程方法(比较主题)

<?php
$imap
= imap_open('{imap.gmail.com:993/imap/ssl}INBOX', '[email protected]', 'yourpassword');
$subject = 'Item b';
$threads = array();

// 移除 re: 和 fwd:
$subject = trim(preg_replace("/Re\:|re\:|RE\:|Fwd\:|fwd\:|FWD\:/i", '', $subject));

// 在当前邮箱中搜索主题
$results = imap_search($imap, 'SUBJECT "'.$subject.'"', SE_UID);

// 因为结果可能是 false
if(is_array($results)) {
// 现在获取所有找到的邮件详细信息
$emails = imap_fetch_overview($imap, implode(',', $results), FT_UID);

// 遍历每封邮件
foreach ($emails as $email) {
// 添加到线程
// 我们将日期作为键,因为稍后我们将对其进行排序
$threads[strtotime($email->date)] = $email;
}
}

// 现在重新打开已发送邮件
imap_reopen($imap, '{imap.gmail.com:993/imap/ssl}Sent Messages');

// 并执行相同的操作

// 在当前邮箱中搜索主题
$results = imap_search($imap, 'SUBJECT "'.$subject.'"', SE_UID);

// 因为结果可能是 false
if(is_array($results)) {
// 现在获取所有找到的邮件详细信息
$emails = imap_fetch_overview($imap, implode(',', $results), FT_UID);

// 遍历每封邮件
foreach ($emails as $email) {
// 添加到线程
// 我们将日期作为键,因为稍后我们将对其进行排序
$threads[strtotime($email->date)] = $email;
}
}

// 按时间顺序对键进行排序,以便我们按时间顺序获取线程
ksort($threads);

echo
'<pre>'.print_r($threads, true).'</pre>';
exit;
?>

因此,如果您要将 imap_thread() 用于有用的用途。这可能是我能想到的最优方法

<?php
$imap
= imap_open('{imap.gmail.com:993/imap/ssl}INBOX', '[email protected]', 'password');
$threads = $rootValues = array();

$thread = imap_thread($imap);
$root = 0;

// 首先我们找到每个邮件在邮件线程中的根(或父)值
// 我们忽略没有根值的邮件,除了那些实际上是
// 线程的根

// 我们希望以一种方式收集邮件 ID,以便我们可以获得详细信息
// 所有邮件在一个调用中而不是单独调用(为了性能)

// 对于每个线程
foreach ($thread as $i => $messageId) {
// 获取序列和类型
list($sequence, $type) = explode('.', $i);

// 如果类型不是 num 或 messageId 为 0 或(新线程的开始且没有下一个)或已设置
if($type != 'num' || $messageId == 0
|| ($root == 0 && $thread[$sequence.'.next'] == 0)
|| isset(
$rootValues[$messageId])) {
// 忽略它
continue;
}

// 如果这是新线程的开始
if($root == 0) {
// 设置根
$root = $messageId;
}

// 在这一点上,这将成为线程的一部分
// 让我们记住这封邮件的根
$rootValues[$messageId] = $root;

// 如果没有下一个
if($thread[$sequence.'.next'] == 0) {
// 重置根
$root = 0;
}
}

// 现在在一次调用中获取 rootValues 中所有邮件的详细信息
// 因为对服务器的 1000 行进行一次调用比调用服务器 1000 次更好
$emails = imap_fetch_overview($imap, implode(',', array_keys($rootValues)));

// 对于每个邮件
foreach ($emails as $email) {
// 获取根
$root = $rootValues[$email->msgno];

// 添加到线程
$threads[$root][] = $email;
}

// 不需要排序,线程将自动按时间顺序排列
echo '<pre>'.print_r($threads, true).'</pre>';
imap_close($imap);
exit;
?>
3
whamill at google mail
14 年前
我认为其他人可能会从对结果数组的解释中受益,我的理解是

键:本质上是一个节点 ID
Num:邮件 ID(其中 0 表示对话的开始)
Next:第一个子节点的节点 ID(其中 0 表示没有子节点)
Branch:下一个兄弟节点的节点 ID(其中 0 表示没有兄弟节点)
0
p2 at eduardoruiz dot es
3 年前
获取单个线程(不是整个所有邮件线程)的所有 UIDs。
如果您想仅列出对话线程。

首先,我们必须使用包含在此对话中的任何邮件中的 UID 找到根元素。

然后,我们递归地将所有 UID 邮件列在一个数组中。

<?php
// 如果邮件不在线程中,则函数返回空数组
// 您必须指定 imap_open 返回的 $mailbox 和该线程对话中列出的邮件 UID
// 找到第一个根元素时,父元素和之前的父元素的 'XX.next' 元素都等于 0

function FindThread($mailbox, $uid) {

$mails = array();
$thread = imap_thread($mailbox, SE_UID);

$i = array_search($uid, $thread);
while((
$tree = explode('.', $i)) && ($j = array_search($tree[0], $thread)) &&
(
$treej = explode('.', $j)) && $treej[0] && ($k = array_search($treej[0], $thread)) && ($treek = explode('.', $k)) &&
(
$thread[$treej[0] . '.next'] != 0 || $thread[$treek[0] . '.next'] != 0))
$i = $j;

$mails = FindThreadAll($thread, $tree[0]);

return
$mails;
}

// 通过两种方式遍历邮件,使用 'XX.branch' 和递归地 'XX.next' 用于兄弟和子邮件
// 当在第一次迭代(调用)中没有 'XX.next' 元素时停止

function FindThreadAll(&$thread, $i, $n = 0) {

$mails = array();
while(
$i) {

if(!(
$j = $thread[$i . '.next']) && !$n)
break;

$mails[] = $thread[$i . '.num'];
if(
$j)
$mails = array_merge($mails, FindThreadAll($thread, $j, $n + 1));

$i = $thread[$i . '.branch'];
}

return
$mails;
}

if((
$mails = FindThread(64851))) // 例如 UID 邮件
print_r(imap_fetch_overview($mailbox, implode(',', $mails), SE_UID));
?>

Eduardo Ruiz(西班牙)
-1
p2 at eduardoruiz dot es
3 年前
修复
XX.num 可能为 0,然后停止

<?php
function FindThread(&$mailbox, $uid) {

$mails = array();
$thread = imap_thread($mailbox, SE_UID);

$i = array_search($uid, $thread);
while((
$tree = explode('.', $i)) && ($j = array_search($tree[0], $thread)) &&
(
$treej = explode('.', $j)) && $treej[0] && ($k = array_search($treej[0], $thread)) && ($treek = explode('.', $k)) &&
$thread[$treej[0] . '.num'] && $thread[$treek[0] . '.num'] &&
(
$thread[$treej[0] . '.next'] != 0 || $thread[$treek[0] . '.next'] != 0))
$i = $j;

$mails = FindThreadAll($thread, $tree[0]);

return
$mails;
}
?>

Eduardo Ruiz(西班牙)
-2
mail at moritz-lapp dot de
20 年前
imap_thread 的一种可能选项是常量 SE_UID,它将使 imap_thread 返回 UID 而不是返回数组的 x.num 字段中的序列号。
To Top