Jump to content

implementing php long polling


Recommended Posts

I want to use php-long polling in my php script to get the instant display of data when admin changes anything from backend.

I have browsed for solutions, But could not find any.

I have downloaded php-long-polling- master from github. There , i have a file server.php.

There the code is like this

 // PHP caches file data, like requesting the size of a file, by default. clearstatcache() clears that cache
    clearstatcache();
    // get timestamp of when file has been changed the last time
    $last_change_in_data_file = filemtime($data_source_file);

    // if no timestamp delivered via ajax or data.txt has been changed SINCE last ajax timestamp
    if ($last_ajax_call == null || $last_change_in_data_file > $last_ajax_call) {

        // get content of data.txt
        $data = file_get_contents($data_source_file);

        // put data.txt's content and timestamp of last data.txt change into array
        $result = array(
            'data_from_file' => $data,
            'timestamp' => $last_change_in_data_file
        );

        // encode to JSON, render the result (for AJAX)
        $json = json_encode($result);
        echo $json;

        // leave this loop step
        break;

    } else {
        // wait for 1 sec (not very sexy as this blocks the PHP/Apache process, but that's how it goes)
        sleep( 1 );
        continue;
    }
}

If anything we change in data.txt, it displays as and when in real time. But i am not getting how and where i can add the query or how do i include it in my file.

in server.php, it is there like

// where does the data come from ? In real world this would be a SQL query or something
    $data_source_file = 'data.txt';

but where and how can i add sql query?

can somebody help me how can i start with or how can i add sql query

Link to post
Share on other sites

After many attempts i could do it like this

in server.php, i did like this

 <?php

/**
 * Server-side file.
 * This file is an infinitive loop. Seriously.
 * It gets the file data.txt's last-changed timestamp, checks if this is larger than the timestamp of the
 * AJAX-submitted timestamp (time of last ajax request), and if so, it sends back a JSON with the data from
 * data.txt (and a timestamp). If not, it waits for one seconds and then start the next while step.
 *
 * Note: This returns a JSON, containing the content of data.txt and the timestamp of the last data.txt change.
 * This timestamp is used by the client's JavaScript for the next request, so THIS server-side script here only
 * serves new content after the last file change. Sounds weird, but try it out, you'll get into it really fast!
 */

// set php runtime to unlimited
set_time_limit(0);

// where does the data come from ? In real world this would be a SQL query or something
$data_source_file = 'data.txt';

$servername = "localhost";
$username = "*****";
$password = "******";
$dbname = "mydb";

// Create connection
        $conn = mysqli_connect($servername, $username, $password , $dbname);
        // Check connection
        if (!$conn) {
            die("Connection failed: " . mysqli_connect_error());
        }
 

// main loop
while (true) {

    // if ajax request has send a timestamp, then $last_ajax_call = timestamp, else $last_ajax_call = null
    $last_ajax_call = isset($_GET['timestamp']) ? (int)$_GET['timestamp'] : null;

    // PHP caches file data, like requesting the size of a file, by default. clearstatcache() clears that cache
   // clearstatcache();
    // get timestamp of when file has been changed the last time
   // $last_change_in_data_file = filemtime($data_source_file);

         // Create database
        $sql = "SELECT max(tax_id) FROM taxes";
        $result = mysqli_query($conn, $sql);

        $last_change_in_data_file = mysqli_fetch_array($result, MYSQLI_NUM)[0]; 
    // if no timestamp delivered via ajax or data.txt has been changed SINCE last ajax timestamp
    if ($last_ajax_call == null || $last_change_in_data_file > $last_ajax_call) {

        // get content of data.txt
//        $data = file_get_contents($data_source_file);
        

        // Create database
        $sql = "SELECT tax_id, name FROM taxes";
        $result = mysqli_query($conn, $sql);

        if (mysqli_num_rows($result) > 0) {
            // output data of each row
            while($row = mysqli_fetch_assoc($result)) {
                $data .=  "id: " . $row["tax_id"]. " - Name: " . $row["name"]. "<br>";
                $last_change_in_data_file = $row["id"];
            }
        }
       // mysqli_close($conn);

        // put data.txt's content and timestamp of last data.txt change into array
        $result = array(
            'data_from_file' => $data,
            'timestamp' => $last_change_in_data_file
        );
        mysqli_close($conn);
        // encode to JSON, render the result (for AJAX)
        $json = json_encode($result);
        echo $json;

        // leave this loop step
        break;

    } else {
        // wait for 1 sec (not very sexy as this blocks the PHP/Apache process, but that's how it goes)
        sleep( 1 );
        continue;
    }
} 

But i want to get the data according to particular id. my sql statement has to be something like this

 $sql = "SELECT tax_id, name FROM taxes WHERE grp_id=".$id."";  

So i am not getting from where i should pass the id to this page

Can somebody help me in this??

Link to post
Share on other sites

Not 100% sure what you mean, though I will try.

 

# I've NOT tested, just rough...

 

 

Before this:

        }
       // mysqli_close($conn);

Something like:

        // Create database
        $sql = "SELECT tax_id, name FROM taxes WHERE grp_id=".$id.""; 
        $result = mysqli_query($conn, $sql);

        if (mysqli_num_rows($result) > 0) {
            // output data of each row
            while($row = mysqli_fetch_assoc($result)) {
                $data .=  "id: " . $row["tax_id"]. " - Name: " . $row["name"]. "<br>";
                $last_change_in_data_file = $row["id"];
            }
        }

This what you mean?

Link to post
Share on other sites

basically i want to pass a variable to this page along with timestamp. Timestamp is getting passed like this in client.js

 

timestamp is getting passed like this
 

function getContent(timestamp)
{
    var queryString = {'timestamp' : timestamp};

    $.ajax(
        {
            type: 'GET',
            url: 'http://localhost/php-long-polling/server/server.php',
            data: queryString,
            success: function(data){
                // put result data into "obj"
                var obj = jQuery.parseJSON(data);
                // put the data_from_file into #response
                $('#response').html(obj.data_from_file);
                // call the function again, this time with the timestamp we just got from server.php
                getContent(obj.timestamp);
            }
        }
    );
}

// initialize jQuery
$(function() {
    getContent();
});

so here i am not getting how to do it

Link to post
Share on other sites

the original logic actually has a race condition in it, that will prevent it from showing all the new data if multiple pieces of data are added in a single second and at the same time that the code checks for new data. any data added before the code checks for new data would be displayed. any data added in the same second and after the code checks for new data won't be seen, since the code is using the timestamp to detect if there is new data. you would only see the 'orphaned' new data when the timestamp changes due to later data being added.

 

if you were using this logic for a 'chat' script or forum software 'new replies', and two or more replies are posted in the same second and happen to coincide with the code checking for new replies, some of the concurrent replies might not get displayed and the chat would stall with one person waiting for a reply from the other person before posting their own reply. the vBulletin forum software had/has a bug like this, where it wouldn't show all new replies until a later reply was posted that had a different timestamp than what it thought it had shown all the data for.

 

the correct logic would be to use the highest id of the last data that was displayed.

 

any server/client code referencing a 'timestamp' variable/element needs to be changed to something like 'last_id'. a single sql query should be used to find data that has an id greater then the received last_id value. if any data is found, retrieve it and output it to the client. the new last_id value that you return to the client would be the highest id in the data that was just retrieved.

 

the current client side code is completely replacing the displayed content when it receives data. this should be changed so that it appends the new data to the existing content. this will avoid redrawing the existing content and causing it to flicker.

Edited by mac_gyver
Link to post
Share on other sites

@

mac_gyver

 

My exact scenario is:

 

There is a group A formed by a user User1 and in that group there will be minimum 4 members. When minimum 4 members are online, group owner (User1) will updates some queries on db and he will hit a button 'OK'. When he hits the button , a filed in database sets 1. When this sets to 1, all the online users of this group gets the notification about this. And for them approve button will be activated. Once they approve its immediately visible to admin that who all approved.

 

I am doing it with php-long-polling. But here i am not getting how exactly i can use this to my requirement. I just need some hints and direction. Can you please guide me how can i go with this along with php long polling. I am not comfortable in java-scripts so i am using this.

Link to post
Share on other sites
  • 3 weeks later...

I got the solutions for that. But in my server.php i am displaying a form which will be having few input fields. This should display in #response area. Now i am able to display the same on instant basis. As it loads on seconds basis, i am not able to enter anything on any input boxes. Here is my display page.

<?php
ob_start();
include('includes/sessions.php');
include('includes/config.php'); 
include('includes/functions.php');
$grp_id = $_GET['id'];
?>
<!DOCTYPE html>
    <head>
       <!-- head section -->
        <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<script type="text/javascript">
function getContent(timestamp)
{
    var queryString = {'timestamp' : timestamp,  'id' : <?php echo $grp_id; ?>};

    $.ajax(
        {
            type: 'GET',
            url: 'http://localhost/folder/server.php',
            data: queryString,
            success: function(data){
                // put result data into "obj"
                var obj = JSON.parse(data);
                // put the data_from_file into #response

                $('#response').html(obj.data_from_file);
                // call the function again, this time with the timestamp we just got from server.php
                getContent(obj.timestamp);
            }
        }
    );
}

// initialize jQuery
$(function() {
    getContent();
});

</script>

    </head>

    <body<?php if ($body_classes) { echo ' class="' . $body_classes . '"'; } ?>>
<div id="page-content" class="block">
    <div class="row gutter30">
        <div class="col-xs-12">
        <div id="response"></div>

        <!--Contents in server.php will display according to the condition changes in database-->

</div>
</div>
</div> 

server.php

<?php
set_time_limit(0);
$data_source_file = 'data.txt';
include('includes/sessions.php');
include('includes/config.php'); 
include('includes/functions.php');

// main loop
while (true) {

    // if ajax request has send a timestamp, then $last_ajax_call = timestamp, else $last_ajax_call = null
    $last_ajax_call = isset($_GET['timestamp']) ? (int)$_GET['timestamp'] : null;

    // PHP caches file data, like requesting the size of a file, by default. clearstatcache() clears that cache
   // clearstatcache();
    // get timestamp of when file has been changed the last time
   // $last_change_in_data_file = filemtime($data_source_file);

         // Create database
       $sql = "SELECT max(gid) FROM base_grp WHERE gid=".$_GET['id']."";
        $result = mysqli_query($conn, $sql);

        $last_change_in_data_file = mysqli_fetch_array($result, MYSQLI_NUM)[0]; 
    // if no timestamp delivered via ajax or data.txt has been changed SINCE last ajax timestamp
    if ($last_ajax_call == null || $last_change_in_data_file > $last_ajax_call) {

      $sql = "SELECT is_set FROM base_grp WHERE gid =".$_GET['id']."";
        $result = mysqli_query($conn, $sql);

        if (mysqli_num_rows($result) > 0) {
            // output data of each row
            while($row = mysqli_fetch_assoc($result)) {
               if($row['is_set'] == 0)
               {
                $data .= '<form name="frm" method="post" action="show_data.php">
    <div class="row gutter30" style="margin-top:20px;">  
        <div class="form-group">
        <img src="img/img1.png" class="image" /> 
<input type="text" name="img1" class="form-control"  />
<input type="hidden" name="img1_id" value="3" />
</div>

<div class="form-group"> 
       <img src="img/img2.png" class="image" /> 
<input type="text" name="img2" class="form-control" />
<input type="hidden" name="img2_id" value="4" />
</div>

<div class="form-group"> 
       <img src="img/img3.png" class="image" /> 
<input type="text" name="img3" class="form-control" />
<input type="hidden" name="img3_id" value="2" />
</div>

<div class="form-group"> 
       <img src="img/img4.png" class="image" /> 
<input type="text" name="img4" class="form-control"  />
<input type="hidden" name="img4_1" value="1" />
</div>

<button type="submit" name="lock" class="btn btn-sm btn-success">SUBMIT</button>
</div>

</form> ';  
$last_change_in_data_file = $row["gid"];  
               }
              else  if($row['is_set'] = 1)
               {
        $data .=  "<div class='row gutter30' style='margin-top:20px;'>
        <div class='col-xs-3'>
        <img src='img/img1.png' class='image' /> </div>
        <div class='col-xs-3'>
        <img src='img/img2.png' class='image' /> </div>
        <div class='col-xs-3'>
        <img src='img/img3.png' class='image' /> </div>
        <div class='col-xs-3'>
        <img src='img/img4.png' class='image' /> </div>
        </div>
";
                $last_change_in_data_file = $row["gid"];
               }
                $last_change_in_data_file = $row["gid"];
            }
        }
       // mysqli_close($conn);

        // put data.txt's content and timestamp of last data.txt change into array
        $result = array(
            'data_from_file' => $data,
            'timestamp' => $last_change_in_data_file
        );
        mysqli_close($conn);
        // encode to JSON, render the result (for AJAX)
        $json = json_encode($result);
        echo $json;

        // leave this loop step
        break;

    } else {
        // wait for 1 sec (not very sexy as this blocks the PHP/Apache process, but that's how it goes)
        sleep( 1 );
        continue;
    }
}

I tried adding delay of 20 seconds in function like this

$(function() {
    
    setTimeout("getContent();",20000);
});

But for 1st time also it takes 20 secs to display data. Can somebody suggest me where i am going wrong?

Link to post
Share on other sites

i don't think you have gotten much interest in this because it looks like you are just randomly changing code without having a plan.

 

long-polling, or even better, websockets, is just the method that's used to transfer the data. this code/method is at a lower-level and will just be used when the application has new data to send to the client. what the data actually is and what it means is up to your application code.

 

the code works by checking if there is any new data. the following logic is the most important part of the original code -  

 if ($last_ajax_call == null || $last_change_in_data_file > $last_ajax_call) {

this logic is checking if there is no timestamp from the client (which indicates this is the first request from the client) or if there's data with a timestamp that's greater then (a not equal comparison would work too) what was transferred in the last ajax call. also, as i wrote above, there is a problem using a timestamp for this and the value being passed and compared should be the last_id of the data that was transferred.

 

for what you have described, you have a series of steps/states. you should use the step number as the last_id and detect when the step number changes. you would use the step number in the code to determine what logic to perform and what output to send for each step.

 

the id/grp_id, which i assume is the group id, is in addition to the value you use to detect that there is new data. the group id would be used to select/qualify which data to examine. your client code needs to send the group id with the ajax request, in addition to the value being used to detect new data, and your code needs to make sure that the current user is a member of the group id that's contained in the request.

 

as to what your application code needs to be, the best advice i can give, is to sit down with paper and pencil and plan what each type of user (admin, members) will see and do at each step. this will define what data you need or what data will be submitted from the client for each step, what processing you will need to do on the server for each step, and what result you need to produce or output from each step.

Edited by mac_gyver
Link to post
Share on other sites
This thread is more than a year old.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.