wake-up-neo.com

E-Mail von PHP hat die Codierung des Betreff-Headers unterbrochen

Mein PHP Skript sendet E-Mails an Benutzer und wenn die E-Mails in ihren Postfächern ankommen, werden in der Betreffzeile ($subject) Zeichen wie a^£ Am Ende von hinzugefügt Mein Betreff-Text. Dies ist offensichtlich ein Codierungsproblem. Der Inhalt der E-Mail-Nachricht selbst ist in Ordnung, nur die Betreffzeile ist unterbrochen.

Ich habe überall gesucht, kann aber nicht finden wie ich mein Motiv richtig codiere.

Das ist mein Header. Beachten Sie, dass ich Content-Type Mit charset=utf-8 Und Content-Transfer-Encoding: 8bit Verwende.

//set all necessary headers
$headers = "From: $sender_name<$from>\n";
$headers .= "Reply-To: $sender_name<$from>\n";
$headers .= "X-Sender: $sender_name<$from>\n";
$headers .= "X-Mailer: PHP4\n"; //mailer
$headers .= "X-Priority: 3\n"; //1 UrgentMessage, 3 Normal
$headers .= "MIME-Version: 1.0\n";
$headers .= "X-MSMail-Priority: High\n";
$headers .= "Importance: 3\n";
$headers .= "Date: $date\n";
$headers .= "Delivered-to: $to\n";
$headers .= "Return-Path: $sender_name<$from>\n";
$headers .= "Envelope-from: $sender_name<$from>\n";
$headers .= "Content-Transfer-Encoding: 8bit\n";
$headers .= "Content-Type: text/plain; charset=UTF-8\n";
51
daza166

Update Um eine praktischere und aktuellere Antwort zu erhalten, werfen Sie einen Blick auf Palecs Antwort .


Die angegebene Zeichenkodierung in Content-Type beschreibt nur die Zeichenkodierung des Nachrichtentexts, nicht jedoch den Header. Sie müssen die encoded-Word -Syntax entweder mit der quoted-printable -Codierung oder mit der Base64-Codierung :

encoded-Word = "=?" charset "?" encoding "?" encoded-text "?="

Sie können imap_8bit für die quoted-printable Kodierung und base64_encode für die Base64-Codierung:

"Subject: =?UTF-8?B?".base64_encode($subject)."?="
"Subject: =?UTF-8?Q?".imap_8bit($subject)."?="
79
Gumbo

TL; DR

$preferences = ['input-charset' => 'UTF-8', 'output-charset' => 'UTF-8'];
$encoded_subject = iconv_mime_encode('Subject', $subject, $preferences);
$encoded_subject = substr($encoded_subject, strlen('Subject: '));
mail($to, $encoded_subject, $message, $headers);

oder

mb_internal_encoding('UTF-8');
$encoded_subject = mb_encode_mimeheader($subject, 'UTF-8', 'B', "\r\n", strlen('Subject: '));
mail($to, $encoded_subject, $message, $headers);

Problem und Lösung

Die Kopfzeilen Content-Type Und Content-Transfer-Encoding Gelten nur für den Hauptteil Ihrer Nachricht. Für Header gibt es einen Mechanismus zum Angeben ihrer Codierung, der in RFC 2047 angegeben ist.

Sie sollten Ihr Subject über iconv_mime_encode() kodieren, das ab PHP 5 existiert:

$preferences = ["input-charset" => "UTF-8", "output-charset" => "UTF-8"];
$encoded_subject = iconv_mime_encode("Subject", $subject, $preferences);

Ändern Sie input-charset So, dass es mit der Kodierung Ihrer Zeichenfolge $subject Übereinstimmt. Sie sollten output-charset Als UTF-8 Belassen. Verwenden Sie vor PHP 5.4 array() anstelle von [].

Jetzt ist $encoded_subject (Ohne Zeilenumbruch)

Subject: =?UTF-8?B?VmVyeSBsb25nIHRleHQgY29udGFpbmluZyBzcGVjaWFsIGM=?=
 =?UTF-8?B?aGFyYWN0ZXJzIGxpa2UgxJvFocSNxZnFvsO9w6HDrcOpPD4/PSsqIHA=?=
 =?UTF-8?B?cm9kdWNlcyBzZXZlcmFsIGVuY29kZWQtd29yZHMsIHNwYW5uaW5nIG0=?=
 =?UTF-8?B?dWx0aXBsZSBsaW5lcw==?=

für $subject mit:

Very long text containing special characters like ěščřžýáíé<>?=+* produces several encoded-words, spanning multiple lines

Wie funktioniert es?

Die Funktion iconv_mime_encode() teilt den Text, codiert jedes Stück einzeln in ein <encoded-Word> Token und folds das Leerzeichen zwischen ihnen. Das codierte Wort lautet =?<charset>?<encoding>?<encoded-text>?=, Wobei:

Sie können =?CP1250?B?QWhvaiwgc3bsdGU=?= In den UTF-8-String Ahoj, světe (Hello, world In tschechischer Sprache) über iconv("CP1250", "UTF-8", base64_decode("QWhvaiwgc3bsdGU=")) oder direkt über iconv_mime_decode("=?CP1250?B?QWhvaiwgc3bsdGU=?=", 0, "UTF-8") dekodieren.

Das Codieren in codierte Wörter ist komplizierter, da in der Spezifikation jedes codierte Wort-Token höchstens 75 Byte lang sein muss und jede Zeile, die ein codiertes Wort-Token enthält, höchstens 76 Byte lang sein muss (einschließlich Leerzeichen am Anfang einer Fortsetzungszeile) ). Implementieren Sie die Codierung nicht selbst. Alles, was Sie wirklich wissen müssen, ist, dass iconv_mime_encode() die Spezifikation einhält.

Interessante Literatur ist der Wikipedia-Artikel nicode und E-Mail .

Alternativen

Eine rudimentäre Option besteht darin, nur einen eingeschränkten Zeichensatz zu verwenden. ASCII funktioniert garantiert. ISO Latin 1 (ISO-8859-1), wie empfohlen von user2250504 , funktioniert wahrscheinlich auch, da es oft als Fallback verwendet wird, wenn Es ist keine Kodierung angegeben, aber diese Zeichensätze sind sehr klein und Sie können wahrscheinlich nicht alle gewünschten Zeichen kodieren. Außerdem sagen die RFCs nichts darüber aus, ob Latin 1 funktionieren soll oder nicht.

Sie können auch mb_encode_mimeheader() verwenden, wie antwortete Paul Norman , aber es ist einfach, es falsch zu verwenden.

  1. Sie müssen mb_internal_encoding() verwenden, um die intern verwendete Codierung der mbstring-Funktionen festzulegen. Die Funktionen mb_* Erwarten, dass sich Eingabezeichenfolgen in dieser Codierung befinden. Achtung: Der zweite Parameter von mb_encode_mimeheader() hat nichts mit der Eingabezeichenfolge zu tun (trotz der Anweisungen im Handbuch). Es entspricht dem <charset> Im verschlüsselten Wort (siehe Wie funktioniert es? oben). Die Eingabezeichenfolge wird von der internen Codierung in diese umcodiert, bevor sie an die B- oder Q-Codierung übergeben wird.

    Das Festlegen der internen Codierung ist möglicherweise nicht erforderlich, da PHP 5.6), da die zugrunde liegende Konfigurationsoption mbstring.internal_encoding zugunsten der Option default_charset Option, die standardmäßig auf UTF-8 eingestellt ist. Beachten Sie, dass dies nur eine Standardeinstellung ist und es möglicherweise unangemessen ist, sich auf Standardeinstellungen in Ihrem Code zu verlassen.

  2. Sie müssen den Kopfzeilennamen und den Doppelpunkt in die Eingabezeichenfolge einfügen. Der RFC legt eine starke Begrenzung der Zeilenlänge fest und muss auch für die erste Zeile gelten! Eine Alternative besteht darin, mit dem fünften Parameter zu experimentieren ($indent; Letzterer ab September 2015), dies ist jedoch noch unbequemer.

  3. Die Implementierung weist möglicherweise Fehler auf. Selbst bei korrekter Verwendung kann die Ausgabe unterbrochen werden. Zumindest sagen dies viele Kommentare auf der Handbuchseite. Es ist mir nicht gelungen, ein Problem zu finden, aber ich weiß, dass die Implementierung von codierten Wörtern schwierig ist. Wenn Sie potenzielle oder tatsächliche Fehler in mb_encode_mimeheader() oder iconv_mime_encode() finden, lassen Sie es mich bitte in den Kommentaren wissen.

Es gibt auch mindestens einen Vorteil bei der Verwendung von mb_encode_mimeheader(): Es werden nicht immer alle Header-Inhalte codiert, wodurch Platz gespart wird und der Text für den Menschen lesbar bleibt. Die Codierung ist nur für Nicht-ASCII-Teile erforderlich. Die Ausgabe analog zum obigen Beispiel iconv_mime_encode() ist:

Subject: Very long text containing special characters like
 =?UTF-8?B?xJvFocSNxZnFvsO9w6HDrcOpPD4/PSsqIHByb2R1Y2VzIHNldmVyYWwgZW5j?=
 =?UTF-8?B?b2RlZC13b3Jkcywgc3Bhbm5pbmcgbXVsdGlwbGUgbGluZXM=?=

Anwendungsbeispiel für mb_encode_mimeheader():

mb_internal_encoding('UTF-8');
$encoded_subject = mb_encode_mimeheader("Subject: $subject", 'UTF-8');
$encoded_subject = substr($encoded_subject, strlen('Subject: '));
mail($to, $encoded_subject, $message, $headers);

Dies ist eine Alternative zum Snippet in TL; DR oben in diesem Beitrag. Anstatt nur den Platz für Subject: Zu reservieren, wird er tatsächlich dort abgelegt und dann entfernt, um ihn mit der dummen Schnittstelle von mail() verwenden zu können.

Wenn Sie mbstring-Funktionen besser mögen als die iconv-Funktionen, können Sie mb_send_mail() verwenden. Es verwendet mail() intern, codiert jedoch Betreff und Text der Nachricht automatisch. Wieder mit Sorgfalt verwenden .

Andere Überschriften als Subject müssen anders behandelt werden

Beachten Sie, dass Sie nicht davon ausgehen müssen, dass die Codierung des gesamten Inhalts eines Headers für alle Header, die möglicherweise Nicht-ASCII-Zeichen enthalten, in Ordnung ist. Z.B. From, To, Cc, Bcc und Reply-To können Namen für die darin enthaltenen Adressen enthalten, es können jedoch nur die Namen codiert werden, nicht die Adressen. Der Grund dafür ist, dass <encoded-Word> - Token möglicherweise nur <text> -, <ctext> - und <Word> - Token ersetzen, und dies nur unter bestimmten Umständen (siehe §5 von RFC 2047 ).

Das Kodieren von Nicht-ASCII-Text in anderen Kopfzeilen ist eine verwandte, aber andere Frage. Wenn Sie mehr über dieses Thema erfahren möchten, suchen Sie. Wenn Sie keine Antwort finden, stellen Sie eine andere Frage und weisen Sie mich in den Kommentaren darauf hin.

56
Palec

mb_encode_mimeheader () für UTF-8-Zeichenfolgen kann hier nützlich sein, z.

$subject = mb_encode_mimeheader($subjectText,"UTF-8");
18
Paul Norman