PHP Conference Japan 2024

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);

?>

添加注释

用户贡献的注释 2 条注释

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

如果你比较 Outlook 和 gmail.com 上的线程,你会发现 Outlook 根据主题标题来确定线程,而不是实际的父子关系。

然而,Gmail 似乎正确地获取了线程,但是不包括你使用他们的 Web 界面在 {imap.google.com:993/imap/ssl}Sent Messages 中发送的邮件。这意味着使用 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 表示没有兄弟节点)
To Top