3

As stated in the title, db table trace_users is not getting updated when no activity happens for 30 mins but its get updated when its 6 mins.

The following code (both JS and PHP) is inside the same php file mno.php.

I am storing the last activity of user through this line let lastActivity = <?php echo time(); ?> ; present in the script below.

<?php
session_start();     
if (isset($_GET['action']) && $_GET['action'] == "logout")
{
    session_destroy(); // destroy session data in storage
    !isset($_SESSION['admin']);
    $open = "false";
    $write = "0";
    $stmt = $connect->prepare("UPDATE trace_users SET open=?, write=? WHERE user_name=?");
    $usname = !empty($_SESSION['user_name']) ? $_SESSION['user_name'] : '';
    $stmt->bind_param('sss', $open, $write, $usname);
    $stmt->execute();
}
?>
<script>
    jQuery(document).ready(function($) {

        let lastActivity = <?php echo time(); ?> ;  // storing last activity of user

        let now = <?php echo time(); ?> ;

        let logoutAfter = 360;

        let timer = setInterval(function() {
            now++;
            let delta = now - lastActivity;
            console.log(delta);
            if (delta > logoutAfter) {
                clearInterval(timer);
                //DO AJAX REQUEST TO close.php
                $.ajax({
                    url: "/abc/mno.php",
                    type: 'GET',
                    data: {
                        action: 'logout'
                    }, // Line A
                    success: function(data) {
                        console.log(data); // Line B
                    },
                    error: function(jqXHR, textStatus, errorThrown) {
                        alert(textStatus);
                    }
                });
            }
        }, 1000);
    }); 
</script>

Problem Statement:

I am wondering what changes I should make in the code above so that when no activity happens for 30 mins (1800 seconds) then the db table should also get updated.

Case1 (Not Working) : 1800 seconds (page logs out, db doesn't update)
Case2 (Working) : 360 seconds (page logs out, db gets updated)

The values inside session.gc_maxlifetime are 1440(Local Value) 1440(Master Value)

This is what I have tried/debugged:

On the network tab, I am getting Request Method GET when session timeout is set 6 mins and 60 mins.

user1950349
  • 4,738
  • 19
  • 67
  • 119
  • Session by default will be destroyed after 20 minutes automatically, it works for 6 min since there is a session it doesn’t for 30. – estinamir Apr 28 '20 at 14:50
  • @bestinamir My above code works perfectly fine. When I have setup the session timeout for 30 mins (1800 seconds), the only thing which it doesn't do that it doesn't update the table, it do logs out. – user1950349 Apr 28 '20 at 15:04
  • I changed the value from 30 mins to 6 mins and saw its working for 6 mins. When I am setting 6 mins, I am getting a GET (200) request on network tab. That is what I have tried – user1950349 Apr 29 '20 at 16:33
  • When I am setting 60 mins, I am getting same GET (200) request on the network tab. – user1950349 Apr 29 '20 at 22:33
  • 1
    While editing your question is encouraged, please try to "chunk" or "batch" your edits to reduce the amount of noise. You have [recently made a very large number of edits in a very short period of time](https://stackoverflow.com/posts/61482965/revisions). – Cody Gray - on strike May 03 '20 at 00:13
  • You shouldn't rely on the client to check any activity : closing the browser would stop the ajax from being sent without triggering any logout. I suggest to find ideas in this answer https://stackoverflow.com/questions/520237/how-do-i-expire-a-php-session-after-30-minutes – Tuckbros May 08 '20 at 16:16

4 Answers4

2

You need to pass to the javascript some sort of permanent UID that identifies the user even after the session expires.

For the sake of simplification, I'm using user_name that already exists in your code. But you can also assign an UUID for each user, so that one can't guess another user's name and can't modify their stats.


First, you'll pass the $_SESSION['user_name'] from PHP to the JS closure.

let userName = "<?php echo $_SESSION['user_name']; ?>";  // don't forget to wrap the JS string value in quotes or apostrophes

Then, you'll pass it in the AJAX request payload.

data: {
    action: 'logout',
    user_name: userName  // added param
},

Finally, you'll overwrite the value that is sent to DB (if it's sent with the payload)

$usname = !empty($_SESSION['user_name']) ? $_SESSION['user_name'] : '';  // original line
if (isset($_GET['user_name']) && !empty($_GET['user_name'])) {
    $usname = $_GET['user_name'];
}
$stmt->bind_param('sss', $open, $write, $usname);  // original line

Complete updated code:

<?php
if (isset($_GET['action']) && $_GET['action'] == "logout")
{
    session_destroy(); // destroy session data in storage
    !isset($_SESSION['pageadmin']);
    $open = "false";
    $write = "0";
    $stmt = $connect->prepare("UPDATE trace_users SET open=?, write=? WHERE user_name=?");
    $usname = !empty($_SESSION['user_name']) ? $_SESSION['user_name'] : '';
    if (isset($_GET['user_name']) && !empty($_GET['user_name'])) {
        $usname = $_GET['user_name'];
    }
    $stmt->bind_param('sss', $open, $write, $usname);
    $stmt->execute();
}
?>
<script>
    jQuery(document).ready(function($) {

        let lastActivity = <?php echo time(); ?> ;  // storing last activity of user

        let now = <?php echo time(); ?> ;

        let logoutAfter = 10;

        let userName = "<?php echo $_SESSION['user_name']; ?>";

        let timer = setInterval(function() {
            now++;
            let delta = now - lastActivity;
            console.log(delta);
            if (delta > logoutAfter) {
                clearInterval(timer);
                //DO AJAX REQUEST TO close.php
                $.ajax({
                    url: "/abc/mno.php",
                    type: 'GET',
                    data: {
                        action: 'logout',
                        user_name: userName
                    }, // Line A
                    success: function(data) {
                        console.log(data); // Line B
                    },
                    error: function(jqXHR, textStatus, errorThrown) {
                        alert(textStatus);
                    }
                });
            }
        }, 1000);
    }); 
</script>

Few notes at the end:

  • It's bad practice to combine PHP, HTML and JS altogether in one file.
  • There are some parts of your code that are not easily reproducible and I had to guess from the context (e.g. i guessed that the session is already set, jQuery is used but there's no <script src="...jquery.js">, there's a DB query but no separate SQL import to try it quickly). Please read and apply the knowledge from How to create a Minimal, Reproducible Example in your next question, so that more people are able or willing to help you with it.
Petr Hejda
  • 40,554
  • 8
  • 72
  • 100
  • When you say session is already set, you are trying to say whether I have used `session_start()` at the top of the code ? – user1950349 May 03 '20 at 00:49
  • @user1950349 Not only that. Also when you assign a value to the sesion. So anything like `$_SESSION['user_name'] = "xxx";` to actually show in which part of the code you set the session value. – Petr Hejda May 03 '20 at 00:52
  • I am do storing the last activity `($_SESSION['LAST_ACTIVITY'])` of the session but I am not using `$_SESSION['user_name']` anywhere. Although, I am using `$_POST['user_name']`. – user1950349 May 03 '20 at 00:59
  • I just tested your code and it doesn't seem to work. I am wondering if you have had a chance to go through problem statement. When I am am forcing logout after 6 mins then db table is getting updated but when I am forcing after 30 mins then db table is not getting updated. – user1950349 May 03 '20 at 01:59
  • @user1950349 ad "I am not using `$_SESSION['user_name']`": I can see this snippet in your original code `$usname = !empty($_SESSION['user_name']) ? $_SESSION['user_name'] : '';` ... Also, when I tested the code with my modifications, it was working in my environment after session expiry... So now I'm a bit confused. Please reconsider clarifying your question so it's better reproducible, as per the linked article. – Petr Hejda May 03 '20 at 07:10
  • My apologies for the confusion. Yes, I am using $_SESSION['user_name'] but I am not storing it in any variable. Is your db table trace_users getting update when logoutAfter is equivalent to 3600 or 1800 ? – user1950349 May 03 '20 at 07:39
  • i am wondering if the db table `trace_users` is getting updated when you are setting `logoutAfter` equivalent to 3600 ? – user1950349 May 04 '20 at 02:27
  • Are you around ? – user1950349 May 05 '20 at 02:51
1

The problem is that you are not changing anything that will prevent the session from expiring after N seconds, you are just coding your script in a way that it will destroy the session after this time. The session gc (garbage colector) executes periodically and deletes old sessions and when that happens, $_SESSION['LAST_ACTIVITY'] will be deleted as well.

You must either try to prevent the gc from deleting sessions or change the logic in your application.

Enrico Dias
  • 1,417
  • 9
  • 21
  • I am wondering if you are able to get my question. Also, what would be the best approach to solve this question ? I don't think so I have to prevent the session from expiring after N seconds. If there is no acitivity for N seconds then the page should log out and db table should also get updated (which is not happening). – user1950349 Apr 28 '20 at 15:30
  • @user1950349 I did get the question. You could increase the value of session.gc_maxlifetime to a few hours and this specific code will work if the user remains online and running that js code. The best approach would be to change the logic in your application. You will never know for sure when the user left the page, you can only know his last request. – Enrico Dias Apr 28 '20 at 15:36
  • 1
    Yes, I think the best approach would be to change the logic in my application. I don't think so it would be correct to change the value of `session.gc_maxlifetime`. You are right, I would never know when the user left the page. I am approaching this question on the basis of last request. – user1950349 Apr 28 '20 at 15:49
  • I am wondering what changes I need to make in my logic ? My current logic works perfectly fine when time is set for 360 seconds **(page logs out and db gets updated)** but when it is 1800 it doesn't work properly **(page do logout but db update doesn't happen)**. – user1950349 Apr 28 '20 at 16:13
  • 1
    Assume that every request will be the last and save the data that you need. For example, updates the user's last activity time in a table and consider the user as logged in the this activity happened in the last X minutes. – Enrico Dias Apr 28 '20 at 16:17
  • I have made changes in my table **trace_users**. I have added one column **LastActivity**. That column is storing the user's last activity in a table. I am wondering if you can let me know what changes I need to do now. – user1950349 Apr 29 '20 at 00:31
  • That's just it. When consulting this table to see online users, instead of relying only on the "open" and "write" field you can select only the users that the LastActivity happened in the last X minutes. You can also make a cronjob to update the "open" and "write" fields based on the last activity if those fields are important for your system. – Enrico Dias Apr 29 '20 at 00:53
1

PHP process does not sit indefinitely and does not have program structure as a loop ala Node.js server, which won’t allow you to react to session expiry since it’s not a process that invalidates it, but rather a simple timestamp associated with session that is checked every time you attempt to work with it.

The solution I offer is a simple script that is ran every N minutes to perform a comparison of last user activity timestamp (which, I assume is updated on the request for that user) against expiry period (in you case it is 30 minutes). You can also set session expiry to 30 minutes, though strictly it’s not necessary. If the time difference will exceed 30 minutes, you update the timestamp for the user in your table, and invalidate their session if necessary. The script can be ran through cron or its alternatives and go through all users you require to perform a check.

Do not forget to handle the case when user is logged out on the server but client does not know about it and may continue sending logout requests - raising alert box is rather unclear (it is better to return HTTP Unauthorized code and handle it differently - redirecting to login screen, for example)

Daniel Protopopov
  • 6,778
  • 3
  • 23
  • 39
0

There are two things you need to fix.

1). Set the ajax request to be async:false.

$.ajax({
    url: "/abc/mno.php",
      type: 'GET',
      async: false,   //  <---- ADDED ASYNC FALSE
      data: {
        action: 'logout'
    }, // Line A
     success: function(data) {
        console.log(data); // Line B
      },
     error: function(jqXHR, textStatus, errorThrown) {
         alert(textStatus);
       }
 });

2). Destroy the session after performing the SQL Query.

<?php
session_start();     
if (isset($_GET['action']) && $_GET['action'] == "logout")
{
    !isset($_SESSION['pageadmin']);
    $open = "false";
    $write = "0";
    $stmt = $connect->prepare("UPDATE trace_users SET open=?, write=? WHERE user_name=?");
    $usname = !empty($_SESSION['user_name']) ? $_SESSION['user_name'] : '';
    $stmt->bind_param('sss', $open, $write, $usname);
    $stmt->execute();
    session_destroy(); // destroy session data in storage <---- DESTORY AT THE END
}
?>
Hackinet
  • 3,252
  • 1
  • 10
  • 22