Alessandro Tedesco
Alessandro Tedesco

Reputation: 144

PHP counter issue using flock()

I have a problem with a counter. I need to count two variables, separated with a |, but sometimes the counter doesn't increase a variable's value.

numeri.txt (the counter):

5240|593389

This is the PHP script:

$filename="numeri.txt";
$fp=fopen($filename,"r");
if(!flock($fp,LOCK_SH))
{
    while(true)
    {
        usleep(100000);
        if(flock($fp,LOCK_SH))
        {
            break;
        }
    }
}
$contents=fread($fp,filesize($filename));
flock($fp,LOCK_UN);
fclose($fp);
$fp=fopen($filename,'a');
if(!flock($fp,LOCK_EX))
{
    while(true)
    {
        usleep(100000);
        if(flock($fp,LOCK_EX))
        {
            break;
        }
    }
}
ftruncate($fp,0);
$contents=explode("|",$contents);
$clicks=$contents[0];
$impressions=$contents[1]+1;
fwrite($fp,$clicks."|".$impressions);
flock($fp,LOCK_UN);
fclose($fp);

I set the counter to the right value but after 3-4 days the counter hasn't counted about 50 impressions (the number after "|")

How to fix the code?

Upvotes: 2

Views: 312

Answers (1)

Robbie
Robbie

Reputation: 17710

Two problems:

1) There is still the opportunity to do a read, another process to write the file, then this process write the file. Millisecond timing required, but it's possible.

2) You don't actually verify the "open" works - it can fail.

The solution is to firstly check for open failure and retry, and secondly do one lock - exclusive. Paraphrased code:

while (!$fh = fopen($file, 'c+')) {   // Read write, do not truncate, place pointer at start.
    usleep(100000);
}
while (!flock(LOCK_EX)) {
    usleep(100000);
}
$content = fread();
// Process content...
ftruncate();
fseek(0);  // Or frewind();
fwrite();
fclose();  // Also releases the lock.

Upvotes: 3

Related Questions