I have here an myAction function in some controller. And it has one class instance:
public function myAction() {
...
$myAnalyzer = new Analysis();
$myAnalyzer->analyze();
...
}
Let say this function analyze() takes 10 mins. That means it blocks my.phtml 10 mins, which is unacceptable. What I want is to render my.phtml first and then to show intermediate result from analyze() on my.phtml.
function analyze() {
...
foreach($items as $rv) {
...
...
// new result should be stored in db here
}
}
As far as I know, it's impossible, for there is just one thread in PHP. So I decided to ajax-call from my.phtml to run myAnalyzer instance.
First question: is that right? Can I do it in myAction() without blocking?
OK, now I run myAnalyzer using some script, say worker.php, from my.phtml with the help of javascript or JQuery.
Second question: how can I know when each foreach-loop ends? In other words, how can I let worker.php send some signal (or event) to my.phtml or zend framework. I do NOT want to update my.phtml on a time basis using javascript timer. That's all that I need to know, since intermediate data is supposed to be stored in DB.
Third question: the myAnalyzer muss stop, when user leaves pages. For that I have this code.
window.onbeforeunload = function(e) {
// killer.php kills myAnalyzer
};
But how can javascript communicate with myAnalyzer? Is there something like process-id? I mean, when worker.php runs myAnalyzer, it registers its process-id in zend framework. And when user leave page, killer.php stops myAnalyzer using this process-id.
I appreciate the help in advance.
First Q.: Yeah, I'm afraid that is correct.
Second Q.: I do not understand what do you mean here. See code example below
foreach($data => $item) {
...
}
//code here will be executed only after foreach loop is done.
Third Q.: Take a look at this page. You can set this to false (But I suppose it is already like that) and send something to client from time to time. Or you can set it to true and check if user is still connected with connection_aborted function. What I mean here is that you can run your worker.php with ajax and configure your request so browser will not disconnect it because of timeout (so connection will be kept while user stay on page). But it will be closed if user leave the page.
EDIT:
About second question. There are few options:
1) you may use some shared memory (like memcached, for instance). And call server with another ajax request from time to time. So after each loop is ended - you put some value into memcached and during request you can check that value and build response/update your page based on that value
2) There is such thing like partial response. It is possible to get some piece of response with XMLHTTPRequest, but as I remember - that is not really useful at this moment as it is not supported by many browsers. I do not have any details about this. Never tried to use it, but I know for sure that some browsers allow to process portions of response with XMLHTTPRequest.
3) You can use invisible iframe to call your worker.php instead of XMLHTTPRequest. In this case you can send some piece of where you can put a javascript which will call some function in parrent window and that function will update your page. That is one of Long-polling COMET implementations if you want to get some more information. There are some pitfalls (for instance, you may need to ensure that you are sending some specyfic amount of symbols in response in order to get it executed in some browser), but it is still possible to use (some web browser chats are based on this).
2) and 3) is also good because it will solve your third question problem automatically. At the same time 1) may be simpler, but it will not solve a problem in third question.
One more thing - as you will have long running script you must remember that session may block execution of any other requests (if default file based PHP session is used - this will happen for sure)
Related
Requirement:
I need to run a background process (per a user request) that takes about 30 to 60 seconds to complete. I'd like to give the user some status feedback. Note: Toly is right, 'Background' is not required.
What's working:
The process prints about 20 status messages during this time and I retrieve them with a proc_open and listening on a read pipe using fgets. I can save those messages into a session var and using timestamps (to help debug) I can see that the session array is getting written to with these messages as the process progresses.
The Trouble:
My plan was to poll the server with ajax calls (every sec) to retrieve these session vars for display in the DOM. The bottleneck seems to be that the server cannot service the ajax request while it's still running the background process. Everything dumps out at once when the background process completes. From what I can tell, the issue is not with output buffering because using (debugging) timestamps saved with each process message shows the server is writing to the session var sequentially, so that's how I know the proc_open and pipe reads are working as I expect. The issue appears to be the server not being able to give the AJAX request it's JSON object until it is done with the process; or, probably more accurately, done with the loop that is reading the pipe.
Obvious Misconception:
I thought sending a process to the background (using &) might give me a solution here. Apparently I do not know the difference between a background process and a forked process. What benefit is gained - if any - by running a process in the background when doing so appears to make no difference to me in this scenario?
Possible Solutions:
I do not expect the user initiated process that runs this
process/scenario to be that heavy, but if there's something I can
build into this solution that would help a heavy load then I would
like to do that now.
Is this a multi-threading (pthreads) or a
multi-process (fork) solution?
Or, should I save a process id,
let go polling it with a while( .. fgets ..) statement and then
come back to the process after the server has serviced the ajax
request?
I suppose I could run fake status messages and then
response accurately when the results come back after completion.
The time to process the request is not dependent upon the user, so
my fake timing could be pretty accurate. However, I would like to
know what the solution would be to provide real-time feedback.
After google-ing one day for a technique to get the same behavior you are describing here I come up with an easy solution for my project.
A bit of important theory:
- session_start () and a set like $_SESSION["x"] = "y" will always lock the session file.
Case scenario:
- A - process.php - running through an ajax call
- B - get_session.php - a second ajax call;
The main problem is/was, that even if you set a $_SESSION inside a process that is being run through an AJAX it will always have to wait the for the session file to get unlocked and it will result into a sync between the two processes (A. + B.) - both finishing at the same time!
So, the easiest way to fix this matter and get a good result is by using session_write_close() after each set. E.g.:
%_SESSION["A"] = "B";
$_SESSION["x"] = "y";
session_write_close();
PS: Best approach is to have a customed set of functions to handle the sessions.
Sorry for the mark-up. I just created an stack account.
Why would you think that you need a background process? Also, where did you get the idea that you needed one?
A normal php script, with sufficient time out set, with flush() function used every step of the way will give you the output you need for your AJAX.
What's even easier, since you use sessions - AJAX request to a separate handler, that will just check what's in session, and if there is smth new - will return you the new part.
$_SESSION['progress'] = array();
inside process.php
$_SESSION['progress'][] = 'Done 5%';
// complete some commands
$_SESSION['progress'][] = 'Done 10%';
inside ajax.php
if(count($_SESSION['progress']) > $_GET['laststep']) {
// echo the new messages
}
inside your normal page
$.ajax('ajax.php', 'GET', 'laststep=1', success: function(data){ show(data);})
Something like that should work.
Let's say that there are two PHP functions.
function first()
{
$keyword = $this->input->post('keyword');
//search for the result and show it to users.
$this->search->search($keyword);
//send the keyword to save it into the db.
$this->second($keyword);
}
function second($keyword)
{
//saving a search keyword in the db
}
I would like to send the result as soon as possible and save the search word into DB.
However, I want to run the second script later after specific time.
because I want to deliver the search result as soon as possible to my customer.
Is there a way to run second PHP script after a user run the first PHP script?
* I don't want to use Cron Job
* I wanna run those two script separatly.
* don't want to use sleep because it will stop the first script for certain times.
You'll need to route it back round to your server, but this asynchronous php approach works quite nicely (I've been using it for about a year to do almost exactly what you seem to be doing).
See: How to run the PHP code asynchronous
Instead of making a web request from PHP, as suggested by Bob Davies, I would initiate a new HTTP request from the html page after the page has been shown.
For example:
Client makes an HTTP request (search)
Server gets the stuff and returns results to the end user
The result HTML contains a snippet, such as < img src="empty.gif?searchterm=mysearch"/> which initiates another request when browser renders the html
When that another request hits your server, you can save the search to DB
Similar methods are used to do for example client tracking, when you don't want the end user having to wait your metrics stuff. Some points:
It is quite simple to understand, implement and debug, which (to my opinion) doing HTTP requests on the server side aren't
It's more scalable
It doesn't require having javascript on, just a browser rendering the HTML
The target can be any server anywhere which doesn't have to be accessible from your web server, just to end user.
If you want to use PHP only then as far as I know, you can use sleep(), but it delays the execution of the code.
I think the easiest way to delay the job is with AJAX. Simply put the second function inside another PHP document and call XMLHTTPRequest() or $.ajax (if you want to use jQuery), and delay the code for some seconds. You can do that with setInterval(), or with delay() (jQuery).
Simply write
delay(2000).function(){
//saving a search keyword in the db
}
Remember that here you should use AJAX and not PHP code.
I am trying create a small web application that allows a user to "login" and "logout."
What I am currently having a problem with is allowing the client to send constant "heartbeats" or messages to the server to notify that it is still active.
This is more of a logical question. What I want to do is have a while(1) loop in php that checks if n number of heartbeats have been skipped. I still want the client and server to be able to interact while this loop is going on (essentially I want the server to behave as if it has a separate "check_for_heartbeats" thread.
How does one accomplish this using php? I am running XAMPP at the moment. Any help would be much appreciated.
Edit: To clarify, what I want to do is be able to catch a browser close event even on instances where the window.unload event won't fire (e.g. a client gets disconnected from the internet). In this case, having a thread to monitor heartbeats seems to be the most intuitive solution, though I'm not sure how to make it happen in php.
Edit 2: isLoggedIn() is just helper function that checks to see if a session boolean variable ($_SESSION['is_logged_in')) is set.
Edit Final: Okay, so I now understand exactly what the comments and responses were saying. So to paraphrase, here is the potential solution:
Have Javascript code to send "heartbeats" to the server. The server will add a timestamp associated with these beats.
Modify the database to hold these time stamps
Query the entire "timestamps" table (more likely a 'users' table with a 'timestamp' attribute), and see if the difference between NOW and last timestamp is greater than some threshold.
"Log off" any users passed this threshold.
The only issue is if there is just one user logged in or if all users lose connection at the same time - but in these cases, no one else will be there to see that a user has lost connection.
This is a combination of multiple responses, but I think chris's response takes care of the majority of the issue. Thank you to both chris and Alex Lunix for the helpful contributions. :D
Here is a code snippet for a better explanation
Server Side:
function checkBeats()
{
while(isLoggedIn())
{
// it's been > 30 secs since last heartbeat
if((time() - $_SESSION['heartbeat']) > 30)
{
logout();
break;
}
}
}
What i usually do is call a php file using javascript (jQuery) and update a database or whatevery you like. This question might answer yours: Whats the easiest way to determine if a user is online? (PHP/MYSQL)
You could use ajax to heartbeat a script that changes the heartbeats session variable, and just at the top of every script do this check (put it in a function and call that of course):
// it's been > 30 secs since last heartbeat
if((time() - $_SESSION['heartbeat']) > 30)
{
logout();
}
Edit:
If you want the database to reflect that status instantly instead of when they next visit the page, you'll need to use MySQL. Without using another program (such as a java program) to check the database the only thing I can think of is to add this at the top of every page (in a function that gets called of course):
mysql_query("UPDATE `users` SET `loggedin`=0 WHERE heartbeat<".time()-30);
Which would update every user, which means the accuracy of the loggedin value would be set by the frequency of page views.
I need to create an event listener. I'm a novice so be kind :)
Basically I am on page1.php (php file); I want inside a loop to go check page2.xml (xml file) for some information which should be received at some point. Either check it all the time, or wait and every 5 minutes or so to see if some information has been received there. Either of them work for me.
If no info has been received after a few minutes, then I want to run again the loop (until it is received), otherwise, move forward and do something with my newly received information. This part I have no problem with, just the event listener itself. I couldn't find the function I should be using anywhere. :( I only need to check and retrieve the content of the xml file every so often.
I am not so sure how I should go about this if there isn't just a function which does this, but I couldn't find much when I searched for "event listener php".
Any help would be appreciated: reference to tutorials/sample code/even just telling me what keywords I should be looking for or what I need to learn first in order to do this.
Thanks!
Well, first you should understand the terminology you're using. PHP is not an event-driven language, it is a request-driven language. A request comes into the web-server, PHP parses it and a response is sent back to the requester. At no point are there events triggered that you can process or handle. You can implement your own "event system" but ultimately this is much more work than what your use-case entails.
Your best bet is likely utilizing AJAX and continuously making requests to your PHP script until you return the data that you are looking for. Ultimately you will need to learn about the XMLHttpRequest JavaScript object. After you understand how to make asynchronous requests utilizing JavaScript you can look at the setInterval() method for how to repeatedly make a request.
Once you can repeatedly make asynchronous requests it should be a relatively simple process of creating a webpage where you can trigger the AJAX requests to be sent.
There is no need for a loop in your PHP code. The loop is effectively done on the other end. Here's a textual workflow that you might follow:
Go to a site designed to trigger your AJAX calls and trigger them.
Make your async request to your PHP script.
Inside your PHP script open up the XML file and check for the necessary content.
Return a response in the form of a JSON object. One response can mean the data wasn't updated, the other response means the data was updated.
Parse the response, if the data was not updated repeat from step (2). If the data was updated continue to step (6).
Display a celebratory greeting that your data was updated or a notice that we are still waiting for the data to be updated. Perhaps you can have the number of tries as well, off to the side.
I did the following:
Automatically saved to database every time something new came in.
Then ran a php loop that every few minutes checked to see if there is something new in the database which fits the parameters of this new event (including that it happenned within the timeframe of the past few minutes). I used flush(); and then sleep(120); in the loop to get the loop to keep running every few minutes, until the new info came in in which case it will break(); or die();.
I did something like this writing an inbox parser in PHP. You're best option is to:
Code page1.php in which you just need to do 2 things: read XML from page2.xml and
if there is something "new" just execute the data-parsing
code.
Setup a Cron job (if you're under linux) to execute every 5 minutes or so (Cron command is something like: php /path/to/page1.php). In the same way, if you're running Windows you can setup a scheduled task and execute the same command. Be aware that the full path to your PHP installation should be in PATH environment variable.
I am looking for a way to start a function on form submit that would not leave the browser window waiting for the result.
Example:
User fills in the form and press submit, the data from the form via javascript goes to the database and a function in php that will take several seconds will start but I dont want the user to be left waiting for the end of that function. I would like to be able to take him to another page and leave the function doing its thing server side.
Any thoughts?
Thanks
Thanks for all the replies...
I got the ajax part. But I cannot call ajax and have the browser move to another page.
This is what I wanted.
-User fills form and submits
-Result from the form passed to database
-long annoying process function starts
-user carries on visiting the rest of the site, independent of the status on the "long annoying process function"
By the way and before someone suggests it. No, it cannot be done by cron job
Use AJAX to call the php script, and at the top of the script turn on ignore_ user_ abort.
ignore_user_abort(true);
That way if they navigate away from the page the script will continue running in the backround. You can also use
set_time_limit(0);
to set a time limit, useful if you know your script will take a while to complete.
The most common method is:
exec("$COMMAND > /dev/null 2>&1 &");
Ah, ok, well you're essentially asking therefore, does PHP support threading, and the general answer is no... however...
there are some tricks you can perform to mimick this behaviour, one of which is highlighted above and involves forking out to a separate process on the server, this can be acheived in a number of ways, including the;
exec()
method. You also may want to look here;
PHP threading
I have also seen people try to force a flush of the output buffer halfway through the script, attempting to force the response back to the client, I dont know how successful this approach is, but maybe someone else will have some information on that one.
This is exactly what AJAX (shorthand for asynchronous JavaScript + XML) is for;
AJAX Information
It allows you to code using client side code, and send asynchronous requests to your server, such that the user's browser is not interuppted by an entire page request.
There is alot of information relating to AJAX out there on the web, so take a deep breath and get googling!
Sounds like you want to use some of the features AJAX (Asynchronous Javascript and XML - google) have to offer.
Basically, you would have a page with content. When a user clicks a button, javascript would be used to POST data to the server and begin processing. Simultaneously, that javascript might load a page from the server and then display it (eg, load data, and then replace the contents of a DIV with that new page.)
This kind of thing is the premise behind AJAX, which you see everywhere when you have a web page doing multiple things simultaneously.
Worth noting: This doesn't mean that the script is running "in the background on the server." Your web browser is still maintaining a connection with the web server - which means that the code is running in the "background" on the client's side. And by "background" we really mean "processing the HTTP request in parallel with other HTTP requests to give the feel of a 'background' running process"