PHP从多维数组(IMAP)创建多维消息线程数组

jku*_*ner 5 php email imap

我的问题如下:

如果你看下面你会看到有一个带有消息ID的数据结构,然后是包含应该聚合的消息详细信息的最终数据结构imap_fetch_overview.消息ID来自imap_thread.问题是它没有将电子邮件详细信息放在消息ID所在的位置.

这是我的数据结构:

[5] => Array
    (
        [0] => 5
        [1] => 9
    )

[10] => Array
    (
        [0] => 10
        [1] => 11
    )
Run Code Online (Sandbox Code Playgroud)

我想拥有的是:

[5] => Array
    (
        [0] => messageDetails for id 5
        [1] => messageDetails for id 9
    )

[10] => Array
    (
        [0] => messageDetails for id 10
        [1] => messageDetails for id 11
    )
Run Code Online (Sandbox Code Playgroud)

这是我到目前为止的代码:

$emails = imap_fetch_overview($imap, implode(',',$ids));

// root is the array index position of the threads message, such as 5 or 10
foreach($threads as $root => $messages){

    // id is the id being given to us from `imap_thread`
    foreach($message as $key => $id){

      foreach($emails as $index => $email){

         if($id === $email->msgno){
             $threads[$root][$key] = $email;
             break;
          }
      }
    }
 }
Run Code Online (Sandbox Code Playgroud)

这是来自$ email之一的打印输出:

    [0] => stdClass Object
    (
        [subject] => Cloud Storage Dump
        [from] => Josh Doe
        [to] => jondoe@domain.com
        [date] => Mon, 21 Jan 2013 23:18:00 -0500
        [message_id] => <50FE12F8.9050506@domain.com>
        [size] => 2559
        [uid] => 5
        [msgno] => 5
        [recent] => 0
        [flagged] => 0
        [answered] => 1
        [deleted] => 0
        [seen] => 0
        [draft] => 0
        [udate] => 1358828308
    )
Run Code Online (Sandbox Code Playgroud)

如果你注意到,msgno是5,它会腐蚀到$id,所以从技术上讲,数据应该填充到最终的数据结构中.

而且,这似乎是一种处理此问题的低效方法.

如果您需要任何其他说明,请告诉我.

更新代码

这段代码是我在php api上找到的代码和我的一些修复的组合.我认为有问题的仍然是$root.

$addedEmails = array();
$thread = imap_thread($imap);
foreach ($thread as $i => $messageId) { 
    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(in_array($messageId, $addedEmails)){
    continue;
}
array_push($addedEmails,$messageId);

//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; 
    } 
  }
$ids=array();
$threads = array();
foreach($rootValues as $id => $root){
    if(!array_key_exists($root,$threads)){
        $threads[$root] = array();
    }
    if(!in_array($id,$threads[$root])){
        $threads[$root][] = $id;
       $ids[]=$id;
    }
 }
 $emails = imap_fetch_overview($imap, implode(',', array_keys($rootValues)));

 $keys = array();
 foreach($emails as $k => $email)
 {
$keys[$email->msgno] = $k;
 }

 $threads = array_map(function($thread) use($emails, $keys)
{
// Iterate emails in these threads
return array_map(function($msgno) use($emails, $keys)
{
    // Swap the msgno with the email details
    return $emails[$keys[$msgno]];

}, $thread);
}, $threads);
Run Code Online (Sandbox Code Playgroud)

Woo*_*Dzu 4

请记住,在 php 中,无论您使用什么函数,它最终都会转换为某种循环。不过,您可以采取一些步骤来提高效率,这些步骤在 PHP 5.5 和 5.3/5.4 中是不同的。

PHP 5.3/5.4方式

最有效的方法是将函数拆分为两个单独的步骤。第一步,您将为电子邮件列表生成键映射。

$keys = array();
foreach($emails as $k => $email)
{
    $keys[$email->msgno] = $k;
}
Run Code Online (Sandbox Code Playgroud)

在第二步中,您迭代多维 $threads 中的所有值并将它们替换为电子邮件详细信息:

// Iterate threads
$threads = array_map(function($thread) use($emails, $keys)
{
    // Iterate emails in these threads
    return array_map(function($msgno) use($emails, $keys)
    {
        // Swap the msgno with the email details
        return $emails[$keys[$msgno]];

    }, $thread);

}, $threads);
Run Code Online (Sandbox Code Playgroud)

概念证明:http://pastebin.com/rp5QFN4J

匿名函数中关键字使用说明:

为了使用父作用域中定义的变量,可以使用use ()关键字将父作用域中的变量导入到闭包作用域中。尽管它是在 PHP 5.3 中引入的,但尚未在官方 PHP 手册中记录。php 的 wiki 上只有一份草稿文档https://wiki.php.net/rfc/closures#userland_perspective

PHP 5.5

此版本中的新功能之一使您能够使用生成器,它的内存指纹要小得多,因此效率更高。

生成器中关键字yield的解释:

生成器函数的核心是yield关键字。从最简单的形式来看,yield 语句看起来很像 return 语句,不同之处在于,yield 不是停止执行函数并返回,而是为循环生成器的代码提供一个值,并暂停生成器函数的执行。

第一步:

function genetateKeyMap($emails)
{
    foreach($emails as $k => $email)
    {
        // Yielding key => value pair to result set
        yield $email->msgno => $k;
    }
};
$keys = iterator_to_array(genetateKeyMap($emails));
Run Code Online (Sandbox Code Playgroud)

第二步:

function updateThreads($emails, $threads, $keys)
{
    foreach($threads as $thread)
    {
        $array = array();

        // Create a set of detailed emails
        foreach($thread as $msgno)
        {
            $array[] = $emails[$keys[$msgno]];
        }

        // Yielding array to result set
        yield $array;
    }
};
$threads = iterator_to_array(updateThreads($emails, $threads, $keys));
Run Code Online (Sandbox Code Playgroud)

关于生成器返回的值的几句话:

生成器返回一个对象,该对象是 SPL Iterator 的实例,因此它需要使用 iterator_to_array() 才能将其转换为与代码所期望的完全相同的数组结构。您不需要这样做,但需要在生成器函数之后更新代码,这可能会更有效。

概念证明:http://pastebin.com/9Z4pftBH

测试性能:

我生成了 7000 个线程的列表,每个线程有 5 条消息,并测试了每种方法的性能(5 次测试的平均值):

                   Takes:       Memory used:
                   ----------------------------
3x foreach():      2.8s              5.2 MB
PHP 5.3/5.4 way    0.061s            2.7 MB
PHP 5.5 way        0.036s            2.7 MB
Run Code Online (Sandbox Code Playgroud)

尽管您的计算机/服务器上的结果可能有所不同,但概述表明 2 步方法比使用 3 个 foreach 循环快大约 45-77 倍

测试脚本: http: //pastebin.com/M40hf0x7