Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read a file in real time with PHP and Ajax [closed]

I want to read a txt file in real time and update the new lines added in my front end. The text file holds the logs data of two users. I've tried a bit to find a way to work but both the php script as the js don't work as I want to.

JS Script

  function ShowNewMessages(res, curr, end) {
    // build the front end
    var newdiv1 = "<div class=\"message\"> <div class=\"own-avatar-message\" style=\"background-image: url("+res[curr][4].replace(/\\\//g, "/")+");\"></div><div class=\"own-message2\"><div class=\"own-message\"><p>" +  res[curr][3]  +"</p></div><div class=\"message-timestamp\"> "+ res[curr][1] +" • "+res[curr][2] +"</div></div> </div>"; 

    $( newdiv1 ).insertAfter( $( ".message" ).last());
    var height = 0;
    $('.message').each(function(i, value){
        height += parseInt($(this).height());
    });

    height += '';

    $('div').animate({scrollTop: height});

    // check if there is another messsage or checkagain for new messages             
    curr++;
    if (curr <= end) ShowNewMessages(res, curr, end);
    else {setTimeout(function() {GetNewMesssages()}, 10000);}
}



function GetNewMesssages() {
    $.ajax({
        type:'POST',
        url: "../lib/GetMesssage.php",
        data: "friend=" + MsgFriend,
        dataType: 'json',
        async: false, 
        success:function(data){
            // check error state
            if (data[0] != "Error") {
                var start = 0;
                var end = data.length-1; // minus one for the new line
                if (end > 0 ) ShowNewMessages(data, start, end-1);
                else setTimeout(function() {GetNewMesssages()}, 10000);

            }
            else alert('some error message');
            }
        });   
} 

GetMesssage.php

<?php 

    session_start();

    // get the requirements for the file name
    $MyEmail = $_SESSION['login'];
    $FriendEmail = $_POST['friend'];

    if (strcmp($MyEmail, $FriendEmail) < 0) {
        $FileName = $MyEmail.'-'.$FriendEmail.'.txt';
    }
    else {
        $FileName = $FriendEmail.'-'.$MyEmail.'.txt';
    }


    $File = fopen('data/'.$FileName, "r") or die(json_encode(array_push($messages, "Error")));

    // count the lines exist in the file
    $count = 0;
    while(!feof($File)){
        $tmp = fgets($File);
        $count++;
    }

    // if the session var that holds the offset position is not set 
    // or has value more than the lines of text file
    if (!isset($_SESSION['pos']) || $_SESSION['pos'] >= $count) {
       $_SESSION['pos'] = 0; // back to the beginning
    } 

    // the array that holds the logs data
    $messages = array(); 

    // move the pointer to the current position
    fseek($File, $_SESSION['pos']);

    // read the file
    while(!feof($File)){
        $msg = fgets($File);
        $msg1 = explode("<!@#@>", $msg);
        array_push($messages, $msg1);
        $_SESSION['pos']++;
    }

    // get the current offset position
    //$_SESSION['pos'] = ftell($File);

    // return the array
    echo json_encode($messages);

    fclose($File)
?>

Explainig

Assume that the routine starts for the first time when we call the GetNewMesssages() function. The function will fire the php and get the data from the file.

Next if the data holds somenthing, the return array isn't empty, we execute the ShowNewMessages() function and we update the front end.

When we finish we start another GetNewMesssages() callback.

What I want

I want to check the file for new added lines every N seconds and update my front end.

Problems

I have the problems both in php and js script. I didn't want to add two questions.

In PHP

I didn't found how to use the ftell() to get only the new added lines. As the script works like it is, I get constantly all the file into my array in every update.

In JS

In JS I have problems synchronize the ajax call. That means in every call of the GetNewMessages() function I get 2-3 calls again of the function. It has to do with setTimeout() but I also couldn't find how to set the call back.

UPDATE

An example of the text file I read from:

line_id <BreakString> some data <BreakString> blah blah

another_line_id <BreakString> some_other data <BreakString> blah blah 2

Note: doesn't mean anything. It's just separate the contents to array.

UPDATE 2

After a moderator notice, I'll small my question only to find what is the best method to read a file in real time keeping my structure.


1 Answers

PHP

In PHP fseek() sets the file pointer to a specific byte offset in the file. The assumption you appear to be making in your code is that fseek() will set the pointer to a specific line in the file.

Assuming your messages do not contain any linefeed characters (no such truth is founded in this code), you could just use file() instead to load the entire file into an array in PHP and determine the line offset you need that way.

For example...

$file = file($fileName);
$mssages = array_slice($file, $_SESSION['pos']);

This would give you an array of all lines in the file starting at $_SESSION['pos'] line offset.


Refactoring your existing PHP code

To refactor your existing code instead you could modify the following loop to stop reading at the point where it hits $_SESSION['pos'] and thus continue your logic of loading new messages from that point on...

$count = 0;
while(!feof($File)){
    $tmp = fgets($File);
    $count++;
}

Instead you do this...

$count = 0;
while(!feof($File) || $count < $_SESSION['pos']){
    $tmp = fgets($File);
    $count++;
}

Now remove the fseek($File, $_SESSION['pos']) part and your second loop should pick up at the line where you expect it to.


File Format

If the file storing these messages is intended to be a line-separated format, where each line in the file represents exactly one message (this appears to be the assumption you are making in your code) then the safest way to avoid the message contents itself from corrupting your file is to encode the message contents in a manner that does not allow for linefeed characters in the message payload. Examples of such encodings include JSON, and Base64.

The latter will give you about 33% more inflated storage cost, but both will be safe for a line-separated file format.


Javascript

It is unclear why your javascript logic uses curr and end to determine whether or not it should use setTimeout(). If you want a global loop to constantly check for new data every 10 seconds you should be calling setInterval(GetNewMesssages, 10000) from the global scope once instead of using setTimeout() conditionally. In your code if the condition that the current message is the last message found you will no longer call your PHP again from js to check for new messages, which clearly isn't what you want. The difference between setTimeout and setInterval is that setTimeout is only triggered once when the timer is reached, whereas setInterval will continuously trigger the callback at the given interval.

like image 185
Sherif Avatar answered Dec 21 '25 23:12

Sherif



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!