views:

575

answers:

5

PHP provides a mechanism to register a shutdown function:

register_shutdown_function('shutdown_func');

The problem is that in the recent versions of PHP, this function is still executed DURING the request.

I have a platform (in Zend Framework if that matters) where any piece of code throughout the request can register an entry to be logged into the database. Rather than have tons of individual insert statements throughout the request, slowing the page down, I queue them up to be insert at the end of the request. I would like to be able to do this after the HTTP request is complete with the user so the length of time to log or do any other cleanup tasks doesn't affect the user's perceived load time of the page.

Is there a built in method in PHP to do this? Or do I need to configure some kind of shared memory space scheme with an external process and signal that process to do the logging?

+1  A: 

Aside from register_shutdown_function() there aren't built in methods for determining when a script has exited. However, the Zend Framework has several hooks for running code at specific points in the dispatch process. For your requirements the most relevant would be the action controller's post-dispatch hook which takes place just after an action has been dispatched, and the dispatchLoopShutdown event for the controller plugin broker.

You should read the manual to determine which will be a better fit for you.

EDIT: I guess I didn't understand completely. From those hooks you could fork the current process in some form or another. PHP has several ways to fork processes as you can read in the manual under program execution. I would suggest going over the pcntl extension - read this blog post to see an example of forking child processes to run in the background.

Eran Galperin
Neither of those hooks solves my problem by themselves. Those hooks are still being run within the HTTP request. As far as the browser is concerned the page is still being loaded. For example, say the logging took 5 seconds, the browser would see a 5+ second load time.
D-Rock
I see. I edited my answer to suggest how to use those hooks to fulfill your requirements.
Eran Galperin
A: 

The comments on this random guy's blog sound similar to what you want. If that header trick doesn't work, one of the comments on that blog suggests exec()ing to a separate PHP script to run in the background, if your Web host's configuration allows such a thing.

Nicholas Piasecki
A: 

This might be a little hackish for your taste, but it would be an effective and simple workaround.

You could create a "queue" managed by your DB of choice, and first, store the request in your queue table in the database, then output an iframe that leads to a script that will trigger the instructions that match up with the queue_id in your database.

ex:

<?php
mysql_query('INSERT INTO queue ('instructions') VALUES ('something would go here');
echo('<iframe src="/yourapp/execute_queue/id?' . mysql_insert_id() . '" />');
?>

and the frame would do something like

ex:

<?php
$result = mysql_query('SELECT instructions FROM queue WHERE id = ' . $_GET['id']);
// From here, simply execute some instruction based on the "instructions" field, then delete the instruction from the database.
?>

Like I said, I can see how you could consider this hackish, but the frame will load independent of its parent page, so it would achieve what you want without running some other app in the background.

Hope this at least points you in the right direction.

Cheers!

Mike Keen
The point of the exercise is to avoid doing the DB inserts while the user's browser is connected. If I had to insert the items into a queue in the DB to be processed later, why wouldn't I just put them where they go in the DB in the first place?
D-Rock
It sounded like your main concern was the perceived load time for the user. Offloading all of those instructions to an iframe would certainly accomplish this. If you don't want to use a database for the queue, use memcache.
Mike Keen
There are other queuing systems than using a table in MySQL. A lot of them are light weight. For example, see Amazon SimpleDB.
Gary Richardson
Thanks for mentioning SimpleDB Gary. This would actually be perfect considering the problem "D-Rock" is having.
Mike Keen
+4  A: 

If you're really concerned about the insert times of MySQL, you're probably addressing the symptoms and not the cause.

For instance, if your PHP/Apache process is executing after the user gets their HTML, your PHP/Apache process is still locked into that request. Since it's busy, if another request comes along, Apache has to fork another thread, dedicate more memory to it, open additional database connections, etc.

If you're running into performance problems, you need to remove heavy lifting from your PHP/Apache execution. If you have a lot of cleanup tasks going on, you're burning precious Apache processes.

I would consider logging your entries to a file and have a crontab load these into your database out of band. If you have other heavy duty tasks, use a queuing/job processing system to do the work out of band.

Gary Richardson
A: 

Well I ran into the same problem - I didn't want to block a redirect while sending a report over XMPP (Jabber). It seems, there is no solution for this... :-(