php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #25649 feof behavior changed - inconsistent!
Submitted: 2003-09-24 20:12 UTC Modified: 2003-10-08 07:11 UTC
Votes:5
Avg. Score:5.0 ± 0.0
Reproduced:5 of 5 (100.0%)
Same Version:5 (100.0%)
Same OS:5 (100.0%)
From: lew at mailduct dot com Assigned: wez
Status: Closed Package: Filesystem function related
PHP Version: 4.3.3 OS: *
Private report: No CVE-ID:
 [2003-09-24 20:12 UTC] lew at mailduct dot com
Description:
------------
The behavior of "feof" has changed with the newer versions of PHP (4.1.1 exhibited different behavior).  I believe 4.3.X has a problem with how it handles "feof" under FreeBSD.

For example, suppose I want to "tail" a logfile that keeps growing, such as "maillog" or even "httpd-access.log".  In the old PHP, once I reached the end of file, "feof" would become true.  If a process *added* to the file, then "feof" would become false until I read to the end of the file again.  This is consistent with "tail" behavior.  Under the new PHP (4.3.X), once "feof" becomes true, it *never* goes false again.  Thus, it is *impossible* to "tail" a file!!

Reproduce code:
---------------
<?php

$fh = fopen( '/var/log/maillog' );

//  Endless loop, for testing purposes
while( TRUE ) {

  //  Perform a "tail" on a growing logfile
  while( !feof($fh) ) {
    $log = fgets( $fh,512 );
    print( $log );
  }

  //  We've hit the end, until more data ready
  print( "EOF detected... sleeping\n" );
  sleep( 1 );
}

?>

Expected result:
----------------
I expect to see all the lines contained in maillog until we hit the EOF.  Then I expect to see the "EOF detected" until more lines are added to maillog via another process.  At that point, I expect to see the new lines of data until we hit the new EOF point again.

If I replace "fopen" with "popen" like this:
  $fh = popen( 'tail -f /var/log/maillog','r' );
then it works.  But I shouldn't have to spawn off a tail to do what the older 4.1.X version of PHP used to do.  Someone has changed something in how EOF is detected (and reset). 

Can you please see if you can find the cause of this.

Thank you for listening.



Actual result:
--------------
test line 1
test line 2
test line 3
EOF detected... sleeping
EOF detected... sleeping
EOF detected... sleeping
... forever ...
EOF detected... sleeping

(even though maillog continues to have more lines appended to it, either through a daemon or a simple "cat more.txt >> /var/log/maillog" ).


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2003-09-24 20:17 UTC] lew at mailduct dot com
My typo.  Make that:  $fh = fopen( '/var/log/maillog','r' );
 [2003-09-26 06:30 UTC] sniper@php.net
Actually this is not really bug. The way you try to implement 'tail -f' is inherently wrong.

 [2003-09-26 11:01 UTC] lew at mailduct dot com
As I pointed out, this used to work on an older 4.X series of PHP.  My use of feof is consistent with what the PHP4 documentation states.  Therefore, please elaborate if you believe it is not a bug.  Without continuously doing a "stat" and bytecount of the file (aka: wasteful look), how else would you implement this?  The behavior of "end of file" detection has been changed, thus creating this problem.
 [2003-09-26 11:23 UTC] wez@php.net
I will look into it this weekend.
 [2003-10-08 07:11 UTC] wez@php.net
As stated by sniper, your script is technically incorrect,
although you did highlight a problem with feof().

The background on this bug is that prior to the introduction
of the streams API, PHP feof() would simply call the
libc feof() function.

The docs for feof() under linux state that once the EOF
indicator has been set, it can only be reset by calling
clearerr() or by seeking the file pointer.  From what you
have described, it sounds like the BSD version of feof()
does a little more than that.

The PHP streams implementation uses the following logic
for determining the EOF status:

- after a read attempt, if no bytes were read OR
  (a read error occurred and the error != EWOULDBLOCK)
  --> set the EOF indicator
  otherwise, clear the EOF indicator [1]

- after a successful fseek(), clear the EOF indicator.

[1] - this step was missing and has just been comitted to
the CVS.

The feof() function call works like this:
- if stream buffer has data, return false
- otherwise, return the EOF indicator flag.

Hopefully you will agree that the current behaviour is
somewhat more portable than before.

In terms of your script, the correct way to implement
tail is something like this (untested!):

$fp = fopen($file, 'r');
while (true) {
   $r = array($fp);
   $n = stream_select($r, $w = null, $e = null, 30);
   if ($n) {
      echo fgets($fp);
   }
}
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Thu Apr 17 21:01:56 2014 UTC