4

Anyone know how to close the connection (besides just flush()?), but keep executing some code afterwards.

I don't want the client to see the long process that may occur after the page is done.

hakre
  • 193,403
  • 52
  • 435
  • 836
Kristopher Ives
  • 5,838
  • 7
  • 42
  • 67
  • 1
    Why would you need too? PHP takes mere milliseconds to run a normal script. Anything that takes longer shouldn't be accessible by the general public. – animuson Jul 17 '10 at 06:00
  • animuson, while I agree with your statement generally, we don't know whether or not this application/website is accessible to the general public. It could very well be an application running internally somewhere. So I fail to see the relevance of your comment... – kander Jul 17 '10 at 09:17

5 Answers5

9

You might want to look at pcntl_fork() -- it allows you to fork your current script and run it in a separate thread.

I used it in a project where a user uploaded a file and then the script performed various operations on it, including communicating with a third-party server, which could take a long time. After the initial upload, the script forked and displayed the next page to the user, and the parent killed itself off. The child then continued executing, and was queried by the returned page for its status using AJAX. it made the application much more responsive, and the user got feedback as to the status while it was executing.

This link has more on how to use it:

If you can't use pcntl_fork, you can always fall back to returning a page quickly that fires an AJAX request to execute more items from a queue.


mvds reminds the following (which can apply in a specific server configuration): Don't fork the entire apache webserver, but start a separate process instead. Let that process fork off a child which lives on. Look for proc_open to get full fd interaction between your php script and the process.

Community
  • 1
  • 1
Jhong
  • 2,714
  • 22
  • 19
  • note that you can only use pcntl_fork() (in this context) with a custom made http server written in php itself - so typically this response can only be used about 0.0001% of the time ;) – nathan Jul 17 '10 at 06:15
  • Then you fork the entire apache daemon I think. That's not a pretty sight, and probably not the intended use of an apache module... – mvds Jul 17 '10 at 08:50
  • I believe it forks the apache worker process, of which the process would be one of many (albeit way too many by default). That, coupled with the fact that the average linux server is pretty good at forking processes, leads me to expect it is not as problematic as you might expect. My experience was not for a mission-critical app though. As I say though, this is only the minor part of the solution -- the main architecture of the solution is setting up the client - server model properly. – Jhong Jul 17 '10 at 12:49
5

I don't want the client to see the long process that may occur after the page is done.

sadly, the page isn't done until after the long process has finished - hence what you ask for is impossible (to implement in the way you infer) I'm afraid.

The key here, pointed to by Jhong's answer and inversely suggested by animusen's comment, is that the whole point of what we do with HTTP as web developers is to respond to a request as quickly as possible /end - that's it, so if you're doing anything else, then it points to some design decision that could perhaps have been a little better :)

Typically, you take the additional task you are doing after returning the 'page' and hand it over to some other process, normally that means placing the task in a job queue and having a cli daemon or a cron job pick it up and do what's needed.

The exact solution is specific to what you're doing, and the answer to a different (set of) questions; but for this one it comes down to: no you can't close the connection, and one would advise you look at refactoring the long running process out of that script / page.

nathan
  • 5,402
  • 1
  • 22
  • 18
4

Take a look at PHP's ignore_user_abort-setting. You can set it using the ignore_user_abort() function.

An example of (optional) use has been given (and has been reported working by the OP) in the following duplicate question:

It basically gives reference to user-notes in the PHP manual. A central one is

which is also the base for the following two I'd like to suggest you to look into:

Community
  • 1
  • 1
BrandonG
  • 367
  • 1
  • 5
  • This is the *actual* best answer. He states that the client doesn't need to know the result of the process, so he doesn't need to use forking, queues or any of that stuff. – pjbeardsley Feb 01 '13 at 15:26
  • @pjbeardsley: Actually this is not the best answer because it comes with some problems unnoticed: First of all the *`ingnore_user_abort`* is optional according to the linked sources, yet named first and necessary. Second, the answer as presented here had been given earlier and was not linked. Also the source-code was missing (this was also an issue for the linked duplicate but fixed there now). However, the linked examples presented do what you say for some webserver / php configurations quite well and are some kind of a *common* to do so. So it's not a totally bad answer either. – hakre Aug 24 '13 at 09:27
3

Don't fork the entire apache webserver, but start a separate process instead. Let that process fork off a child which lives on. Look for proc_open to get full fd interaction between your php script and the process.

mvds
  • 45,755
  • 8
  • 102
  • 111
  • This is likely an addition to an existing answer, especially the more lengthy fear you have that pcntl_fork would do something bad / resource intensive in a very special Apache + PHP inside of Apache SAPI module usage. This should be a comment to the existing answer or an additional edit for the things to think about when using that answer, not an additional answer because it's not really clear for what it is worth. I merged it and I ask you to delete it. - Also as I'm thinking more about it, there should be some reference that explains this better, like a link, perhaps to `fork` in PHP. – hakre Aug 24 '13 at 07:49
  • @hakre I don't agree with you. The question is "how do I run code after running a PHP script" and the answer is to "start a separate process instead. Let that process fork off a child which lives on. Look for proc_open to get full fd interaction between your php script and the process." – mvds Aug 25 '13 at 08:24
  • @hakre, and if you don't agree, simply downvote, that's the way this site works. – mvds Aug 25 '13 at 08:26
2

We solved this issue by inserting the work that needs to be done into a job queue, and then have a cron-script pick up the backend jobs regularly. Probably not exactly what you need, but it works very well for data-intensive processes.

(you could also use Zend Server's job queue, if you've got a wad of cash and want a tried-and-tested solution)

kander
  • 4,226
  • 1
  • 22
  • 43