Edit: seems I write my posts a tad slower than sneaky...
Well the principle is there and needs little modification.
# all your "real" output here, spinner included if you want to show that.
# ...
# Just before you start mailing or whatever else is time consuming and not necessary to
# show the current page
echo str_repeat(' ', 4096);
# or just ob_flush() if you need more output buffering
ob_end_flush();
If PHP is NOT run as an Apache module (CGI works) you can use pcntl_fork, assuming that you are on *NIX and php was compiled with support for it. There should be some pecl extension for running services under windows.
There are several things to take care of when forking. The overall process is describe below, but you may want to read more on this. Here are some links I found that may be good starting points:linux daemon howto, php pcntl fork, unix daemon programming (in C rather than PHP, but the principles are the same).
If you have any open resources, such as a database connection, they need to be closed down,
either before or directly after you fork, since the child process is an exact copy of the parent
and these resources should not be shared.
If you have output buffering turned on, it should be flushed and turned off.
The file descriptors for stdin, stdout and stderr should be closed and then redirected to /dev/null or a log file for stdout or stderr if either is used for display_errors (in php.ini)
pcntl_fork() returns < 0 on error, > 0 in the parent process and 0 in the child process.
This is how you distringuish between parent and child code execution after the fork
### This corresponds to the part before echo str_repeat() in the previous code.
# output the webpage doing as little work as is needed just for this purpose
echo '<html><head>Title</title><body><div>Processing emails</div></body></html>';
### Prepare to fork - This would be where echo str_repeat() was.
# Close db connections etc
mysqli_close($link);
# If you have output buffering turned on, it should be closed
ob_end_flush();
# error forking
if ($pid = pcntl_fork() < 0)
{
# But since no fork was performed in this case, here you could instead
# fallback to doing it as in the code example without forking above
exit 1;
}
# Parent process (the initial process). This process should exit
elseif ($pid)
{
exit 0;
}
### The child process will continue code execution here
# Now you have to make this process the session leader
# Returns -1 on error
if (($sid = posix_setsid()) < 0)
{
exit 1;
}
# close file descriptors for stdin, stdout and stderr
# After this point you can no longer use echo and print.
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
/* The fopen calls will commence with File Descriptor 0, which is stdin,
* then FD 1 which is stdout, and then FD 2 which is stderr
*/
$stdIn = fopen('/dev/null', 'r'); # File descriptor 0, stdin
$stdOut = fopen('/dev/null', 'w'); # FD 1, stdout
# Assuming display_errors is set to sdterr, you might want to point
# stderr to an error log
$stdErr = fopen('/path/to/log', 'w'); # Set FD 2, stderr
# If your child process requires DB connection or other such resources
# this process can now acquire them as needed
$link = mysqli_connect(/* ... */);
### Here goes the actual work
# I.e. send your emails
If this is not a viable option, you could also perform a call to system() to start running another script in the background.
# If any command line arguments are needed, escape them for safety.
$arg1 = escapeshellarg('argument');
# Execute script in the background - script.php would be the one sending the emails
exec("php /patho/to/script.php $arg1 2>/dev/null >&- /dev/null &");