Search code examples
javascriptphphtmlajaxreload

Reload page for all connected user after one's clicking a button


@LSerni I've edited the code using a db too but i'm still blocked :(

It execute the clicked.php but the check status seems not working.

I have 2 fields in a table, if the second one is higher then the first the page has to reload, but it isn't like this so far, the clicked.php file work fine. check-status.php:

<?php
$reply = [ 'status' => 'success', 'pressed' => false ];

for ($sec = 0; $sec < 60; $sec++) {
     // ... check whether anyone pressed the button ...
   $mysqli = new mysqli("localhost", "risicov19", "Edukapol19", "my_risicov19");
    if ($mysqli->connect_errno) {
    echo "Failed to connect to MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
    }
    $sql = "SELECT pre FROM data";
    $rs = mysqli_query($mysqli, $sql);
    while($row = mysqli_fetch_assoc($rs)) {
    $val1 = $row['pre'];
     }
    $sql = "SELECT data FROM data";
    $rs = mysqli_query($mysqli, $sql);
    while($row = mysqli_fetch_assoc($rs)) {
    $val2 = $row['data'];
     }
    if($val2 > $val1){
    $sql = "UPDATE data SET pre = '" .$val2. "'";
    if ($mysqli->query($sql) === TRUE) {

    }
     $reply['pressed'] = true;
     break;
    }
   sleep(1);
}
die(json_encode($reply);

and clicked.php:

<?php
$mysqli = new mysqli("localhost", "risicov19", "Edukapol19", "my_risicov19");
if ($mysqli->connect_errno) {
    echo "Failed to connect to MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
$sql = "SELECT data FROM data";
$rs = mysqli_query($mysqli, $sql);
while($row = mysqli_fetch_assoc($rs)) {
    $val = $row['data'];
}
$agg = $val + 1;
$sql = "UPDATE data SET data = '" .$agg. "'";
    if ($mysqli->query($sql) === TRUE) {

    }
die(json_encode([ 'status' => 'success' ]));

Solution

  • You need for the other users to know when one of them clicked the button.

    As @balzacLeGeek observed, this is a job for Web sockets. But you can do it in AJAX too, using a suspended AJAX call more or less like this:

    var refresher = function() {
        // CLIENT timestamp - beware of server time zones!
        const since = Date.now() / 1000;
        $.post('check-status.php', { since: since }).then(reply => {
            if (reply.pressed) {
                // do_reload();
            } else {
                // display some small animation to let user know we checked
            }
            // Schedule another round.
            window.setTimeout(refresher, 10);
        });
    };
    

    In the page, when it is loaded, just call refresher() and it will start to, well, check for refresh.

    The check-status function gets called continuously, which will cause an unacceptable load on the system. But since it waits for the server to return, we can implement it like this:

    <?php
        $reply = [ 'status' => 'success', 'pressed' => false ];
    
        for ($sec = 0; $sec < 60; $sec++) {
           // ... check whether anyone pressed the button ...
           if (file_exists('pressed.json')) {
               if (filemtime('pressed.json') > $_POST['since']) {
                   // return immediately.
                   $reply['data']    = json_decode(file_get_contents('pressed.json'), true);
                   $reply['pressed'] = true;
                   break;
               }
           }
           sleep(1);
        }
        header("Content-Type: application/json;charset=UTF-8");
        die(json_encode($reply);
    

    The call is suspended for up to 60 seconds or until someone presses a button; in that case, within one second, all the suspended calls return, and all the users' screen reload; and they immediately issue another call that will wait for the next press, and so on.

    You might want to play with the suspension time, or implement a setInterval watchdog that ensures that the refresher calls are properly issued (if one call fails, the whole cycle stops for that client; the client is "dead". So upon refresh you set a global timestamp variable, and the setInterval watchdog function verifies that the timestamp is never older than the suspension time plus a reasonable margin, say 2 seconds. If it is, the watchdog merely calls refresher() to restart the cycle, and maybe displays a brief animation "Your Internet connection had a problem").

    The pressed function simply creates a text file in response to the button press. Or you can use a database, or a shared memory area, or Redis or whatever:

    $('#button').on('click', ()=> {
        $.post('/clicked.php',
          {
              user: 'lserni', // or read from a global application object
              message: $('#message').val()
          }
    });
    

    The script gets called, and for the file case, you have a race condition (what if two people both press the button at the same time?):

    <?php
        $written = file_put_contents(
            'pressed.json',
            json_encode([
                'message' => $_POST['message'],
                'user'    => $_POST['user'],
                'date'    => date('d/m/Y H:i:s'),
            ]),
            LOCK_EX
        );
    
        header('Content-Type: application/json; charset=UTF-8');
    
        if (!$written) {
            die(json_encode([ 'status' => 'failure' ]));
        }
    
        die(json_encode([ 'status' => 'success' ]));
    

    In the case of the file, it was not enough to check for the file's existence, because after the first click the file always exists, and if ten clients check, the first cannot simply delete the file (the other nine would find nothing and assume nobody pressed the button).

    So, each client records the last time it has run a button check, and the server replies whether a press file was created later than the requested time. This too is not ideal (the last time is not saved but read off the clock, so there is a small time window when the press will not be detected; and the client time might be different from the server time), but it should be enough to start.

    Simplified version (just tested)

    This uses three files poll.php, poll.html and click.php. Each client records the random identifier assigned to the last clicker, and instructs the poller to ignore that identifier. This way, the poller will wait until a different identifier comes by, which means that somebody clicked.

    All clients are on the same page (browsers might complain if the same page opens more than X connections, and the script won't work. I used 3, here).

    poll.html:

    <html>
    <head>
            <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <style>
            div {
                    display: inline-block;
                    width: 150px;
                    height: 3em;
                    border: 1px solid red;
                    padding: 2px;
            }
    </style>
    </head>
    <body>
            <div><button></button><br /><input type="text" size="12" /></div>
    </body>
    <script>
            function freshener(clientName, x) {
                    $.post('poll.php',
                            {
                                    client: clientName,
                                    ignore: x || 0
                            }
                    )
                    .then(reply => {
                            // Identify our client
                            let client = $('#'+clientName).closest('div');
                            client.find('input').val(reply.text);
                            window.setTimeout(function() { freshener(clientName, reply.ignore); }, 1);
                    });
            }
    
            $(_=>{
                    let btn = $('div').clone();
                    $('div').remove();
                    for (var i = 1; i < 4; i++) {
                            $('body').append(btn.clone().find('button').attr({ id: "x"+i }).text("X"+i).end());
                            // Create a poller for object x(i)
                            freshener("x"+i);
                    }
                    $('body').on('click', 'div', function(event) {
                            let clientName = $(this).find('button').attr('id');
                            $.post('click.php', { client: clientName });
                    });
            });
    </script>
    </html>
    

    poll.php:

    <?php
        $reply = [ 'status' => 'success', 'pressed' => false, 'ignore' => $_POST['ignore'], 'text' => 'WAIT ' . date('H:i:s') ];
    
        $wait  = rand(2, 5);
    
        for ($sec = 0; $sec < $wait; $sec++) {
            if (file_exists('pressed.json')) {
                $data = json_decode(file_get_contents('pressed.json'), true);
                if ($data['ignore'] != $_POST['ignore']) {
                   // return immediately.
                   $reply['text']    = $data['text'];
                   $reply['pressed'] = true;
                   $reply['ignore'] = $data['ignore'];
                   break;
               }
           }
           sleep(1);
        }
        header("Content-Type: application/json;charset=UTF-8");
        die(json_encode($reply));
    

    and finally a very simple click.php:

    error_reporting(E_ALL);
    ini_set("display_errors", true);
    // Someone clicked!
    $time = date("H:i:s");
    file_put_contents(
        'pressed.json',
        json_encode([
            'text' => "client {$_POST['client']}",
            'ignore' => floor(rand())
        ]),
        LOCK_EX
    );
    

    You can call manually the click.php script to see whether there are errors in the file creation.