10

I want to run a php script that will send out a bunch of emails (newsletters) but since it can take a while for that to run I want the user be immediately redirected to a page with a message like "Your newsletters are queued to be sent out."
So far the header redirects work but they do not cause the redirection until after all the emails get sent. Can I close the connection after sending the redirect so the browser acts on the redirect immediately? I tried stuff like this


ob_start();
ignore_user_abort(true);
header( "refresh:1;url=waitforit.php?msg=Newsletters queued up, will send soon.");
header("Connection: close");
header("Content-Length: " . mb_strlen($resp));
echo $resp;
//@ob_end_clean();
ob_end_flush();
flush();

in various permutations and combinations but to no avail. I suspect it cannot be done so simply but before I start messing with cron jobs or custom daemons I thought I'd look into this approach. Thanks

eg like blockhead suggests below, but no luck

ob_start();
ignore_user_abort(true);
header( "refresh:1;url=mailing_done.php?msg=Newsletters queued for sending.");
header("Connection: close");
header("Content-Length: 0" );
//echo $resp;
ob_end_flush();
flush(); 
ozahorulia
  • 9,798
  • 8
  • 48
  • 72
lost baby
  • 3,178
  • 4
  • 32
  • 53

7 Answers7

15

If you want to run script continuously and still want to display some output then, why don't you use an ajax request to server which can queue mails and still let user continue browsing that page.

If you don't want to use this; instead you could use, the script runs background even if the user will be redirected to show_usermessage.php page

<?PHP
    //Redirect to another file that shows that mail queued
    header("Location: show_usermessage.php");

    //Erase the output buffer
    ob_end_clean();

    //Tell the browser that the connection's closed
    header("Connection: close");

    //Ignore the user's abort (which we caused with the redirect).
    ignore_user_abort(true);
    //Extend time limit to 30 minutes
    set_time_limit(1800);
    //Extend memory limit to 10MB
    ini_set("memory_limit","10M");
    //Start output buffering again
    ob_start();

    //Tell the browser we're serious... there's really
    //nothing else to receive from this page.
    header("Content-Length: 0");

    //Send the output buffer and turn output buffering off.
    ob_end_flush();
    flush();
    //Close the session.
    session_write_close();


    //Do some of your work, like the queue can be ran here,
    //.......
    //.......
?>
Bhavesh G
  • 3,000
  • 4
  • 39
  • 66
  • does that work for you, I just tried it and it didn't work, is there some wrinkle to it I'm missing? – lost baby May 08 '12 at 19:16
  • I don't really want any output to browser from this script, I want the script to continue processing after the redirect happens (slowly sending emails). Maybe I'll try to exec a separate script but that seems like it may raise some security issue. – lost baby May 08 '12 at 19:24
  • that seems easiest now, I'll probably do that. I didn't really want to since the site doesn't use much ajax and it's therefore a change in style. Thanks – lost baby May 08 '12 at 19:32
  • interesting, I will try that . Thanks. – lost baby May 08 '12 at 20:19
  • Not working for me. I pasted your code, except i redirected back to the current url.'$r=1". It does redirect, but never does the work, after. – Doug Cassidy Jul 24 '13 at 18:18
  • ignore_user_abort() must be turned on in php.ini. Still, the accepted answer didnt work for me. Please see my answer. – Doug Cassidy Jul 24 '13 at 19:17
  • 1
    I would like to add that IE7 will not behave as expected if there is server output compression... In my case, I had to add those two lines: `apache_setenv('no-gzip', 1);` `ini_set('zlib.output_compression', 0);` I hope it saves someone the hours I spent. – Salketer Jan 19 '15 at 12:11
7

I would try this:

<?php
header("Location: waitforit.php?msg=Newsletters queued up, will send soon.");
header("Content-Length: 0");
header("Connection: close");
flush();
//Do work here
?>
Phil W
  • 549
  • 2
  • 5
3

Here is the proper way to do it... code by Sonia Desbiens, aka: fataqui 07/01/2005

<?php

set_time_limit ( 0 );

/* start the forced redirect */

header ( 'Connection: close' );

ob_start ();

/* close out the server process, release to the client */

header ( 'Content-Length: 0' );

header ( 'Location: /page_to_redirect_to.php' );

ob_end_flush ();

flush ();

/* end the forced redirect and continue with this script process */

ignore_user_abort ( true );

/* the rest of your script here */

/*
 * this example will redirect the user to '/page_to_redirect_to.php'
 * then this script will continue by sleeping for '10' seconds, then
 * this script will write a file, and then this script will exit...
*/

sleep ( 10 );

file_put_contents ( './out.txt', '' );

exit ();

?>
John
  • 31
  • 1
1

Try setting Content-Length to 0

blockhead
  • 9,655
  • 3
  • 43
  • 69
1

To get this to work on IIS7 you need to be using isapi with responseBufferLimit="0" in your web.config and output_buffering=Off in php.ini, then something like this should work...

  session_write_close();
  header( "Location: ".$sURL ) ;
    // try to allow continued processing
  if(ob_get_length())
    ob_end_clean();
  header("Connection: close");
  ignore_user_abort(); // optional
  ob_start();
  header("Content-Length: 0");
  ob_end_flush(); // Strange behaviour, will not work
  flush(); // Unless both are called !
Stephan
  • 41,764
  • 65
  • 238
  • 329
1

the accepted answer didnt work for me, but this did. Please note that the ignore_user_abort() must be turned on in php.ini

ignore_user_abort(true);
    session_write_close(); // optional, this will close the session.
    header("Location: $url");
    header("Content-Length: 0");
    ob_end_flush();
    flush();
    //do_function_that_takes_five_mins();

http://waynepan.com/2007/10/11/how-to-use-ignore_user_abort-to-do-process-out-of-band/

Doug Cassidy
  • 1,796
  • 3
  • 17
  • 28
0

Basically, you are looking to asynchronously perform the job. I would have the php script create another thread (or process) that sends the emails asynchronously. That way, the thread that the request is running in isn't tied to the processing of the emails. The thread that is processing the request and returning the result can operating completely separately from the thread that is sending the emails.

This thread seems to be touching upon this approach, perhaps it will be more helpful regarding implementation details: Run PHP Task Asynchronously

Community
  • 1
  • 1
Localghost
  • 714
  • 3
  • 7