I was trying to understand the way sleep works for the multiple request from the same machine.
For example, I want to track the user login and logout in one of my application and I have done as explained below:
An ajax request is sent to browser when an user tries close/refresh the current browser. What I have done is binded an event on page unload, which sends an ajax request on server, where I have set up the session timestamp value in session and used the sleep(10) as below ->
//sets the timestamp value
$session->timestamp = time();
//wait if another request arrives withing 10 seconds
sleep(10);
if ($session->timestamp) {
//clear user session here
$session->timestamp = null;
}
Since I only want to reset the session only when user is trying to close his browser tab/window, it works fine in this case and reset the session after 10 seconds.
But, I do not want to reset the session in case of page refresh. Since in the case of page refresh, Again the page unload event is called first which sets the timestamp value in session, but the request is being for refresh and it sets the timestamp value at very first place as in below code:
if ($session->timestamp) {
$session->timestamp = null;
}
Now what I am thinking is when 10 seconds complete in sleep call of previous request the timestamp value is set to null and therefore the user session should not be reset.
But somehow this is not the case. Might be I am missing something here.
If you want to destroy user session after he closes browser, just set cookie without expiring date.
You've only explained your problem in the comments - not in the question. Each http request is handled in isolation regardless if they are from the same machine, however when a client emits an http request, it may re-use an existing connection in which case the request will be blocked until the earlier request on the same socket is processed.
I need to track the login/logout activity of an user on my application
The right way to do this is in the session handler (and not embedding time information inside the session itself) or via SSO. You can set a very short TTL and use a javascript invoked beacon to get better granularity of the end-of-session time in the absence of an explicit logout.
Unless the user specifically logs out there wont be a logout event. Because of the way websites work you can't force the user to logout. Usually sessions just time out (30 minutes is a pretty common value). If a timely logout is imporant to you consider switching it around and implement a keep-alive ticker that will periodically (every 5 minutes?) send the server a message that the user is still logged in (= has the website open in a tab).
In the session table keep a timestamp of last notify, if that timestamp is too old you can consider the user has logged out. Run a scheduled job to record the log out 'event'.
Using onunload to perform an AJAX request is very unreliable because you can not force the browser to wait and you can not distguinish between a reload/redirect or a close.
I have a problem with logged in users closing their browsers.
My code can't run due to the browser closing and so their logonstatus cant update to 'N' in the database. Also due to the session being destroyed they cant go back to the main pages as I have this code if (!isset($_SESSION['logged in'])) { etc to prevent people from viewing any pages without logging in.
When a user logs on their logonstatus changes to 'Y' and I record the time they logged in.
I record their lastactivity time on each page load.
I redirect users to the login page and change their logonstatus if they have been idle for 20 min on a page.
I also have a cron job due to the browser close issue which runs every 5 minutes and checks if the users last activity has been longer than 20 min and if so their logonstatus becomes 'N'
I think users having to wait 20+ min to re-login due to browser close is too long and so I would like to make it possible to login in again straight away.
I have read about the unload functions of javascript but apparently it is unreliable.
Is there any other way I could go about this?
Closing the browser is always a client side action. So you will need javascript to send the action to the server for PHP to do something.
You can use onbeforeunload to send something to the server, but it is indeed unreliable. A more reliable method is to make the session time a lot shorter (eg: 2min) and then have an ajax call every 30seconds to the server to keep the session alive (make sure its a page with a very small impact on server/connection). If the request fails 4 times, the session is destroyed. Now your cronjob can run every 2mins and a user only has to wait that long.
Another approach is to store a cookie on the users computer with a GUID and save it in the database with the "Logged ='Y'". Now when somebody tries to log in to an account which is already logged in, check if its the same user (cookie) and if so, allow it.
This still makes it possible for one user to log in twice, just harder and not by mistake.
You need to change the duration of your session cookies so that they last as long as the browser window remains open; do this with session_set_cookie_params, setting the lifetime to 0. Don't forget to make sure that your cron script and PHP's session gc max lifetime don't delete sessions before 20 minutes have passed.
Since you keep a record of their last access time and check it on each request, you can continue to log out people after 20 minutes of inactivity (just destroy their session and redirect to the login page).
I have an online game. I wish to show how many user are online. The problem is to know when a user is offline.
Is there a way to perform a check on sessions cookie to acknowledge whether the session with the broswer was closed?
I was thinking about simply set a timeout on the server which launch a script that count how many session cookie are present, but how do I check if the session cookie is about somebody who's logged and not just a visitor?
How did you handle this?
1) I don't want to rely on a script fired with the logout button, since nobody ever logout... people simply close the browser.
2) About timestamps and registering activity? Since in my game users interact with an svg (not moving through pages), they generate a huge amount of clicks. Making a query for each click for each of them refreshing a record would be very expensive.
When the user interacts with the site, set their last activity time.
If it is longer than 30 mins or so, you can assume they are offline.
You can also explicitly set someone to offline when they click logout.
However, your case is a little different. You could use a heartbeat style script.
Whilst they are on the page, use setInterval() to extend the expiry date, up to a maximum range (in case the user leaves their browser window open for hours on end).
Since your code gets executed when the page is loaded you cannot make a check if the user closed his browser or not.
So the common approach would be to use timestamps and update this stamp if the user does something on your site and if the timestamp is older than say 5 minutes you just assume he is offline
I need a php code that will run a query after a specific session is going to expire,
if(!isset($_SESSION['test'])) query;
something like that but does work with session expire.
The problem you'll run into: a session doesn't just expire by itself, a session is expired on the next page load if the user waited too long. As there's no communication between browser and server between requests (user action), the server won't know if e.g. the user just closed the browser (or is doing the dishes while the website remains open).
So I guess there's nothing wrong with your code as it'll tell you if the sessin is expired on the next pageload (as long as there is a next pageload :)).
If you really need to make sure, the query runs after a session expired, I guess you'll have to save your sessions to a database and run a "cleanup"-script on each pageload to run your query and get rid of expired sessions. (e.g. save "lastUserAction" and compare that to whatever your session-limit is)
I would suggest to save the date you created the session in the database and check if the session expires like:
if($db->checkExpiredSession('test') == true) {
$db->query('...');
}
Just a thought.
I have a community site where a user can have many friends. When displaying all of his friends, I want to include whether his friend is online or offline.
My method is, when user logs in, create a session and update the users table, on status column "online". If he click the logout button, then i will set the status to "offline". What if he close his browser without clicking the logout button? Here is what I want to do:
session_start();
if (!isset($_SESSION['LAST_ACTIVITY'])) {
// initiate value
$_SESSION['LAST_ACTIVITY'] = time();
}
if (time() - $_SESSION['LAST_ACTIVITY'] > 3600) {
// last activity is more than 10 minutes ago
session_destroy();
//direct to a php, say this user is idle and thus status = offline
header("location: update_status.php?user=".$_SESSION['username']."&status=offline");
// den redirect them to login page
} else {
// update last activity timestamp
$_SESSION['LAST_ACTIVITY'] = time();
}
Is this an appropriate way?
EDIT:
It would be helpful to see some easy sample code with how to check whenever a user is online and update whenever a user visits a page?
Do I need to include php?user=$_SESSION['userid'] in every link?
Well if you determine whether or not a user is logged out, simply by your online/offline column - then users closing their browser window (without hitting your logout link/button) will still be logged in.
The normal approach here is to keep track of when your users move around your site, storing the last time of when they navigated to a page. Then you set a predefined constant of what makes a user active (say navigating around your page within the last 15 minutes) - and then you use this on your SQL Query to grab every user that is a friend of the visiting person and has been on the site within the last 15 minutes.
In a SQL Query where you store your time as datetime (this is a query for MySQL) this could look something like:
SELECT col1, col2, col3 FROM Users WHERE DATE_ADD(LastActive, INTERVAL 15 MINUTE) > NOW() AND UserIsFriendOfCurrentUser
Of course your query would need adapted to fit your setup better, but hopefully you get the idea.
In my Opinion this will not work. When the User closes the Browser, the code you put here will never be called.
A possible way would be to save the last time the user was active in the database, whenever the User calls a page. If the Users last Activity was longer than say 5 minutes ago, you could count him as offline, and show this to other Users.
I think that phpbb3 does it that way...
Getting an Event when the User closes the Browser is heavy and does not always work.
After the header location redirect, definitely put this line:
die();
Not all user agents (browsers, web spiders, etc) will listen to your redirect header. Killing the script is the only safe way to make sure they don't get the rest of the page.
As far as I understand your solution is the user still marked as online even when the last time the user was on the site is more then 10 minutes ago.
You should save in your database, the last time an user was active on your site. When a user looks then on the list of friends you get also this time and look if that is more then 10 minutes ago, i.e. the friend is inactive.
The best way to do this is to store activity times for logged in users. When they navigate to a page, or perform an action, update their "last activity" time in the database with the current time. If this time falls outside of a threshold (say, 15 minutes), then you can class the user as no longer active.
In our project we use this technique:
every user after login is being associated with session id.
browser sends an "activity-request" with AJAX by timer.
a lightweight server-side script updates last activity in DB, searching
user by session id.
cronjob updates all timed-out users, marking them as offline
(actually session id gets empty), and
executes logout routines for every
user to avoid corrupted data on
business-level layer.
What happens to user?
Until user closes his browser - activity is being updated. After closing the browser user gets timed out and marked as offline. When user tries to reach any location which requires authorization - the first thing we try to do is to find him in DB by session id, and if not - he just can see nothing but login form (i.e. include(); exit();).
Bad thing - tabbed browsers use the same session id for each opened tab in the same window. But our project is a game and multi-users from one browser are not allowed. Actually it's a very rare situation wher 2 or more users try to login from different tabs of one browser...
I see a couple of problems in your code:
if (time() - $_SESSION['LAST_ACTIVITY'] > 3600) {
// last activity is more than 10 minutes ago
session_destroy();
//direct to a php, say this user is idle and thus status = offline
header("location: update_status.php?user=".$_SESSION['username']."&status=offline");
}
In this piece of code you destroy the session and then get the username from the session you just destroyed.
By the way, if I understand your question, you don't get the intended behaviour with this code.
You check if the user is just accessing your site (owner of the session you start) has been idle form more than 10 minutes and, if true, you disconnect him just now that it tries to reenter in the site.
You shouldn't use $_SESSION for storing the last activity time. Every time a user goes to a page, update a column called last_activity with the current time in the users table:
<?php
session_start();
$userId = (int)$_SESSION['user']; // make sure it's an integer
$db->exec('
UPDATE users
SET last_activity = NOW()
WHERE id = ' . $_SESSION['id']
);
//...
When you retrieve your list of friends, add an if statement to check if they are online (this is the MySQL version):
SELECT
u.user_name, u.name, etc,
IF(
UNIX_TIMESTAMP() - UNIX_TIMESTAMP(u.last_activity) < 300,
1, 0
) as online
...
The 300 is seconds so 5 minutes. Adjust this as you see fit. Then your friends can be accessed like so:
$friends = $db->query(/* above SQL */);
foreach($friends as $friend)
{
if($friend['online'] == 1)
// online specific stuff
else
// offline specific stuff
}
Database sessions may be a good way for you to manage who is online and who isn't. Theoretically they should expire on their own. I wrote a how-to on my blog about it, you can read more at http://brennydoogles.wordpress.com/2011/09/06/taking-control-of-your-php-sessions-using-database-sessions/
This also has the added bonus of allowing you to kill a user session remotely if you need to :)