1

I'm trying to write to a file with PHP and this is the code I'm using (taken from this answer to my previous question):

$fp = fopen("counter.txt", "r+");

while(!flock($fp, LOCK_EX)) {  // acquire an exclusive lock
    // waiting to lock the file
}

$counter = intval(fread($fp, filesize("counter.txt")));
$counter++;

ftruncate($fp, 0);      // truncate file
fwrite($fp, $counter);  // set your data
fflush($fp);            // flush output before releasing the lock
flock($fp, LOCK_UN);    // release the lock

fclose($fp);

The read part works fine, if the file gets read, it's content is read well, i.e. if the file contains 2289, then 2289 is read.

The problem is that when it increments and rewrites the value to that file, [NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL]1 gets written.

What am I missing? Why do null characters get written?

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
BackSlash
  • 21,927
  • 22
  • 96
  • 136
  • It's secondary to what you're asking about, but the locking code here is pretty weird. Without the `LOCK_NB` flag, `flock` will block until it can acquire a lock, so it makes little sense to loop waiting for it to return `true`. The only reason it'd return `false` without `LOCK_NB` is if you failed to open the file and are passing it `null` as the first argument, and waiting definitely isn't going to help with that... and the loop means you'll be spewing warnings in an infinite loop in that scenario. – Mark Amery Jan 13 '20 at 19:08
  • @MarkAmery I've never been a PHP expert and especially in 2013 I was at the beginning of my career, so that was a beginner mistake for sure :) Thank you for pointing that out! – BackSlash Jan 17 '20 at 09:38

3 Answers3

5

The thing you are missing is rewind(). Without it, after you truncate to 0 bytes, the pointer is still not at the beginning (reference). So when you write your new value, it pads it with NULL in your file.

This script will read a file (or create if it doesn't exist) for a current count, increments, and writes it back to the same file every time the page loads.

$filename = date('Y-m-d').".txt";

$fp = fopen($filename, "c+"); 
if (flock($fp, LOCK_EX)) {
    $number = intval(fread($fp, filesize($filename)));
    $number++;

    ftruncate($fp, 0);    // Clear the file
    rewind($fp);          // Move pointer to the beginning
    fwrite($fp, $number); // Write incremented number
    fflush($fp);          // Write any buffered output
    flock($fp, LOCK_UN);  // Unlock the file
}
fclose($fp);
André Chalella
  • 13,788
  • 10
  • 54
  • 62
Elte156
  • 188
  • 2
  • 7
  • This drove me crazy, I went on a wild goose chase thinking it was character encodings, BOM etc. Turns out it was this! Thanks so much – Ian May 23 '19 at 08:22
  • This should be the accepted anwser. It explains the why of the `NUL` entries in the question. – AitorF Dec 15 '20 at 11:46
0

EDIT #2:

Try this with flock (tested)

If file is not locked, it will throw an Exception (see added line) if(...

I borrowed the Exception snippet from this accepted answer.

<?php

$filename = "numbers.txt";
$filename = fopen($filename, 'a') or die("can't open file");

if (!flock($filename, LOCK_EX)) {
    throw new Exception(sprintf('Unable to obtain lock on file: %s', $filename));
}

file_put_contents('numbers.txt', ((int)file_get_contents('numbers.txt'))+1);

// To show the contents of the file, you 
// include("numbers.txt");

    fflush($filename);            // flush output before releasing the lock
    flock($filename, LOCK_UN);    // release the lock


fclose($filename);
echo file_get_contents('numbers.txt');

?>
Community
  • 1
  • 1
Funk Forty Niner
  • 74,450
  • 15
  • 68
  • 141
-1

You can use this code, a simplified version, but am not sure if it's the best:

<?php
$fr = fopen("count.txt", "r");
$text = fread($fr, filesize("count.txt"));
$fw = fopen("count.txt", "w");
$text++;
fwrite($fw, $text);
?>
SRKR
  • 33
  • 8