Link to home
Start Free TrialLog in
Avatar of Richard Korts
Richard KortsFlag for United States of America

asked on

Want timeout warnings in php app

Have a php app, using standard session variables & standard 20 minute timeout. We would like to issue a warning to user after 10 minutes on no activity, again at 15 and finally at 19 minutes. Then display "You have been logged out message at 20 minutes".


Can someone point me to techniques for doing this?


Thank you

Avatar of Scott Fell
Scott Fell
Flag of United States of America image

At log in, set a client side cookie with a timestamp. With every page refresh, reset that cookie value. At the same time have a javascript on every page that reads the coookie every minute. If it is at the 10, 15 or 19 stage, present the appropriate alert or notification.  At the 21 stage, throw another alert to that they are about to be logged out. Have a timer set for a minute. If no action, redirect to the logout page. If they elect to stay logged in, reset the cookie with the current time stamp.

I have built that in to my log in system where I have a table that tracks logins and instead of storing a cookie, I journal it in the table. This is for private admin systems though where there are only less than 50 users and the requirement was to track user log ins.  
Hi,

If the user is not doing any action, good chance he is not in front of the computer, so I don't think these alert will really help, when the user will be back, the session will be probably ended anyway and he will be log off and redirected to the login form...

Checking every minute can take a lot of resssource and cause other script conflict.

Of course that all depend of the application type, I guess you have good reason to implement this.

I do use session cookie with expiration, I do save last login in DB, I don't really use it but this is usefull if you need to track login let say you want to know if the employee has login recently. I do plan to save the logout as well to check login duration.
So I guess I would use these DB data instead of setting extra cookies...
Avatar of Richard Korts

ASKER

Scott, I like the idea of using a database table. Are you suggesting javascript that uses ajax that reads the table data?

Also, I presume table updated resetting timer on every page load?

Thank you,

Richard
Yes, every time there is a log in, I collect either the user's id number or username.  Either one that I can use to tie it back into the user or contact record.

Then the timestamp, IP ( I realize it is not going to be 100% accurate, but it helps). That is the minimum.  Depending on what I am doing and the needs, I also collect the page that the user logged in from (I have some systems where there are multiple sites or modules they can be using with a single user source), and the timestamp to use for when the session should end.

You can also create a related table that takes the ID of the log in table and add a fields for a timestamp and page or route. You end up with a log of every page used. You can also add a field to record the action taken on that page such as a save, create, delete etc.  This comes in handy for something like tracking who last edited an invoice.

That is all extra. The key that you are looking for is to track user logins using the DB and yes, the way that would work is through an ajax call. When doing an ajax call, I believe that resets any session cookie timeouts. This is where having a field for the date/time you want the user logged out to be.  I did something like this a while back in classic asp and after a few people asked about it I posted an article https://www.experts-exchange.com/articles/18259/Classic-ASP-Login-System-Utilizing-a-Token.html. Different language but the idea is the same.


It could be something as simple as this

(function() { 
  // LOAD YOUR CHECKPOINTS HERE
  const times = [
      6 * 60 * 1000, 
      10 * 60 * 1000, 
      19 * 60 * 1000, 
      20 * 60 * 1000
   ];

  // TOTAL TIME IS THE LAST ITEM IN THE CHECKPOINT ARRAY
  // WE POP IT - EASIER TO KEEP ALL TIMES IN ONE PLACE
  const totalTime = times.pop()

  // MARK OUR POINT IN HISTORY
  const startTime = Date.now()

  // FIRST TIMEOUT IS AT THE FRONT OF THE ARRAY
  let currentTimeout = times.shift()
  
  // USE setInterval - REPEATING A setTimout HAS A CUMULATIVE
  // ERROR THAT WILL THROW YOUR TIMINGS OUT
  // setInterval is NOT MORE ACCURATE - JUST WITH THIS PARADIGM WE REGULARLY CHECK
  // AGAINST A FIXED REFERENCE POINT - IN THIS CASE startTime. THAT WAY WE DON'T
  // HAVE TO WORRY ABOUT ACCUMULATION ERRORS

  const timer = setInterval(() => {
    // HOW FAR ALONG ARE WE?
    const elapsed = Date.now() - startTime

    // NOTHING TO REPORT - BUG OUT
    if (elapsed < currentTimeout) return
    
    // OUT OF CHECKPOINTS?
    // TIME TO CLEAN UP AND GO HOME
    if (times.length == 0) {
      clearInterval(timer)
      // Redirect here
      window.location = 'login.html'
      return
    }

    // GET THE NEXT CHECKPOINT
    currentTimeout = times.shift()
    
    // WORK OUT WHAT TO TELL THE USER
    const secondsToGo = parseInt((totalTime - elapsed) / 1000 )

    // NB NB NB: I USE AN ALERT HERE - IN REALITY I WOULD POP A <div>
    alert('You will be logged out in ' + secondsToGo + 'seconds') 
  }, 1000)
})()
  


Open in new window

Julian,

Thanks a lot.

I am not clear where this function goes in the Javasript or the significance of the parentheses surrounding the entire function, i.e. (function () ( and the corresponding closing )

Thank you,

Richard
Hi Richard,

First up the parenthesis. In JavaScript this called an Immediately Invoked Function Expression (IFFE for short)
(function() {
  // isolated scope here
})()

Open in new window

Basically it is a chunk of code that executes as soon as it is defined.
Why do we use it here?
I use it to define scope. By declaring variables and functions inside an IIFE I don't have to worry about collisions with other variables on the page. You can have identical functions and variables outside the IIFE to those inside and they won't interfere with each other.

As to where this goes - you can put it in a <script> block anywhere on the page - in this instance it does not matter as it is relating to a time and not to elements on the page. It does potentially interact with the page in the instance where the popup is a page element but typically your first timeout is going to be long after the page is rendered - however if you are going to get references to page elements at the start of the function it would be best to put those in an onLoad.

Your time-out function is not going to work the way you think.  Somewhere it says that you should Never use PHP session time-out as a timer.  PHP does not run a timer to determine when to kill a session.  It only checks when someone runs "session_start()" on a page.  Even then it does not always 'expire' sessions.  "session.gc_probability" determines when 'gc' (garbage collection) occurs so that it doesn't run all the time and use up server time for no reason.  I have had session data stay on the server for many years simply because the code that put it there was never run again.
https://www.php.net/manual/en/session.configuration.php#ini.session.gc-probability
Julian, I see, it runs right away, like <body onLoad=...>

What's confusing me is how does it keep checking? Is a Javascript timer somehow invoked that I don't see?

From my naïve perspective, I need to start a Javascript timer on page load for say 10 minutes, then when that expires, close it & display the first message, start a 2nd timer, etc.

We think the most likely scenario is a user gets to the Summary page (the result of their running the app), then just lets it sit. Later, they decide, "oh, I need to submit a request for Quote", but by "later" the php session has timed out & all the $_SESSION variables which carried all the details of the configuration the user built are empty.

So this would be a way to warn them & after 20 minutes, if they are not paying attention, which they likely are not, the are logged out.

I think the way I would handle the waning's is to have a few separate little pages that I would reference with window,open so they would pop up over the app page, then I would time those out & close them a few seconds before the next warning. Except the last one which would say "You have been logged out due to inactivity", it would just stay.

We will probable include he code in a number of the major new steps on the apps pages so if they stop elsewhere, the same messages, etc., will occur.

Can you clarify how your code keeps running?

Thank you,

Richard
Is a JavaScript timer involved?
Yes. JavaScript has two timer functions
setTimeout()
This is a run-once function. You specify the callback and the timeout and after the elapsed time it calls that function - and then terminates

setInterval()
This is almost identical to setTimeout in that you call it the same way - the difference is it calls your function every N milliseconds where N is the value you specify as the second parameter.

Regarding run a timer for 10 minutes, display a message - close and start another timer - this is problematic for the following reasons.

The setTimeout / setInterval functions in JavaScript are not accurate - in otherwords if you say fire after 1000ms then the timer will fire back at the closest point to this timeout but it in all likelihood will not be the exact time. Due to the synchronous nature of Javascript the timing is never going to be bang on accurate.

Using a strategy of start timer, fires, start another - there is a cumulative error that builds up as each inaccurate firing results in a slight error that is then added to the previous execution. In your case this will probably be benign - but in general a better strategy is to
a) On page load get the current time
b) Run a setInterval timer that fires every Nms (1second) and checks to see if the difference between the current time and the start time exceeds the desired timeout - if it does display the message and then set the next timeout value

This is what the code above does. It marks the start time and then waits for the first timeout (which it gets from the array of times). One this time is reached it shows a message and then loads the next timeout value. This continues until it has exhausted all the values in the array.

The solution allows for an unlimited number of timeout checkpoints. It could be extended that you could load a different message at each checkpoint by having objects in the array with properties timeoutValue and message - and the code then keeps track of the current object rather than the time and uses that to display the message.

As for the type of popup - my personal preference is not to use window.open - it has a number of disadvantages - instead I would use a styled <div> to show the message.


As to your last question - the code I posted keeps running until it runs out of timer values at which point it fires a redirect to another page (login.html for instance) but this is just a suggestion - after final timeout it is relatively simple to display a popup with a "You have been logged out" message.
Julian,

I made a slightly modified version of your js, it's attached. I embedded it in the code for the page most likely to require it, I reduced the timeouts to make the testing easier (not long waiting). I'm using alerts for the time being, will do something else when we go live, as you suggested.

It gives an alert at 2 minutes & then 3 minutes, then goes to the login (index.php) page with a variable passed telling that page it has time out.

I don't understand why it's alerting me at 2 minutes & then 3, I had intended 2, 4 & then logout on 6.

What am I doing wrong?

Thank you,

Richard

julian_rev.js
ASKER CERTIFIED SOLUTION
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks, Julian.

I guess I'm still not getting it right.

See attached, this is what I understood you meant. I noted the time on my PC when each event occurred, recognizing it's only correct to the minute.

Loaded page - 11:45
Message for 2 minutes - 11:46
Message for 3 minutes ?? - 11:47
Message for 5 minutes ?? - 11:49
Goes to login page - 11:51

The overall effect is OK, but I can't see why I'm not getting an alert at 2 minutes, 4 minutes & logout at 6 minutes.

I can't understand why there is a message at 3 & 5 minutes.

I think I can see how to just set a timer 3 times to 2 minutes and on the last one (3rd), window.location to logon page.

Thank you,

Richard

julian_rev1.js
Hi Richard,
Timings with minutes are tricky - a 1 minute gap on the minutes could be 1.59 in actual time.

I put together this sample https://www.marcorpsa.com/ee/t4007.html

To demonstrate the concept. The sample allows you to enter your time values (one per line) in the <textarea>. Values are entered in seconds.

When you click start a timer starts and the bottom time value shows the next timecheck.

What you should see is that when the timer gets to the timecheck value the popup shows.

I have also included a simple popup to demonstrate how the timer continues in the background while the popup is showing. If the popup is left open - the next timeout will update the message - it won't open another popup.