Why does it sometimes return an empty string file_get_contents or fgets ?

In this case, we will consider the function fgets .

It was tested with the http and https protocol, the same thing happens. The truth, it seemed, with http works more stable, but anyway, sooner or later it returns an empty string. Checked without the .htaccess file to exclude additional options for the occurrence of errors.

To cause an error , you need to go to the address of this page, the code that I post, several times in a row, changing the value of the number variable in the GET request.


Here is an example :

Step 1 . http: //myhost/1/index.php? number

Step 2 . http: //myhost/1/index.php? number

Step 3 . Here at this step, fgets can already produce an empty string, but if it did not produce an empty string, try further changing the variable in the GET request http: //myhost/1/index.php? Number = 194

Step 4 . http: //myhost/1/index.php? number = 19

Step 5 . http: //myhost/1/index.php? number = 1

Step 6 . http: //myhost/1/index.php? number =

Step 7 . http: //myhost/1/index.php? number

Step 8 . http: //myhost/1/index.php? number = 195

.... etc

When fgets returns an empty string, fopen at that time gives it away -

resource(199) of type (stream) , that is, apparently, this is probably normal for fopen , but until the end I'm not sure.


Cat code :

 <?php $a2 = $_GET['number']; if($_SERVER['REQUEST_METHOD'] == 'GET'){ for ($i=0; $i < 1000; $i++) { //Проверяем, есть ли этот файл if(file_exists(__DIR__.'/cahe.json')){ $fp = fopen(__DIR__.'/cahe.json', "r" ); if($fp == false || $fp == ''){ return var_dump("============= ОШИБКА =============="); } $mytext = '-'; while (!feof($fp)) { $mytext = fgets($fp, 999); } if($mytext == ''){ var_dump($mytext); return var_dump($fp); } $json_data = json_decode($mytext, true); fclose($fp); $c = $json_data['count_m'] + 1; $data = '{"count":"1", "count_m":"'.$c.'"}'; $fp = fopen(__DIR__.'/cahe.json', "w" ); fwrite($fp, $data); fclose($fp); }else{ return var_dump("============= ФАЙЛА НЕТУ =============="); } } } 

Note * If, go directly to this page, without passing a variable in a GET request

http: //myhost/1/index.php

And , remove this piece of code:

 $a2 = $_GET['number']; if($_SERVER['REQUEST_METHOD'] == 'GET'){} 

Then everything works stably ( DOES NOT WORK STABLE, ENTRIES TO FILE AS HIT, JUST A BLANK LINE IS NOT DRAWN ).

Here is code2 (also unstable)

  for ($i=0; $i < 1000; $i++) { $data = file_get_contents(__DIR__.'/cahe.json'); $json_data = json_decode($data, true); if($data == false || $data == ''){ return var_dump($data.' - '.$i.' $number-'.$number); }else{ $count_index = $json_data['count_index']; $count_index = $count_index + 1; $data = '{"count":"1", "count_index":"'.$count_index.'"}'; // Флаг LOCK_EX для предотвращения записи данного файла кем-нибудь другим в данное время file_put_contents(__DIR__.'/cahe.json', $data, LOCK_EX); } } 
  • I honestly tried to find where you use in this piece there is no number parameter or something sensitive to the type of the request method, but I did not find. And I did not understand the sacred meaning of the cycle for 1000 iterations ... - fens
  • @fens For each pass in the loop, a new record is added to the file - '{"count":"1", "count_m":"'.$c.'"}'; You read everything correctly, the variable number not used. It just stands there for research, in order to show this bug or error. - gilo1212

1 answer 1

It looks like a banal race condition, a race condition. At the end of each of the 1000 loop iterations, you open the file with the w modifier. Those. open the file for writing, put the pointer to the beginning of the file, cut the file to zero length. A parallel stream between fopen(..., 'w') and before fwrite will receive a zero-length file.

Yes, file functions do not block other operations on the same file. Generally do not block. You can easily write one file in two streams and get as a result the most complete as lucky.

For concurrent processing of a single file, grab the recommended flock lock. Once again I pay attention - the lock is a recommendation. You are obliged to check it always and everywhere when working with this file.

To read a file, capture LOCK_SH , to write LOCK_EX . To overwrite a file (!), You must:

  1. open file in read and write mode r+
  2. lock writing lock LOCK_EX
  3. read file
  4. change data in memory
  5. clear file by calling ftruncate
  6. write new data
  7. make fflush
  8. and only then release the lock
  9. close the handle by calling fclose

Otherwise, you will lose data.

Who said that working with files is easy? Competitive file access is a storehouse of interesting rakes and a great way to kill the performance of competitive queries. It has not yet reached the question of what to do if for some reason the script fell between ftruncate but to fwrite and other delights of recovering from a crash.


How can you catch the race condition, going consistently to a specific clearly local url browser - there are options. For example, a browser may send two requests instead of one. Or even ask for something, even a favicon, and she accidentally does not exist and revitalizes and is passed to the front controller, index.php. Or something else logical, but that does not come to mind, until you know about it.

  • I will do everything that you wrote, and accomplish your goal. Thanks for the help. - gilo1212
  • Nyche yourself! Exactly, everything works like a clock, everything is stable. Wow. How could a GET request with a variable affect this? I do not understand anything. - gilo1212
  • The variable in the GET request has nothing to do with it, or rather, it seems to reveal, shows an error, but no longer applies to anything. Simply, if you send a request with the same value of a variable, the error will not be visible, there will be no fgets producing an empty string. But at the same time, the data will be lost when writing to the file. This answer is correct! Thank you very much. - gilo1212
  • But what about file_put_contents and file_put_contents they are unstable. I added their code to my question. - gilo1212
  • And if you open not w , but a ? - Qwertiy