Начинаем рубрику «для самых маленьких», с решением простеньких проблем.
Есть такая функция в PHP — mail(), и отправляет она, как все знают, электронную почту. Чуть меньше народу знает, что на самом деле отправляет не она, а mail-сервер, которому она просто передаёт письмо.
Ситуация с настоящим mail-сервером при тестировании сайта на локалке (под Windows) несколько неудобна по двум причинам. Во-первых, достаточно геморойно его установить. Во-вторых, при тестировании обычно не нужно действительно рассылать письма по настоящим адресам. Лучше всего в локальной версии все отправляемые письма складывать в отдельную папочку. Сразу видно сколько писем было отправлено, кому и что у них внутри.
Проще простого. На самом деле, mail() просто вызывает программу прописаную в php.ini в параметре sendmail_path и передаёт текст письма ей во входящий поток. Напишем свою программу и пропишем её здесь. Так как мы говорим о PHP, не будем ничего придумывать и напишем её на том же PHP.
Запуск сценария
Просто так PHP-сценарий запустить нельзя, нужно вызывать интерпретатор. В дистрибутиве PHP есть три исполняемых файла: php-cgi.exe, php.exe и php-win.exe. Нам нужен интерпретатор для работы с командной строкой — php.exe. Ему нужно указать флаг -f (выполнение кода из файла) и вторым аргументом передать путь к файлу. Таким образом прописываем в php.ini:
sendmail_path = "c:\web\php\php.exe -f c:\web\mail\sendmail.php" ; ну или где у вас находится php |
Перезапускаем сервер. Теперь при вызове mail() будет запускаться sendmail.php
. Осталось написать его :)
sendmail.php
<?php /* Получаем текст письма из входного потока */ $mail = ''; $fp = fopen('php://stdin', 'rb'); while (!feof($fp)) { $mail .= fread($fp, 512); } /* Корректировка заголовков */ $e = preg_split('~(\r\n\r\n)|(\n\r\n\r)|(\n\n)|(\r\r)~', $mail, 2); $headersS = preg_split('~(\r\n)|(\n\r)|\n|\r~', $e[0]); $headers = Array(); $body = isset($e[1]) ? $e[1] : ''; foreach ($headersS as $header) { $e = explode(':', $header, 2); $name = strtolower(trim($e[0])); $value = trim(isset($e[1]) ? $e[1] : ''); $headers[$name] = $value; } $headersS = implode("\r\n", $headersS); if (!isset($headers['date'])) { $headersS = 'Date: '.gmdate('D, j M Y H:i:s')."\r\n".$headersS; } if (!isset($headers['content-type'])) { $headersS = "Content-Type: text/plain; charset=windows-1251\r\n".$headersS; } if (!isset($headers['mime-version'])) { $headersS = "MIME-Version: 1.0\r\n".$headersS; } $mail = $headersS."\r\n\r\n".$body; /* Генерируем имя файла */ $fileNameBase = dirName(__FILE__).'/mails/'.date('Ymd_His'); $fileName = $fileNameBase.'.eml'; $i = 1; while (file_exists($fileName)) { $i++; $fileName = $fileNameBase.'_'.$i.'.eml'; } /* Скидываем письмо в файл */ file_put_contents($fileName, $mail); ?> |
Тестируем
<?php $to = 'to@mail.loc'; $from = 'from@mail.loc'; $subject = 'Hello!'; $message = 'Hard porn on www.pornuha.loc!'; mail($to, $subject, $message, 'From: '.$from); ?> |
Запускаем и получаем в папке mails (подкаталог каталога с sendmail.php) файл 20090305_134543.eml
(отправлено 5 марта в 13:45):
MIME-Version: 1.0 Content-Type: text/plain; charset=windows-1251 Date: Thu, 5 Mar 2009 10:45:43 To: to@mail.loc Subject: Hello! From: from@mail.loc Hard porn on www.pornuha.loc! |
Кому мешают заголовки, можно открыть файл в любой почтовой программе (The Bat, например).
Пояснения к sendmail.php
Входной поток читается как обычный файл с именем php://stdin
.
Так как одновременно может рассылаться несколько писем, имена файлов могут совпасть. Поэтому проверяем наличие файла и добавляем к нему префикс «_1», «_2″… пока не найдётся свободный.
Корректировка заголовков
Письмо состоит из служебных заголовков (например, To: to@mail.loc
, в примере) и тела письма (собственно, отображаемый текст). Каждый заголовок располагается на отдельной строке, от тела письма отделяется пустой строкой.
В то же время переносы строк, в разных системах, как известно, кодируются по разному: \r\n
, \n\r
, \n
или \r
. Некоторые же почтовые программы (тот же Bat) под виндой глючат, если строки в заголовках переносятся не с помощью \r\n
. А mail() норовит переносить строки одним \n
.
Поэтому, для избежания проблем при просмотре письма, просто вытаскиваем заголовки, разделяем их, а потом собираем вместе с нужным переносом. Заодно проверяем наличие важных заголовков Mime-Version
, Date
и Content-Type
, если их нет — вставляем значения по умолчанию.
Вот и всё ребятки…
Хм.. Как-то я о таком решение не подумал.
На днях как раз ставил себе sendmail но толи из-за Windows Vista, то ли из-за моей криворукости(скорее 2-е конешн) ошибок никаких не выдается, но письма не рассылаются :)
dallone, 5.03.2009, 14:45
dallone, Олежко мне еще при Москве этот скриптик давала :)
adw0rd, 5.03.2009, 21:59
to dallone — у меня на висте та же история, XP нормуль
Борис, 2.04.2009, 17:43
ОГРОМНОЕ СПАСИБО!!!!!!
Vladimir, 11.04.2009, 12:15
Спасибо ща поробуем :)
Историк, 15.04.2009, 18:23
Респект!
sealpania, 2.05.2009, 11:52
Можно бы сделать пример более приятным, если добавить строки для передачи принятого письма в sendmail. Или теорию, о том как отправить письмо по назначению.
Евгений, 3.07.2009, 11:39
Евгений, не понял вас.
vasa_c, 3.07.2009, 12:07
Вопрос: а нахрена это надо? Чтобы получить копию письма в формате eml?
Дмитрий, 29.06.2012, 9:56
Дмитрий, если хотите получить ответ, или сформулируйте вопрос без «нахрена», или перечитайте статью.
vasa_c, 29.06.2012, 12:31