可以';我不明白为什么Zend_Mail::addHeader()会删除换行符


Can't understand why Zend_Mail::addHeader() strips newlines

(由于这是我的第一个SO问题,我只想说,我希望它不是太针对Zend。据我所知,这应该不是一个问题。虽然我本可以在Zend特定的论坛上发布它,但我觉得我在这里至少有可能得到一个好的答案,特别是因为答案可能涉及超越Zend框架的MIME相关问题我面临的问题应该被视为ZF错误,或者如果我误解或滥用了它。)

我一直在使用Zend_Mail构建一个MIME消息,该消息通过电子邮件分发服务SendGrid发送。他们的平台允许你通过他们的SMTP服务器发送电子邮件,但当你使用一个特殊的标头(X-SMTPAPI)时,它提供了额外的功能,该标头的值是一个JSON编码的专有参数字符串,可能会很长。

最终,我传递的标题太长了(我认为超过1000个字符),我出现了错误。我很困惑,因为我知道在将值传递给Zend_Mail::addHeader()之前,它是通过PHP的原生wordwrap()函数传递的,所以我认为行长度永远不会成为问题。

事实证明,addHeader()非常刻意地删除换行符,并且没有通过注释的方式进行特别解释。

// In Zend_Mail::addHeader()
$value = $this->_filterOther($value);

// In Zend_Mail::_filterOther()
$rule = array("'r" => '',
              "'n" => '',
              "'t" => '',
);
return strtr($data, $rule);

好吧,一开始这似乎是合理的——也许ZF想要完全控制格式和换行。Zend_Mail::addHeader()中调用的下一个方法是

$value = $this->_encodeHeader($value);

此方法对值进行编码(根据情况,可以是带引号的可打印字符,也可以是base64),并将其分块为适当长度的行,但如果它包含"不可打印字符",则,由Zend_Mime::isPrintable($value)确定。

考虑到这种方法,换行符(''n)确实被认为是不可打印的字符!因此,如果在之前的方法调用中没有将它们从字符串中剥离出来,那么长标头将被编码为QP并分块为72个字符行,一切都会正常工作。事实上,我做了一个测试,注释掉了对_filterOther()的调用,长头被编码并顺利通过。但现在我只是对ZF进行了一次不小心的黑客攻击,没有真正理解我删除的行背后的目的,所以这不可能是一个长期的解决方案。

我的中期解决方案是扩展Zend_Mail并创建一个新方法addHeaderForceEncode(),该方法将始终对标头的值进行编码,从而始终将其分块为短行。但我仍然不满意,因为我不明白为什么一开始就需要_filterOther()调用——也许我根本不应该处理它。

有人能向我解释一下为什么存在这种剥离换行符的行为吗?这似乎不可避免地会导致标题过长的情况,如果它不包含除换行符之外的任何"不可打印字符"。

我在这个主题上做了很多不同的搜索,并查看了一些ZF错误报告,但没有看到任何人谈论这个。令人惊讶的是,这似乎是一个非常模糊的问题。仅供参考,我与ZF1.11.11合作。


更新:如果有人想关注我打开的ZF问题,这里是:Zend_Mail::addHeader()取消折叠长标题,然后抛出异常

您可能会遇到一些问题。根据RFC 2821,SMTP中的文本行不能超过1000个字符:

文本行

包含的文本行的最大总长度为1000个字符(不包括重复的前导点透明度)。此数字可能会通过使用SMTP而增加服务扩展。

标头不能包含换行符,所以这可能就是Zend剥离换行符的原因。对于长标头,通常插入换行符(SMTP中的CRLF)和制表符来"换行"它们。

摘录自RFC 822:

每个标题字段都可以看作是ASCII字符,包括字段名和字段体。为了方便起见,此概念的字段主体部分实体可以拆分为多行表示;这称为"折叠"。一般规则是,无论在哪里可以是线性空白(而不仅仅是LWSP字符),CRLF紧随其后的至少一个LWSP字符可以改为插入。

我想说的是,_encodeHeader()函数可能应该查看行长度,如果标题比某个神奇的值长,请执行"换行和制表"使其跨越多行。