imap_thread

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

imap_threadReturns a tree of threaded message

说明

imap_thread ( resource $imap , int $flags = SE_FREE ) : array|false

Gets a tree of a threaded message.

参数

imap

imap_open() 返回的 IMAP 流。

flags

返回值

imap_thread() returns an associative array containing a tree of messages threaded by REFERENCES, or false on error.

Every message in the current mailbox will be represented by three entries in the resulting array:

  • $thread["XX.num"] - current message number

  • $thread["XX.next"]

  • $thread["XX.branch"]

范例

Example #1 imap_thread() Example

<?php

// Here we're outputting the threads of a newsgroup, in 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);

?>

User Contributed Notes

p2 at eduardoruiz dot es 21-Sep-2020 01:36
Fix
XX.num maybe 0, then stop

<?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 (Spain)
p2 at eduardoruiz dot es 19-Sep-2020 07:20
Get all UIDs of a single thread (not the whole all messages threads).
If you want to list just a conversation thread.

First, we must find the root element, using an UID included in these conversation in whatever message.

Then recursivelly we list all UIDs messages in an array.

<?php
// Function returns empty array if message is not in a thread
// You must indicate $mailbox returned by imap_open and a message UID listed in that thread conversation
// First root element is found when parent and previous parent are both with 'XX.next' elements equal to 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;
}

// Walk messages in 2 ways, using 'XX.branch' and recursivelly 'XX.next' for sibiling and children messages
// Stop when in first iteration(call) there is no 'XX.next' element

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))) // Example UID message
   
print_r(imap_fetch_overview($mailbox, implode(',', $mails), SE_UID));
?>

Eduardo Ruiz (Spain)
cblanquera at gmail dot com 19-May-2011 08:13
imap_thread() returns threads, but are confined to the current open mailbox you defined in imap_open(). This is not useful for, lets say, getting full threads ( from "Sent Messages" and "Inbox" [took me a day to figure this out]).

If you compare threads on Outlook vs gmail.com you will find that Outlook determines threads by subject title, not actual parent > child relationships.

Gmail however, seems to get threads right, but does not include mail you send using their web interface in  {imap.google.com:993/imap/ssl}Sent Messages . What this means is that threads using php imap won't be perfect for gmail.

If you send mail using Outlook (or any mail client), gmail.com does put it in their "Sent Mail".

So all in all, threads for PHP imap are not perfect. But I blame the imap specifications (DEAR IMAP GUYS, please add better uids and parent ids. thx Chris) more than PHP

So I created the Outlook method for threading (comparing subjects) below:

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

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

//search for subject in current mailbox
$results = imap_search($imap, 'SUBJECT "'.$subject.'"', SE_UID);

//because results can be false
if(is_array($results)) {
   
//now get all the emails details that were found
   
$emails = imap_fetch_overview($imap, implode(',', $results), FT_UID);
   
   
//foreach email
   
foreach ($emails as $email) {
       
//add to threads
        //we date date as the key because later we will sort it
       
$threads[strtotime($email->date)] = $email;
    }   
}

//now reopen sent messages
imap_reopen($imap, '{imap.gmail.com:993/imap/ssl}Sent Messages');

//and do the same thing

//search for subject in current mailbox
$results = imap_search($imap, 'SUBJECT "'.$subject.'"', SE_UID);

//because results can be false
if(is_array($results)) {
   
//now get all the emails details that were found
   
$emails = imap_fetch_overview($imap, implode(',', $results), FT_UID);
   
   
//foreach email
   
foreach ($emails as $email) {
       
//add to threads
        //we date date as the key because later we will sort it
       
$threads[strtotime($email->date)] = $email;
    }   
}

//sort keys so we get threads in chronological order
ksort($threads);

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

so if you are going to use imap_thread() for something useful. This is probably the most optimal way I can think of:

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

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

//first we find the root (or parent) value for each email in the thread
//we ignore emails that have no root value except those that are infact
//the root of a thread

//we want to gather the message IDs in a way where we can get the details of
//all emails on one call rather than individual calls ( for performance )

//foreach thread
foreach ($thread as $i => $messageId) {
   
//get sequence and type
   
list($sequence, $type) = explode('.', $i);
   
   
//if type is not num or messageId is 0 or (start of a new thread and no next) or is already set
   
if($type != 'num' || $messageId == 0
      
|| ($root == 0 && $thread[$sequence.'.next'] == 0)
       || isset(
$rootValues[$messageId])) {
       
//ignore it
       
continue;
    }
   
   
//if this is the start of a new thread
   
if($root == 0) {
       
//set root
       
$root = $messageId;
    }
   
   
//at this point this will be part of a thread
    //let's remember the root for this email
   
$rootValues[$messageId] = $root;
   
   
//if there is no next
   
if($thread[$sequence.'.next'] == 0) {
       
//reset root
       
$root = 0;
    }
}

//now get all the emails details in rootValues in one call
//because one call for 1000 rows to a server is better
//than calling the server 1000 times 
$emails = imap_fetch_overview($imap, implode(',', array_keys($rootValues)));

//foreach email
foreach ($emails as $email) {
   
//get root
   
$root = $rootValues[$email->msgno];
   
   
//add to threads
   
$threads[$root][] = $email;
}   

//there is no need to sort, the threads will automagically in chronological order
echo '<pre>'.print_r($threads, true).'</pre>';
imap_close($imap);
exit;
?>
whamill at google mail 26-Apr-2010 02:23
I figure other people may benefit from an explanation of the resulting array, My understanding is:

Key: Essentially a node ID
Num: The mail ID (where 0 indicates the start of a conversation)
Next: The node ID of the first child (where 0 indicates there are no children)
Branch: The node ID of the next sibling (where 0 indicates there is no sibling)
mail at moritz-lapp dot de 05-Aug-2004 01:47
One possible option to imap_thread is the constant SE_UID, which will make imap_thread return UIDs instead of sequence numbers in the x.num-fields of the returned array.