2015-09-29 32 views
0

Fatal error: Uncaught exception 'Zend\Mail\Exception\RuntimeException' with message 'Malformed header detected' in /var/www/html/plugins/ZendFramework/ZendFramework-2.4.7/library/Zend/Mail/Headers.php on line 88ZendMail - 标题错误

这是我尝试使用Zend\Mail\Storage\Imap::getMessage()函数时得到的错误消息。如果我注释掉这一行,错误被引发(更准确地说,我必须注释掉这两行,这个错误是从同一个函数抛出的),我可以读取这个消息的主题,但后来一些电子邮件,我得到一个:

Fatal error: Uncaught exception 'Zend\Mail\Header\Exception\InvalidArgumentException' with message 'Invalid header value detected' in /var/www/html/plugins/ZendFramework/ZendFramework-2.4.7/library/Zend/Mail/Header/GenericHeader.php on line 61

正如我在测试该插件,我没有使用任何其他分机或插件,而我得到了这些错误,只有Xdebug的PHP扩展,用于调试。这里是我的全部代码:

try { 
$mail = new Zend\Mail\Storage\Imap(array('host'  => 'my.mail.host', 
            'user'  => '[email protected]', 
            'password' => 'xxxxxxxxx')); 
} 
catch (Zend\Mail\Exception $e) { 
print_r($e); 
} 

$undecodable=array(); 
$mail->selectFolder('INBOX'); 

$list=array(1,2,3,4); 

foreach ($list as $mid) { 
$message=$mail->getMessage($mid); 
$content=array(
    'html' => array(
     'part'  => null, 
     'content' => null 
    ), 
    'plain' => array(
     'part'  => null, 
     'content' => null 
    ) 
); 
foreach (new RecursiveIteratorIterator($message) as $part) { 
    try { 
     $strtok=strtok($part->contentType, ';'); 
     if (($strtok == 'text/html') || ($strtok == 'text/plain')) { 
      switch ($part->contentTransferEncoding) { 
       case 'base64': 
        $contentDecoded = base64_decode($part->getContent()); 
        break; 
       case 'quoted-printable': 
        $contentDecoded = quoted_printable_decode($part->getContent()); 
        break; 
       case 'default': 
        $contentDecoded='undecodable'; 
        $undecodable[]=$part->contentTransferEncoding; 
        break; 
      } 
      $strtok=explode("/",$strtok); 
      $strtok=$strtok[1]; 
      $content[$strtok]['part']=$part; 
      $content[$strtok]['content']=$contentDecoded; 
     } 
    } catch (Zend\Mail\Exception $e) { 
     // ignore 
    } 
} 
echo "Message: " . $message->subject."<BR>"; 
if (!empty($content['html']['content'])) echo "HTML part found! "; 
if (!empty($content['plain']['content'])) echo "Plain part found! "; 
echo "<BR><BR>Next message"; 
} 
exit; 

注:消息ID是正确的,我可以通过远程登录邮件服务器获取所有的,他们都存在

回答

0

这是出现在Zend的\同样的问题Mail \ Message :: fromString()方法。该方法实际上正常 - 因为它采用RAW消息并创建该对象。但是,当您尝试使用类方法访问任何消息标题时,问题就会开始。标题获取器以延迟加载模式实现 - 因此,一旦对象由Zend \ Mail \ Message :: fromString()方法创建,事实对象包含“错误”标题就不会被识别。

在我的情况下,问题出现在标题包含本地字符后(对于波兰的例子可能是:¡,ć,,,ą,等等 - 其他国家可能有或多或少的这种类型)。我正在使用不同的邮件服务器(Exchange,sendmail,Lotus)和不同的电子邮件客户端(Outlook,Thunderbird,Windows Mail)检查发送和接收的邮件 - 这些字符出现的标题非常不同 - 例如交换(或Outlook - 没有检查在100%:) :)是添加主题线程头(据我所知的消息线程)和完整的主题放在那里 - 所以这个头,其他(如Topic)也曾经试图从对象中检索异常。

Zend \ Mail \ Message :: fromString()方法的问题在于,它实际上不允许指定从RAW消息中检索的数据的编码。它只需要将数据存入对象内的某个存储器中,并且数据本身在延迟加载的基础上进行检索。 因此,一旦您尝试检索标头 - getter将抛出异常,数据格式错误。

我做的是创建一个继承类,并改变Zend \ Mail \ Message :: fromString()方法的工作方式。请看以下代码:

public static function fromString(string $rawMessage, string $encoding = 'UTF-8') { 
     $message = parent::fromString($rawMessage); 
     foreach($message->getHeaders()->toArray() as $headerName => $headerValue) { 
      try { 
       $message->getHeaders()->get($headerName); 
      } catch (\Zend\Mail\Header\Exception\InvalidArgumentException $e) { // catches only if Header is wrongly structured 
       $message->getHeaders()->removeHeader($headerName); 
       $header = new \Zend\Mail\Header\GenericHeader(); 
       $header->setEncoding($encoding); 
       $header->setFieldName($headerName); 
       $header->setFieldValue($headerValue); 
       $message->getHeaders()->addHeader($header); 
      } 
     } 
     $message->getHeaders()->setEncoding($encoding); // All headers are encoded in $encoding 
     return $message; 
    } 

所以,你可以看到这里是我引入额外的参数$编码这种方法,可以让你指定邮件编码。该参数的默认值为UTF-8 - 主要是方法将向下兼容(尽管PHP正在抛出一个通知它不是:))。 然后它触发标准,父方法来创建一个对象 - 正如我之前描述的那样,它不会失败:) 然后我们有一个代码试图“清理”头 - 通过调用它们中的每个人并捕获InvalidArgumentException if抛出。如果引发此异常(并通过try ... catch ...代码捕获) - 头文件将被删除并重新创建,但使用正确的$编码。 所有的头部被检查并且可能被正确编码的头部替换 - 我再次调用setEncoding()方法针对所有头部 - 对于在foreach(){try ... catch中没有被“替换”的所有头部。 。}块 - 因此所有的头文件都将以相同的编码进行编码。 我知道你可能会认为你为什么不简单地对所有现有的头文件执行这个setEncoding()方法?那么 - 我试过了 - 这个也触发了异常:)所以我假设,该方法只是调用每个头,但一旦调用 - 延迟加载导致抛出异常。

整个想法也在以下Article中描述。