<?xml version="1.0" encoding="UTF-8" ?><oembed><version>1.0</version><provider_name>Чудо{вищные} заметки</provider_name><provider_url>https://miracle.rpz.name</provider_url><author_name>MiRacLe</author_name><author_url>https://miracle.rpz.name/author/miracle/</author_url><title>Асинхронные задачи в PHP</title><html>Не открою Америки, если скажу что порой требуется выполнить некую времязатратную операцию, результат которой либо не нужен пользователю вовсе (запись в лог, удаление временных файлов и другое обслуживание сервера), либо его можно обмануть и сказать что операция выполнена успешно, а саму операцию выполнить &quot;попозже&quot;. Самым наверное близким всем примером такой операции можно назвать отправку почты - smtp-cессия может длится довольно долго, особенно если письмо огромное, сервер тормозной (ну да вы сами всё знаете, в реальном мире великое множество острых углов), но зачем пользователю ждать результата? Что ему делать если результат неуспешный? &quot;Попробуйте повторить операцию позже&quot; ? Не смешно! Рядовой пользователь на ваш рядовой ресурс не вернётся, дотошный - свяжется с вами другими способами, так что можно с чистой совестью соврать ему - мол всё путём, всё отправлено, всё доставлено, записано и всё так успешно и идеально, а в это время потихоньку начать на самом деле выполнять задачу.

Конечно если вокруг вас крутятся тысячи серверов, слово &quot;ынтырпрайз&quot; для вас звучит буднично, то для вас уже изобретено много-много buzz-word-ных решений с очередями сообщений, очередями задач и другими полезными решениями, но львиная доля разработчиков всё-таки создают сайты-визитки, поддерживают сайты на хостинг-планах &quot;всё по 20 рублей&quot;, да и вообще на мой взгляд глупо &lt;a href=&quot;http://highload.com.ua/index.php/2010/07/09/gearman-и-php-асинхронные-задачи/&quot;&gt;для отправки почты окружать простейший php-скрипт кучей софта вроде gearmand + расширения для работы с ним&lt;/a&gt;. Я же хочу показать решение &quot;для бедных&quot;, &lt;strong&gt;простое, но удобное в разработке, поддержке и отладке решение&lt;/strong&gt;.

Итак задача - отправить письмо, не заставляя ждать пользователя.
Имеем:
&lt;pre class=&quot;php&quot;&gt;
list($recipient,$subject,$body) = get_vars_from_request();
include &#039;superpupermailer.php&#039;;
$mailer = new SuperPuperMailer($recipient,$subject,$body);
if ($mailer-&gt;send()) {
echo &quot;Аллилуя! Мы сделали это, храни нас Великий Байт.&quot;;
} else {
echo &quot;О нет, это случилось!!! Быть того не может...но всё же случилось - приходи, милый друг, в другой раз, а сейчас ошибка!&quot;;
}
&lt;/pre&gt;
Тут всё понятно - либо отправилось, либо не отправилось - всем приходилось видеть это с разных сторон, те кто видел это со стороны браузера нередко наблюдали не только &quot;ошибочка вышла&quot;, но и другие подробности вроде конкретных строк в скриптах, warning-ов и Fatal error-ов. Но мы уже выяснили ранее - во-первых пользователю совершенно по барабану что у вас произошло с сервером, а во-вторых &quot;пробовать ещё раз&quot; он скорее всего не будет. Поэтому код можно изменить следующим образом:
&lt;pre class=&quot;php&quot;&gt;
list($recipient,$subject,$body) = get_vars_from_request();
$async_job = &#039;&lt;?php include &quot;superpupermailer.php&quot;; $mailer = new SuperPuperMailer(&quot;&#039;.$recipient.&#039;&quot;,&quot;&#039;.$subject.&#039;&quot;,&quot;&#039;.$body.&#039;&quot;);
return $mailer-&gt;send();&#039;;
if (file_put_contents(&#039;/dir/for/jobs/email.php&#039;,$async_job)) {
echo &quot;Ура! Мы сделали этот мир лучше!&quot;;
} else {
echo &quot;Увы, мир жесток и безжалостен...&quot;;
}
&lt;/pre&gt;

Что это и зачем? Где отправка почты? Каким образом письмо отправится? Больше вопросов чем ответов. Опять-таки остался &lt;em&gt;else&lt;/em&gt;, шило поменяли на мыло? В целом да - заменили тёплое мягким, но всё-таки нет - как часто у вас заканчивается неудачей запись на диск? Чаще чем таймаут при коннекте к почтовому серверу? 

Теперь о главном - зачем файл? Где отправка почты?

Этим займётся другой скрипт:

&lt;pre class=&quot;php&quot;&gt;
if (true === include(&#039;/dir/for/jobs/email.php&#039;)) {
  unlink(&#039;/dir/for/jobs/email.php&#039;);
}
&lt;/pre&gt;

Осталось вызвать этот скрипт. Например так:

&lt;pre class=&quot;php&quot;&gt;
...
echo &#039;Ура! Мы сделали этот мир лучше!&lt;img src=&quot;/job.php&quot; height=&quot;1&quot; width=&quot;1&quot;&gt;&#039;;
...
&lt;/pre&gt;

Или же добавим в cron задание на вызов job.php каждый час/пять минут/каждую минуту.

Всё. Мы &lt;strong&gt;не заставили пользователя ждать&lt;/strong&gt;, мы отправили сообщение, мы молодцы. А если не отправили? Отправим потом - файл-то остался!

Конечно возникает много закономерных возражений - что будет если send.php (или job.php) будет вызван одновременно двумя пользователями? 
Эти вопросы надо обстоятельно решать, задачи создавать с уникальными именами, блокировать одновременный запуск скрипта job.php, в общем работы аж на целых десять минут.

Самое интересное в конце:
&lt;pre class=&quot;php&quot;&gt;
...
$job_name = uniqid(&#039;mail_&#039;);
if (file_put_contents(&#039;/dir/for/jobs/&#039;.$job_name.&#039;.php&#039;,$async_job)) {
   echo &quot;Ура! Мы сделали этот мир лучше!&quot;;	
              $job_url = &#039;/job.php?job=&#039;.$job_name;
              if (false !== ($fh = @fsockopen($_SERVER[&#039;SERVER_ADDR&#039;], $_SERVER[&#039;SERVER_PORT&#039;], 
    $errno, $errstr, 0.01))) {
                  fputs($fh,
                      &quot;GET $job_url HTTP/1.0\r\n&quot;
                          . &quot;Host: {$_SERVER[&#039;HTTP_HOST&#039;]}\r\n\r\n&quot;
                  );
                  fgets($fh,32);
                  fclose($fh);	
              }

} else {
   echo &quot;Увы, мир жесток и безжалостен...&quot;;
}
&lt;/pre&gt;

Что здесь происходит? Мы создали php-файл, при выполнении которого отправится письмо, затем подключились к веб-серверу и запросили скрипт job.php, передав ему параметром имя только что созданного файла. И тут же отключились - ведь нам не важен результат, мы уже солгали пользователю о том, что операция завершилась успешно. На всё-про всё потратили доли секунды.  Дальше уже дело техники - job.php захватит lock-файл, проверить наличие файла, имя которого ему передали, выполнит его, удалит в случае успеха, а затем отпустит lock-файл. Конечно надо не забывать, что скрипт может по каким-то причинам не выполнится (почтовый сервер недоступен, или ответит ошибкой, да и мало ли какие напасти происходят в реальности), поэтому следует вызывать job.php ещё и ещё, но пользователя это уже не должно волновать - у вас его письмо сохранилось и вы его доставите, он вам верит!

Разумеется решение годится не только для отправки почты, но и как я сказал в начале - &lt;strong&gt;для любых действий, результат которых не нужен немедленно&lt;/strong&gt;.</html><type>rich</type></oembed>