Is it possible to run a function when the session ends on a PHP script? My script asks several questions and the user has 30 minutes to answer or the session expires. I would like my script to save any progress to a .txt file if the session expires and the user has not completed all the answers. How would I go about doing this?
I heard about register_shutdown_function('shutdown'); but I'm confused on where I would call it in my script.
My current script starts like this:
ini_set('session.gc_maxlifetime',1800);
ini_set('session.gc_probability',1);
ini_set('session.gc_divisor',1);
session_start();
if($_SESSION['loggedin'] !== 1) {//checks to see if user has logged in
header('Location: login.php');
exit;}
...asks a bunch of questions....
session_destroy();
Thanks for your help.
You have actually two options:
Save the progress when it happens (after every answer) and save information whether it has been completed. This solution works even if the browser has been closed (the progress is saved in real time).
Use AJAX to call the server every eg. 5 seconds and server should return information whether the session ended or not. If session ended, then do in JS what you need (even redirect to different page). This solution does not ensure saving the progress (the browser may be closed before action is made).
You can combine both options, depending on what you need and how your application works.
If page reloads after every answer (eg. reloads to show next question), then you can use option no. 1. If all the questions are on one page, choose option no. 2. If your application is combination of the two, you can choose both options.
EDIT:
Judging from your code, you are mixing:
session in terms of the time for answering questions with
session in terms of storing values between requests to the page in PHP.
The best idea is to separate the two. Give PHP session a much larger time limit, because it is needed for some other things such as actually showing progress or even saving progress. Instead, mark the time when the test began and store it in PHP session. This way after the time for the test is exhausted, you will be able to determine that and ignore any questions answered after that.
There's a couple of methods that come to mind immediately. You could use PHP's built in sleep method or you could use AJAX. Not saying these are the only ways, just the first ones that come to mind.
Edit: Now that I think about it, sleep probably isn't an option as it would exceed maximum run time and throw an error. Haven't tested to make sure, but seems logical. So AJAX is your best bet, assuming I'm not missing anything.
Related
I have a PHP script to pull user specific data from a 3rd party source and dump it into a local table, which I want to execute every X mins when a user is logged in, but it takes about 30 seconds to run, which I don't want the user to experience. I figured the best way of doing this would be to timestamp each successful pull, then place some code in every page footer that checks the last pull and executes the PHP script in the background if it was more than X minutes ago.
I don't want to use a cron job to do this, as there are global and session variables specific to the user that I need when executing the pull script.
I can see that popen() would allow me to run the PHP script, but I can't seem to find any information relating to whether this would be run as the visitor within their session (and therefore with access to the user specific global or session variables) or as a separate session altogether.
Will popen() solve my problem?
Am I going about this the right way or is there a better method to execute the script at intervals while the user is logged in?
Any help or advice is much appreciated, as always!
Cheers
Andy
Maybe an idea to put the session data also in a table.
That way you can easily access it from your background process. You only have to pass the session_id() or the user id as argument so the process knows which user it is currently processing.
No, because PHP still needs to wait on the process started by popen() before it can exit
Probably not, because the solution doesn't fit the architectural constraints of the system
Your reasoning for not using a cron job isn't actually sound. You may have given up on exploring that particular path too quickly and drawn yourself into a corner with trying to fit a square peg in a round hole.
The real problem you're having is figuring out how to do inter-process communication between the web-request and the PHP running from your crontab. This can be solved in a number of ways, and more easily then trying to work against PHP's architectural constraints.
For example, you can store the session ids in your database or some other storage, and access the session files from the PHP script running in your crontab, independently of the web request.
Let's say you determine that a user is logged in based on the last request they made to your server by storing this information in some data store as a timestamp, along with the current session id in the user table.
The cron job can startup every X minutes, look at the database or persistence store for any records that have an active timestamp within the given range, pull those session ids and do what's needed.
Now the question of how do you actually get this to scale effectively if the processing time takes more than 30 seconds can be solved independently as well. See this answer for more details on how to deal with distributed job managers/queues from PHP.
I would use Javascript and AJAX requests to call the script from the frontend and handle the result.
You could then set the interval in JavaScript and send an AJAX-Request each interval tick.
AJAX is what you need:
http://api.jquery.com/jquery.ajax/
The use the method "success" to do something after those 30 seconds.
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.
Is there a way to detect when someone leaves your site? So far I know about using ignore_user_abort() but thats about as far as I've gotten.
The reason I need to know this is because I need a script to execute upon someone leaving the site.
Record a timestamp in a database for each user and update this upon each page load (or through AJAX).
When the last updated timestamp is older than a cetain amount of time (30 mins?) you can consider they've left the site and do your code, deleting the entry afterwards.
Idea: a "user" could be a primary key made up of their IP and user agent as an MD5 hash for example.
1) I've found the Session Timeout Warning PHP Example with jQuery/JS article
2) Session timeout in PHP thread
3) Also article on how to do it with jQuery and ColdFusion
I can be done easily using javascript, check this solution
<body onbeforeunload="winCloseFunc();"> =)
Due to the stateless nature of http, there isn't an easy way to tell when someone leaves a site. It is possible to trigger onUnload when they leave a page, but this also fires as they navigate from page to page on your site.
Mozilla's Ref: https://developer.mozilla.org/en/DOM/window.onunload
MS's Ref: http://msdn.microsoft.com/en-us/library/ms536907(v=vs.85).aspx
Normally a session is setup to just expire when the user hasn't been seen in X minutes.
I have a PHP script something like:
$i=0;
for(;$i<500;++i) {
//Do some operation with files numbered 0 to 500;
}
The thing is, the script works and displays the end results, but the operation takes a while and watching a blank screen can be frustrating. I was thinking if there is some way I can continuously update the page at the client's end, detailing which file is currently being worked upon. That is, can I display and continuously update what is the current value of $i?
The Solution
Thanks everyone! The output buffering is working as suggested. However, David has offered valuable insight and am considering that approach as well.
You can buffer and control the output from the PHP script.
However, you may want to consider the scalability of this design. In general, heavy processes shouldn't be done online. Your particular case may be an edge in that the wait is acceptable, but consider something like this as an alternative for an improved user experience:
The user kicks off a process. This can be as simple as setting a flag on a record in the database or inserting some "to be processed" records into the data.
The user is immediately directed to a page indicating that the process has been queued.
An offline process (either kicked off by the PHP script on the server or scheduled to run regularly) checks the data and does the heavy processing.
In the meantime, the user can refresh the page (manually, by navigating elsewhere and coming back to check, or even use an AJAX polling mechanism to update the page) to check the status of the processing. In this case, it sounds like you'd have several hundred records in a database table queued for processing. As each one finishes, it can be flagged as done. The page can just check how many are left, which one is current, etc. from the data.
When the processing is completed, the page shows the result.
In general this is a better user experience because it doesn't force the user to wait. The user can navigate around the site and check back on progress as desired. Additionally, this approach scales better. If your heavy processing is done directly on the page, what happens when you have many users or the data processing load increases? Will the page start to time out? Will users have to wait longer? By making the process happen outside of the scope of the website you can offload it to better hardware if needed, ensure that records are processed in serial/parallel as business rules demand (avoid race conditions), save processing for off-peak hours, etc.
Check out PHP's Output Buffering.
Try to use:
flush();
http://php.net/manual/ru/function.flush.php
Try the flush() function. Calling this function forces PHP to send whatever output it has so far to the client, instead of waiting for the script to end.
However, some web servers will only send the output once the entire page is done being built, so calling flush() would have no effect in this case.
Also, browsers themselves buffer input, so you may run into problems there. For example, certain versions of IE won't start displaying the page until 256 bytes has been received.
I am trying to make some changes to an opensource project. I want to keep track of when users log in and log out.
Right now I change their login status in db when they login or manually log out. The problem right now is that I cannot find out if the user just closed their browser without pressing on logout button.
For this reason I need to trigger a function that will change database every time the user's session expires.
I've tried session_set_save_handler in PHP, but it looks like I need to override the whole session behavior. What I am looking for is to keep default session behavior and just add functionality when the user's session expires. Is there a way to do that?
I did something really nasty once. Every time a session was "updated" by a page refresh / fetch / etc., I updated a timestamp on a DB row. A second daemon polled the DB every 10 minutes and performed "clean-up" operations.
You won't find any native PHP facilities to achieve your goal. Session timeout doesn't run in the background. You won't even know if a session is timed out, unless a timed out session attempts another access. At this point, nearly impossible to trap, you can make your determination and handle it appropriately.
I'd recommend a queue & poll architecture for this problem. It's easy and will definitely work. Add memcached if you have concerns about transaction performance.
I presume you're using standard PHP file-based sessions. If that's the case, then PHP will do its own garbage collection of stale sessions based on the session.gc_* configuration parameters in php.ini. You can override those to disable the garbage collector completely, then roll your own GC script.
You could either check the timestamps on the files (quick and easy to do in a loop with stat()) to find 'old' sessions, or parse the data in each file to check for a variable that lists the last-access time. Either way, the session files are merely the output of serialize($_SESSION) and can be trivially re-loaded into another PHP instance.
What about window close event on javascript. So basically session is destroyed when all of the windows of the session site are closed. So, when the last window is closed ( this is checked via additional js checking ) send ajax request to server.