PHP hit counter resets / drops (file locking)

I’m using the following basic PHP:

 <?php

    	if (file_exists('count_file.txt'))
    	{
    		$fil = fopen('count_file.txt', r);
    		$dat = fread($fil, filesize('count_file.txt'));
    		echo $dat+1;
    		fclose($fil);
    		$fil = fopen('count_file.txt', w);
    		fwrite($fil, $dat+1);
    	}

    	else
    	{
    		$fil = fopen('count_file.txt', w);
    		fwrite($fil, 1);
    		echo '1';
    		fclose($fil);
    	}
    ?>

as a hit counter (I’d rather not have one but it’s been insisted we do).
The txt file keeps count of the hits and it works…however the counter randomly (sometimes after a few weeks, sometimes months later) decides to trip up and drops from say 4300 to 11. I have been told this is due to not using file locking so if more than one person lands on the site at the same time - it trips up. I was told to implement flock but having looked at the PHP manual I think it is over my head, can anyone tell me how to rectify this as it is becoming annoying??

Use a database.

Based on http://us3.php.net/manual/en/function.flock.php

 <?php
    
        $dat = file_get_contents('count_file.txt');
    	$fil = fopen('count_file.txt', 'w');
        if (flock($fil, LOCK_EX)) {
    		echo $dat+1;
    		fwrite($fil, $dat+1);
    		flock($fil, LOCK_UN);
    	}
    	fclose($fil);

    ?>

I’ve also cleaned up your code quote a bit. I used file_get_contents to read the file, the ‘w’ to write the file.

I also removed the the file_exists check as ‘w’ will automatically create the file

He shouldn’t be storing data like this in a text file. He should be storing it in a database. So this isn’t the correct answer.

The correct answer is to use a database.

I completely disagree, simply because Apache logs, or IIS logs could technically be used to get a visitor count all by themselves, thus no code necessary.

I also disagree that a database must be used. This is a very simplistic idea, and to add in the connection time to get to a database and table is just pointless for something like this.

If 10 people connect at once, how many hits will get logged in that file?

The same number of hits that will get counted in the database.

To explain it more thoroughly, since my last response was a bit rash. flock, like lock() in other languages (.NET, Java) will block the execution of the script until the lock is freed up and can be established by the next user. Thus, if 10 people hit it at once, first person locks it, the other 9 wait, first person releases the lock, 2nd person locks it, the other 8 wait, etc. All of this happens in milliseconds since he is writing a book, just a simple number.

Much appreciated cpradio - giving this a go and have my fingers crossed it will no longer trip up!

Unfortunately today it has again dropped from over 5000 to 13…anyone else with any ideas?

Use a database…

I forgot about the fact that file_get_contents can return false, which is equivalent to 0, this will prevent it from reseting in the event it returns false

 <?php

        $dat = file_get_contents('count_file.txt');
        if ($dat !== false)
        {
            $fil = fopen('count_file.txt', 'w');
            if (flock($fil, LOCK_EX)) {
                echo $dat+1;
                fwrite($fil, $dat+1);
                flock($fil, LOCK_UN);
            }
            fclose($fil);
        }

    ?>

If you want to ensure every person is counted (be aware, this can invoke an infinite loop if it isn’t a locking issue), you can use the below code

 <?php

        //$preventInfiniteLoop = 0; // uncomment to help prevent an infinite loop
        do
        {
            $dat = file_get_contents('count_file.txt');
            if ($dat !== false)
            {
                $fil = fopen('count_file.txt', 'w');
                if (flock($fil, LOCK_EX)) {
                    echo $dat+1;
                    fwrite($fil, $dat+1);
                    flock($fil, LOCK_UN);
                }
                fclose($fil);
            }
            //$preventInfiniteLoop++; // uncomment to help prevent an infinite loop
        } while ($dat === false); // add && $preventInfiniteLoop < 5 to prevent an infinite loop due to locking

    ?>


<?php
        $dat = 0;
        if (file_exists('count_file.txt'))
        {
       	$fil = fopen('count_file.txt', 'w+');
        	if (flock($fil, LOCK_EX))
        	{
        		$dat = fread($fil, filesize('count_file.txt'));
        	}
        }
        else
        {
       		$fil = fopen('count_file.txt', 'w+');
        }
        echo $dat+1;
        fwrite($fil, $dat+1);
        flock($fil, LOCK_UN);
        fclose($fil);
?>

dont use fread, use fgets or file_get_contents, fread will overwrite, unless thats what you want, i only skimmed the code, that seems to be a problem…
also use fputs instead of fwrite, i think it works better. Also come to think of it, you also have to rewind($handle) with fwrite…
http://php.net/manual/en/function.rewind.php

Not if you open the file with ‘w+’