Все мы сталкивались с задачей отправки писем на php. Есть конечно функция mail(), но:
Попробуем написать свою систему отложенной отправки писем, без использования специализированного ПО, наподобие, Gearman.
Что понадобится?
Итак, начнём.
В конфигурационном файле (redis.conf) достаточно прописать:
Создаём скрипт автозагрузки redis:
Прописываем в него:
Делаем файл исполняемым, прописываем в автозагрузку и запускаем:
Напишем небольшой класс для добавления наших писем в очередь:
Таким образом, добавить письмо в очередь можно, подключив данный класс (вместе с классом для redis):
За непосредственную отправку письма будет отвечать небольшой класс php:
Некоторые параметры, естественно, придётся заменить (например, ip — адрес smtp или пароль).
«Демон» на php (PostMail.php), который будет сидеть в процессах и отправлять наши письма:
Запустим наш скрипт PostMail.php:
Можно добавить скрипт в автозагрузку.
Всё готово. Теперь скрипт PostMail.php будет ожидать новых писем в redis, а получив — сразу отправлять юзеру. (Скрипт на php показал себя надежно — память не течёт, цп не кушает. Работает без остановки уже 3 месяца — все хорошо).
P.S> Первый пост на Хабре, поэтому не судите строго. Старался писать коротко и по делу.
- Как выяснилось, не все почтовые сервисы принимают письма, сгенерированные php из данной функции
- Скорость работы оставляет желать лучшего
- Невозможность отложенной отправки
Попробуем написать свою систему отложенной отправки писем, без использования специализированного ПО, наподобие, Gearman.
Что понадобится?
- Redis (лично я юзаю версию redis-2.2.8)
- Либа для работы с Redis (например php_redis)
- php
- Доступ к smtp хостинг — провайдера
Итак, начнём.
Ставим Redis:
cd /install/install_redis/
wget http://redis.googlecode.com/files/redis-2.2.8.tar.gz
tar xvzf redis-2.2.8.tar.gz
cd redis-2.2.8
make
cd src
В конфигурационном файле (redis.conf) достаточно прописать:
daemonize yes
pidfile /var/run/redis.pid
port 6179
Создаём скрипт автозагрузки redis:
nano /etc/init.d/redis
Прописываем в него:
#!/sbin/runscript
depend() {
need net
}
svc_start() {
ebegin "Starting redis"
start-stop-daemon --start --quiet --pidfile /var/run/redis.pid --exec /install/install_redis/redis-2.2.8/src/redis-server /install/install_redis/redis-2.2.8/redis.conf
eend $?
}
svc_stop() {
ebegin "Stopping redis"
start-stop-daemon --stop --quiet --pidfile /var/run/redis.pid --exec /install/install_redis/redis-2.2.8/src/redis-server
eend $?
}
svc_restart() {
ebegin "Restarting redis"
start-stop-daemon --stop --quiet --pidfile /var/run/redis.pid --exec /install/install_redis/redis-2.2.8/src/redis-server
start-stop-daemon --start --quiet --pidfile /var/run/redis.pid --exec /install/install_redis/redis-2.2.8/src/redis-server /install/install_redis/redis-2.2.8/redis.conf
eend $?
}
Делаем файл исполняемым, прописываем в автозагрузку и запускаем:
chmod +x /etc/init.d/redis
rc-update add redis default
/etc/init.d/redis start
Теперь работаем с php:
Напишем небольшой класс для добавления наших писем в очередь:
require('redis.php'); // Подключаем файл - библиотеку для работы с redis (например php_redis)
class post_mess_user
{
public
function post_email_user($array)
{global $redis;
if(!$redis)
{
$redis = new php_redis();
}
if($redis->ERROR_CONNECT)
{
echo 'Ошибка соединения с Redis; тип: '.$redis->ERROR_CONNECT[0].', код ошибки: '.$redis->ERROR_CONNECT[1];
}
else
{
$redis->list_rpush('mails',base64_encode(serialize($array)));
}
}
}
Таким образом, добавить письмо в очередь можно, подключив данный класс (вместе с классом для redis):
$user_mail = new post_mess_user ();
$array=array();
$array['from']='From';
$array['from_email']='support@support.ru';
$array['to']='To';
$array['tema']='Подтверждение регистрации';
$array['mess']='Текст';
$user_mail->post_email_user($array);
За непосредственную отправку письма будет отвечать небольшой класс php:
class mail_smtp
{
function get_data($smtp_conn)
{
$data="";
while($str = fgets($smtp_conn,515))
{
$data .= $str;
if(substr($str,3,1) == " ") { break; }
}
return $data;
}
public
function post($from,$from_email,$to,$tema,$mess)
{
$domain = mb_substr($from_email,mb_strpos($from_email,'@')+1);
$header="Date: ".date("D, j M Y G:i:s")." +0700\r\n";
$header.="From: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode(iconv('utf-8','windows-1251',$from))))."?= <$from_email>\r\n";
$header.="X-Mailer: The Bat! (v3.99.3) Professional\r\n";
$header.="Reply-To: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode(iconv('utf-8','windows-1251',$from_email))))."?= <$from_email>\r\n";
$header.="X-Priority: 3 (Normal)\r\n";
$header.="Message-ID: <172562218.".date("YmjHis")."@$domain>\r\n";
$header.="To: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode(iconv('utf-8','windows-1251',$to))))."?= <$to>\r\n";
$header.="Subject: =?windows-1251?Q?".str_replace("+","_",str_replace("%","=",urlencode(iconv('utf-8','windows-1251',$tema))))."?=\r\n";
$header.="MIME-Version: 1.0\r\n";
$header.="Content-Type: text/html; charset=windows-1251\r\n";
$header.="Content-Transfer-Encoding: 8bit\r\n";
$message = str_replace("\n","",$mess);
$message=iconv('utf-8','windows-1251',$message);
$smtp_conn = fsockopen("80.170.220.47", 25, $errno, $errstr, 10);
$this->get_data($smtp_conn);
fputs($smtp_conn,"EHLO localhost\r\n");
$this->get_data($smtp_conn);
$size_msg=strlen($header."\r\n".$text);
fputs($smtp_conn,"MAIL FROM:<$from_email> SIZE=".$size_msg."\r\n");
$this->get_data($smtp_conn);
fputs($smtp_conn,"RCPT TO:<$to>\r\n");
$this->get_data($smtp_conn);
fputs($smtp_conn,"DATA\r\n");
$this->get_data($smtp_conn);
fputs($smtp_conn,$header."\r\n".$message."\r\n.\r\n");
$this->get_data($smtp_conn);
fputs($smtp_conn,"QUIT\r\n");
$this->get_data($smtp_conn);
return true;
}
}
$mail_smtp = new mail_smtp();
Некоторые параметры, естественно, придётся заменить (например, ip — адрес smtp или пароль).
«Демон» на php (PostMail.php), который будет сидеть в процессах и отправлять наши письма:
ini_set("max_execution_time",'0');
require('classes/mail_smtp.php'); // Подключаем наш класс для отправки почты
require('redis.php'); // Подключаем файл - библиотеку для работы с redis (например php_redis)
while(1)
{
usleep(100000); // спим (менять под свои задачи)
$array_0=$redis->list_lpop('mails');
if($array_0)
{
$array=unserialize(base64_decode($array_0));
if($array['to'])
{
if(!$mail_smtp->post($array['from'],$array['from_email'],$array['to'],$array['tema'],$array['mess']))
{
$redis->list_rpush('mails',$array_0);
}
}
}
}
Запустим наш скрипт PostMail.php:
php -f /var/www/PostMail.php &
Можно добавить скрипт в автозагрузку.
Всё готово. Теперь скрипт PostMail.php будет ожидать новых писем в redis, а получив — сразу отправлять юзеру. (Скрипт на php показал себя надежно — память не течёт, цп не кушает. Работает без остановки уже 3 месяца — все хорошо).
P.S> Первый пост на Хабре, поэтому не судите строго. Старался писать коротко и по делу.