Avatar of Richard Korts
Richard Korts
Flag for United States of America asked on

Change Menu item on Timeout

A customer wants me to have the menu item "Logout" changed to "Login" on a php session timeout. Of course trying to "fix" that in php sends a server roundtrip & the session timeout is reset. We are using the default of 20 min.

So I thought about starting a Javascript timeout event to time out in 20 min after page has loaded. When it times out, use an Ajax query to reload the page including a session destroy with "Login" present.

Is there a simpler way to do this? I would have to do this on about 8 pages (the things shown to the user in the app).
PHPJavaScriptAJAX

Avatar of undefined
Last Comment
Cyril Joudieh

8/22/2022 - Mon
Scott Fell

What I almost always do is create a table to track logins that includes at the very least a unique id, timestamp, ip and user-id. You can either just keep the timestamp representing the log in or add a field for end time.  And if you end up using the database to track logins, add another field for a token where you just hash the login id, ,username, timestamp and store that hash as a token in the db.  

Then store the token as a cookie and every page view, grab the cookie, match the token up with your login table and check the end time. If it is past the time, you can log them out or send them a warning that they will be logged out and allow them to extend the time.  

This will allow you to store an accurate time on their logged in pages when their session will end.

I use this type of thing myself on private projects. 


Cyril Joudieh

If I were building the website or webapp I would have had the menu generated from the same function and all pages would be calling it. I would also use a javascript timer for 20 minutes or better yet I would query the server to get the timeout value and put it in the javascript code when generating the page.
Julian Hansen

In your case, I would use a combination of Scotts recommendation and an AJAX poll.

However, my preferred method would be as follows.
1. Create a session table that is used to store active sessions
2. When a user logs in create / update a record in this table with a session token (using whatever hash you prefer) and the timeout - 
INSERT INTO tbl_session (user_id, token, expires)
VALUES :user_id, :token, DATEADD(Now(), INTERVAL 20 MINUTE)
ON DUPLICATE KEY UPDATE expires=DATEADD(Now(), INTERVAL 20 MINUTE)

Open in new window

This will either create or update a record in the session table keeping the table tight without garbage that needs to be collected.
3. When a user touches the server you check the timeout value
SELECT * FROM tbl_session WHERE token = :token AND expires > NOW()

Open in new window

If you want to update the session when the server is touched you can do this
UPDATE tbl_session SET expires = DATEADD(Now(), INTERVAL 20 MINUTE)
WHERE token = :token AND expires > Now()

Open in new window

You can do a check and set with the above query. After running the query check the rows affected (this will eliminate the need for the select)
$sessionActive = $stmt->rowsCount() > 0;

Open in new window

That is what you use for normal page round trips.

In your client you have code that checks the server every minute for a session expire (this is similar to the way WordPress does it)
(function() {
  const login = document.getElementById('login_button_id');

  setInterval(checkSession, 60 * 1000);

/**
  * Assumes a COOKIE based session token
  * For a Header based token we would need
  * to fetch the token from sessionStorage
  * and add it to the header
  */
  function checkSession() {
    fetch('session_check.php')
      .then(resp => resp.json())
      .then(resp => {
        if (!resp.status) {

          // Assuming we do it your client's way
          login.innerHTML = "Logout"; 

          // usually we would do a redirect or a popup and then a redirect
        }
      }
   }
})();

Open in new window

On the server we would then do the following.
Note this function includes two methods for checking the session - so use the getSession() function that suits your paradigm
[session_check.php]
<?php
/**
  * Common.php includes 
  * - config
  * - database connection - for this example assumes PDO
  * - creates session
  */

require_once('common.php');

/**
  * Assumes the presence of a Session class - available on request 
  */

$status = !!getSession($db);
sendJSON(['status' => $status]);

/**
  * Simple function to send JSON response.
  */
function sendJSON($data) {
  header('Content-type: application/json');
  echo json_encode($data);
}

/** 
  * If using the PHP session
  */
function getSession() {
  return $_SESSION[SESSION_TOKEN] ?? false;
}

/**
  * If using DB method
  */
function getSession($db) {
  $query = <<< QUERY
SELECT * 
FROM 
   `tbl_session` 
WHERE 
   `token` = :token AND 
   `expires` > NOW()
QUERY;
   $session = false;
 
  // SESSION_TOKEN Defined in config
   $token = $_COOKIE[SESSION_TOKEN]
   
   if ($token) {
     $stmt = $db->prepare($query);
     $result = $stmt->execute(['token' => $token];
     if ($result) {
       $session = $stmt->fetch(PDO::FETCH_OBJ);
     }
     return $session;
  }
}

Open in new window


If you your project is in a state where implementing the DB session method is not feasible - then you can still use the JavaScript and the session_check.php files. For the latter - use the first getSession() function to get your PHP session data using $_SESSION. The rest remains the same.
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
Richard Korts

ASKER
Cyril Joudieh 

The menu (header) is a separate php file included on the top of the visible pages.
ASKER CERTIFIED SOLUTION
David Favor

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
Scott Fell

The most complete answer here is from Julian's comment, https://www.experts-exchange.com/questions/29217324/Change-Menu-item-on-Timeout.html#a43296728 .

The comment you selected as the solution only reiterates what was already suggested. The difference is Cyril gave a high-level overview and Julian gave very good details on how to work with both setting the time out on the server and address it to the client.

The selected answer suggests, "On the server side, run a timed function." I am not entirely sure what that means. If it is physically creating a timer on the server, that eats up your servers processer and potentially blocks other services from running.

"study how WordPress handles this situation for a more robust,..."  Wordpress just sets a cookie for 2 days and checks if it is still valid. This does not give you the exact time you are going to time out. https://github.com/WordPress/WordPress/blob/1e02e0f0493a1d6f013dcf5142efac489524941c/wp-includes/user.php#L3141

If you are going to accept David's comment as the solution, you really should select Cyril's as well which in short was always check the server for being logged in or out, send the time remaining to the browser so you can do some type of countdown function in javascript. 


Richard Korts

ASKER
I am sorry that I cannot totally satisfy every one of you. I always recognize that if I don't provide full points to every respondent, the ones not getting full credit feel slighted.

I am perfectly fine if the 4 of you want to do as you suggest. I do not know how to undo what I already did to "fix" it.

In general, I do not believe in using the current trend in our culture that everybody gets an A+ on everything all the time.
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
Scott Fell

No, it is not about being slighted or giving everybody an award. The point of my comment is that the selected solution only said the same thing as the first comment by Cyril.

You need to select the solution that worked for you, and that may not be the 'best' in the eyes of the other participating Experts. However, for anybody that comes across the thread, it is also good to have noted what others feel is the best answer which is why I selected Julian's comment.

Cyril's and David's answers may seem easier to work with, but in my own opinion, it is not very accurate to rely on a client-side timer.  I also prefer to have more granular control. But that is not for everybody.


SOLUTION
Cyril Joudieh

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.