webew
Войти » Регистрация
 
PHP

Отправка e-mail в русской кодировке средствами PHP

17 апреля 2008, 23:22
Автор: Григорий Рубцов [rgbeast]

Стандартная функция отправки почты в PHP не заботится о кодировке письма, а специальные классы отправки писем могут показаться слишком тяжеловесным решением. В настоящей статье приведена простая функция, которая позволяет отправлять MIME-совместимые письма на русском языке.

Тело письма может быть текстом в любой кодировке. Немного сложнее дело обстоит с составляющими заголовка письма (Subject, From, To), они по стандарту должны быть в US-ASCII. Использование в заголовках кодированного текста регламентируется стандартом RFC-2047. Стандарт задает две возможные формы кодирования - BASE64 и Quoted-Printable. BASE64 более универсальна и работает в том числе для кодировки UTF-8, поэтому мы выбрали именно эту форму. MIME-совместимый почтовый клиент обязан уметь читать обе формы.

Далее код функции, который достаточно схематичен:

<?php

function send_mime_mail($name_from, // имя отправителя
                        $email_from, // email отправителя
                        $name_to, // имя получателя
                        $email_to, // email получателя
                        $data_charset, // кодировка переданных данных
                        $send_charset, // кодировка письма
                        $subject, // тема письма
                        $body // текст письма
                        ) {
  $to = mime_header_encode($name_to, $data_charset, $send_charset)
                 . ' <' . $email_to . '>';
  $subject = mime_header_encode($subject, $data_charset, $send_charset);
  $from =  mime_header_encode($name_from, $data_charset, $send_charset)
                     .' <' . $email_from . '>';
  if($data_charset != $send_charset) {
    $body = iconv($data_charset, $send_charset, $body);
  }
  $headers = "From: $from\r\n";
  $headers .= "Content-type: text/plain; charset=$send_charset\r\n";
  $headers .= "Mime-Version: 1.0\r\n";

  return mail($to, $subject, $body, $headers);
}

function mime_header_encode($str, $data_charset, $send_charset) {
  if($data_charset != $send_charset) {
    $str = iconv($data_charset, $send_charset, $str);
  }
  return '=?' . $send_charset . '?B?' . base64_encode($str) . '?=';
}

?>

Мы определили собственную функцию mime_header_encode() для преобразования каждого заголовка в MIME-совместимый формат, несмотря на то, что в PHP предусмотрена аналогичная функция mb_encode_mimeheader(). От встроенной функции нам пришлось отказаться, так как на нашем сервере (PHP 5.1.6) она работает некорректно (возвращает неверный результат).

Использование описанной функции достаточно простое, см. пример ниже. В случае, если кодировка отправки письма отличается от текущей кодировки данных, используется iconv() для преобразования кодировки.

<?php
send_mime_mail('Автор письма',
               'sender@site.ru',
               'Получатель письма',
               'recepient@site.ru',
               'CP1251',  // кодировка, в которой находятся передаваемые строки
               'KOI8-R', // кодировка, в которой будет отправлено письмо
               'Письмо-уведомление',
               "Здравствуйте, я Ваша программа!");
?>

© Все права на данную статью принадлежат порталу webew.ru. Перепечатка в интернет-изданиях разрешается только с указанием автора и прямой ссылки на оригинальную статью. Перепечатка в печатных изданиях допускается только с разрешения редакции.
Статьи, часто просматриваемые вместе с этой:
Магические кавычки в PHP
jQuery для JavaScript
Число прописью средствами PHP
Добавить комментарий
Отображение комментариев: Древовидное | Плоское
NO USERPIC

rgbeast

У iconv обнаружилась одна особенность: если символ не существует в целевой кодировке, то строка обрезается на этом символе. http://ru.php.net/manual/ru/function.iconv.php

Такое происходит при попадании в отправляемый текст юникодовских кавычек или юникодовских тире (или символов алфафита третьего языка при преобразовании UTF-8 в однобайтовую кодировку).

Чтобы игнорировать непечатные символы, следует заменить вызовы iconv на следующие:
   $body = iconv($data_charset, $send_charset . '//IGNORE', $body);
    $str = iconv($data_charset, $send_charset . '//IGNORE', $str);
14.09.2008, 02:02
Ответить

1234ru

На самом деле эта проблема даже шире.

iconv() действительно очень плохо себя ведет, когда наталкивается на символ, которого нет в кодировке-приемнике (грубо говоря). //IGNORE подавляет ошибку, но результирующая строка по-прежнему получается обрезанной (неизвестно еще, что лучше). Т.е., например, iconv('UTF-8', 'KOI8-R//IGNORE', 'Заказ № 123') даст просто "Заказ " (потому что символ № в KOI8-R отсутствует).
Эта проблема достаточно широко известна в среде PHP-программистов (чего только стоят комментарии к странице руководства, посвященной этой функции).
Предлагают альтернативный путь - с использованием mb_convert_encoding(). Но там тоже не все гладко: проблема отсутствия в одних кодировках символов, присутствующих в других (особенно UTF-8) кодировках, там решается не так топорно, как в iconv(), но все же грубовато - они заменяются на знаки вопросов. Помогает установка соответствующей директивы:

ini_set('mbstring.substitute_character', "none");

ну или в php.ini:

mbstring.substitute_character = "none"
То, что не убивает нас, делает нас инвалидами.
09.08.2009, 12:52
Ответить

Serg_pnz

Немного не в тему: а что в пхп-скрипте написать, что бы запросить уведомление о прочтении? /*смотрел-смотрел код оутлукэкспресса, но так и не догнал*/
14.09.2008, 20:11
Ответить
NO USERPIC

rgbeast

Запрос уведомления о прочтении это дополнительный заголовок письма:
Disposition-Notification-To: $email_from

см. RFC 3798

В контексте кода выше:
  $headers = "From: $from\r\n";
  $headers .= "Disposition-Notification-To: $email_from\r\n";
  $headers .= "Content-type: text/plain; charset=$send_charset\r\n";

14.09.2008, 20:59
Ответить
NO USERPIC

Azazel

У меня есть необходимость отправки ссылки в тексте письма. Вместо ссылки приходит:<a href=.....................</a>. Что делать?
08.11.2008, 19:50
Ответить
NO USERPIC

rgbeast

Если тип письма text/plain, то оно будет отправленно как простой текст. Что отправились ссылки, формат письма нужно сделать html. В коде выше замените строчку с Content-Type на следующую
 $headers .= "Content-type: text/html; charset=$send_charset\r\n";


При этом письмо должно быть в html, то есть теги будут вести себя как теги, а новая строка игнорироваться.
08.11.2008, 20:13
Ответить
NO USERPIC

Azazel

Большое спасибо!
08.11.2008, 20:24
Ответить
NO USERPIC

neoromeo

Спасибо , очень полезная статья.
27.11.2008, 15:33
Ответить
NO USERPIC

colonel

Григорий, скажите пожалуйста, а как быть, когда нужно отправить письмо нескольким получателям?

Вопрос относится к этой статье: http://webew.ru/articles/297.webew
27.02.2009, 22:41
Ответить
NO USERPIC

rgbeast

Достаточно просто переписать функцию так, чтобы она принимала массив имен и массив email-адресов получателей:
function send_mime_mail_a($name_from, // имя отправителя
                        $email_from, // email отправителя
                        $name_to, // массив имен получателя
                        $email_to, // массив email-адресов получателя
                        $data_charset, // кодировка переданных данных
                        $send_charset, // кодировка письма
                        $subject, // тема письма
                        $body // текст письма
                        ) {
  $to = '';
  for($i=0;$i<count($name_to);$i++) {
    $to .= mime_header_encode($name_to[$i], $data_charset, $send_charset)  . ' <' . $email_to[$i] . '>' . ( ($i<count($name_to)-1)?', ':'');
  }
  $subject = mime_header_encode($subject, $data_charset, $send_charset);
  /* ... */
  /* далее без изменений */

27.02.2009, 23:34
Ответить
NO USERPIC

colonel

Сдается мне что это не работает ... Письма не приходят, хотя функция mail срабатывает.
28.02.2009, 03:21
Ответить
NO USERPIC

rgbeast

Странно, я ее протестировал, у меня письма дошли. Вызывать нужно примерно так:
send_mime_mail_a('Отправитель', 'sender@example.com', array('Петя', 'Иван'), array('peter@example.com', 'ivan@example.com'), 'KOI8-R', 'KOI8-R', 'Тема письма', 'Текст письма');

Посмотрите, что записано в /var/log/maillog после отправки
28.02.2009, 11:45
Ответить
NO USERPIC

colonel

Да, я там немного напутал с массивами. Просто я отправлял Как 'Петя'; 'asd@asd.ru, asd2@asd.ru'. Вот и не получалось.

Подскажите пожалуйста, как мне запросить уведомление о прочтении и открытии письма?

Спасибо.
02.03.2009, 00:57
Ответить
NO USERPIC

rgbeast

См. выше http://webew.ru/articles/297.webew#1253
02.03.2009, 01:06
Ответить
NO USERPIC

colonel

Спасибо большое. Буду пробовать.
28.02.2009, 00:49
Ответить
NO USERPIC

mixus

Спасибо, очень помогло.
28.04.2009, 17:10
Ответить
NO USERPIC

evgeni777

На основе Вашей статьи сделал скрипт
<?php

$file=file("../mailadr.txt");
$b1=$_POST['name'];
$b2=$_POST['email'];
$b3=$_POST['timer'];
$b4=$_POST['text'];

$headers = 'MIME-Version: 1.0' . "\r\n" ;
$headers .= 'Content-type: text/html; charset=windows-1251' . "\r\n" ;

for($i=0;$i<sizeof($file);$i++)
{
sleep($b3);
mail($file[$i],"",$b4, $headers);}

echo "Почта отправлена ";
echo '<hr>';
echo '<a href="../index.htm"><font face="Times New Roman"><b>Главная</b></font></a>';
echo '<hr>';
echo '<a href="../news.htm"><font face="Times New Roman"><b>Рассылка</b></font></a>';

?>
где $b4=$_POST['text']; - тело письма
$headers = 'MIME-Version: 1.0' . "\r\n" ;
$headers .= 'Content-type: text/html; charset=windows-1251' . "\r\n" ; - заголовки

Скрипт работает, при посылке письма содержащее URL ссылки, письма приходят, но ссылки имеют
URL адрес вида - attachment:/%22http://www.mysite.ru/%22 - как это поправить?
Evg
09.08.2009, 15:19
Ответить
NO USERPIC

rgbeast

%22 это кавычка, посмотрите внимательно содержимое $b4, нет ли лишних кавычек или слешей перед кавычкой. Посмотрите статью про магические кавычки.
10.08.2009, 04:28
Ответить
NO USERPIC

evgeni777

Огромное спасибо ! Статья действительно помогла в проблеме,
советую всем её почитать т.к. не знание такой тонкости может изрядно
потрепать нервы.
Evg
10.08.2009, 04:53
Ответить
NO USERPIC

BuHunyx

Большое спаибо за статью, оч помогла. Вопрос не совсем не потеме , как прикреплять вложения к письмам?
VesV
28.09.2009, 13:12
Ответить
NO USERPIC

rgbeast

Можно использовать один из пакетов, например PEAR::Mail_Mime http://pear.php.net/package/Mail_Mime

Если хотите вручную формировать письмо, то смотрите описание MIME: http://en.wikipedia.org/wiki/MIME
28.09.2009, 14:29
Ответить
NO USERPIC

BuHunyx

Огромное спасибо, все это очень интересно , но время на изучение (к сажелению) очень мало . может найдется пример кода?
VesV
28.09.2009, 15:05
Ответить
NO USERPIC

rgbeast

Вот здесь есть пример: http://phpclub.ru/detail/article/mail#part_7_1
28.09.2009, 18:41
Ответить

css

Вообще функция клевая, у меня только она не стала работать в Аутлуке, я им не пользуюсь, но проверку там делаю, так как остались еще пользователи и клиенты которые в нем сидят, или сидят вообще принудительно на работе.
В Аутлуке не приходило нормально..

Скидываю, как я переделала эту функцию, правда не проверяла если кодировка письма и кодировка сайта не совпадает, но если везде 1251, то все отлично работает. Уходит на несколько емайлов, только емайлы надо писать тупо емайл1, емайл2 и т.д. Без имен. Кодировка везде нормальная и в Бате и в Аутлуке.
Если мало разбираетесь, копируйте функцию, потом на приведенном примере переписывайте вызов.

Функция

function send_mime_mail($name_from, // имя отправителя
                        $email_from, // email отправителя
                        $email_to, // email получателя
                        $data_charset, // кодировка переданных данных
                        $send_charset, // кодировка письма
                        $subject, // тема письма
                        $body // текст письма
                        ) {
  $to = $email_to;
  $subject = mime_header_encode($subject, $data_charset, $send_charset);
  $from =  mime_header_encode($name_from, $data_charset, $send_charset).' <' . $email_from . '>';
  if($data_charset != $send_charset) {
    $body = iconv($data_charset, $send_charset, $body);
  }
 
  $headers ="Content-type: text/plain; charset=\"".$send_charset."\"\n";
  $headers .="From: $from\n";
  $headers.="Mime-Version: 1.0\n";

  return mail($to, $subject, $body, $headers);
}

function mime_header_encode($str, $data_charset, $send_charset) {
  if($data_charset != $send_charset) {
    $str = iconv($data_charset, $send_charset, $str);
  }
  return '=?' . $send_charset . '?B?' . base64_encode($str) . '?=';
}


И сам вызов функции
send_mime_mail('Название от кого пришло - Ваше ФИО или название фирмы',
               'dmin@leadsyou.ru', //Ваш емайл, или куда им отвечать на письмо
               'емайл1, емайл2, емайл3',  //куда письма рассылаем
               'windows-1251',  
               'windows-1251',
               'Тема письма',
               'Я пишу письмо большое, не плохое и кривое... Прочитай его без скуки В Бате или Аутлуке.. ну и так далее.. ');
16.02.2010, 12:56
Ответить
NO USERPIC

rgbeast

Спасибо за комментарий и за то, что поделились своим кодом, я в аутлуке не тестировал. Скажите, какое изменение было ключевым для аутлука - добавление Mime-version в заголовок или отказ от кодирования имени в $to? Вариант описанный в статье не предполагает нескольких адресатов, а Ваш вариант это допускает, но интересно что именно требуется аутлуку.
16.02.2010, 13:24
Ответить

css

Да Вы знаете, я даже точно не скажу.. Мучалась, мучалась, потмо взяла тот код который у меня работал в Аутлуке, совместила с этим.. Как говориться сделала оптимальный вариант. Кстати сейчас сижу над юникодовскими письмами. Приходит нормально, но опять Аутлук гадит, спецсимволы не проходят. Главное в письме проходят, в теле письма, в в теме вопросики на месте этих символов.
У меня название сайта на башкирском есть, там один спецсимвол.. В тело идет, а в тему или в From - нет.
А надо же народу показывать откуда письмо, с какого сайта, а то могут не снять.
Отказ от кодирования в To - это просто возможность добавить несколько емайлов, Аутлук тут не причем. Я думаю не обязательно присылать письма типа "Васе Пупкину <vasa@mail.ru>" можно и просто послать через запятую на емайлы. Потмоу что если мы кодируем емайлы через запятую - у меня есть подозрение, что запятые тоже кодируются.
А с Аутлуком меня просто клиенты достают, сидят на этом Глюке, я 15 писем тестовых сняла, он у меня уже умер, начал пароль просить.. и т.п. но хозяин -барин, приходится. Так что я ему интуитивно подсовываю что нибудь )))) главное чтобы съел. Может как раз добавление Mime-version помогло.

КСТАТИ!!! мне пришло уведомление от вас что комментарий ваш добавлен.. Копирую как это пришло в БАте

ъДТБЧУФЧХКФЕ, ьММБ.


rgbeast ПФЧЕФЙМ ОБ ЧБЫ ЛПННЕОФБТЙК Л УППВЭЕОЙА"пФРТБЧЛБ e-mail Ч ТХУУЛПК ЛПДЙТПЧЛЕ УТЕДУФЧБНЙ PHP":
http://webew.ru/articles/297.webew#2746

рТПЖЙМШ РПМШЪПЧБФЕМС rgbeast НПЦОП РПУНПФТЕФШ ОБ УФТБОЙГЕ
http://webew.ru/users/33.webew


----------
рПЮФПЧЩК ТПВПФ РПТФБМБ webew.ru

Срочно чистите! Это в Бате так пришло. Нет я могу канешно ручками кодировку сменить, но большая часть пользователей в России или не догадаются, или просто тупо не снимут ваше письмо, подумав что это спам.
16.02.2010, 13:38
Ответить
NO USERPIC

rgbeast

Добавил Ваш кусок Mime-Version. Как сейчас Вам пришло уведомление?
16.02.2010, 14:00
Ответить

css

Ну вот! теперь все отлично! приняла Батом, все ок, потмо переслала на другйо емайл, который у меня в Аутлуке сидит, там тоже все нормально.
16.02.2010, 14:05
Ответить
NO USERPIC

rgbeast

Значит дело в Mime-Version. Внес исправление в статью. Большое спасибо!
16.02.2010, 14:20
Ответить
NO USERPIC

webber

У меня проблема, если можете помогите

На nokia n97 письма отправленные функцией mail приходят в крякозяблах
скрин могу отправить
Что интересно с других почтовиков приходят в норм кодировке

Что я только немудрил включая пример приведенный выше
Все равно абра кодабра какая то

Помогите
10.03.2010, 15:11
Ответить
NO USERPIC

rgbeast

В какой кодировке отправляете? Лучше всего не скрин, а заголовки письма, отправленного данной прогой и заголовки нормально читаемого русского письма.
10.03.2010, 17:24
Ответить
NO USERPIC

webber

А отправляю вот так вот:

$headers = 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-type: text/html; charset=windows-1251' . "\r\n"; // <<<< Здесь я что только непробовал ставить но все равно крякозяблы идут
$headers = 'From: www.тратата.ru <тратата@тратата.ru>' . "\r\n";
'Reply-To: тратата@тратата.ru' . "\r\n" .
'X-Mailer: PHP/';

//iconv("ANSI","windows-1251",$submess); ///здесь пробовал конвертировать в другую кодировку переменную темы письма
//iconv("us-ascii","windows-1251",$submess);
//iconv("UTF-8","windows-1251",$submess);
//iconv("ISO-8859-1","windows-1251",$submess);
//iconv("ISO-8859-5","windows-1251",$submess);

$messagen="Заявка с сайта \n\nИмя: $fio\nE-mail: $email\nТелефон: $phone\nАдрес: $adres\nМетро: $mmt\nКомментарий: $mess\n\n$zakaz";
mail($to1, $submess." (метро: $mmt)"."-(адрес: $adres)", $messagen, $headers);
mail($to2, $submess." (метро: $mmt)"."-(адрес: $adres)", $messagen, $headers);
10.03.2010, 17:38
Ответить
NO USERPIC

rgbeast

Это естественно, ведь функция mail не кодирует заголовки в MIME-совместимый формат. Воспользуйтесь функцией send_mime_mail(), описанной в статье.
10.03.2010, 18:04
Ответить
NO USERPIC

webber

Спасибо, заработало
но заработало вот в таком виде
а наоборот KOI8-R передавал
и отправлял CP когда неработало

send_mime_mail('www.тратата.ru',
'тратата@тратата.ru',
'тратата',
'тратата@тратата.ru',
'CP1251', // кодировка, в которой находятся передаваемые строки
'KOI8-R', // кодировка, в которой будет отправлено письмо
$submess." (: $mmt)"."-(: $adres)",
$messagen);
10.03.2010, 18:19
Ответить

1234ru

Бывает нужно также регулировать тип отправляемого письма: простой текст или HTML. Для этого можно сделать дополнительный необязательный аргумент:
function send_mime_mail(
    ...
    $body, // текст письма
    $html = FALSE // письмо в виде html или обычного текста
    ) {
    ...
    $type = ($html) ? 'html' : 'plain';
    $headers .= "Content-type: text/$type; charset=$send_charset\r\n";
    ...
}
То, что не убивает нас, делает нас инвалидами.
25.04.2010, 01:15
Ответить
Добавить комментарий
Отображение комментариев: Древовидное | Плоское
© 2007—2010 webew.ru, связаться: x собака webew.ru
Сайт использует Flede и соответствует стандартам WAI-WCAG 1.0 на уровне A.
Rambler's Top100