I have this code in PHP writing views to a text-file and just increasing the number. My question is here: What happens if two or more people are using my site and running the PHP script? Will the server handle it? Will the increasement be saved to the file?
Here is my code (if it helps):
<?php
$clicks = file_get_contents("clicks.txt");
$clicks++;
$fp = fopen("clicks.txt", "w+");
fwrite($fp, $clicks);
fclose($fp);
//give the count to the user
echo "result: $clicks";
?>
First of all: This is not the way I would write a click counter.
That said, 100 users hitting your server at the same time (with initial clicks at 0) might result in a recorded number of 1..100 with low (=wrong) values being prominent.
EDIT: Implementations
I created the below implementations for a file counter in a text file, SQLite and MySQL
Please do not flame me for using the mysql_*()
function family - as allways the code is meant to be instructive, not productive: instructive in the sense of concentrating on the issue at hand, not the surrounding layers.
counter-file.php:
<?php
//acquire file handle
$fd=fopen('counter.txt','c+b');
if (!$fd) die("Can't acquire file handle");
//lock the file - we must do this BEFORE reading, as not to read an outdated value
if (!flock($fd,LOCK_EX)) die("Can't lock file");
//read and sanitize the counter value
$counter=fgets($fd,10);
if ($counter===false) die("Can't read file");
if (!is_numeric($counter)) {
flock($fd,LOCK_UN);
die("Value in file '$counter' is not numeric");
}
//increase counter and reconvert to string
$counter++;
$counter="$counter";
//Write to file
if (!rewind($fd)) {
flock($fd,LOCK_UN);
die("Can't rewind file handle");
}
$num=fwrite($fd,$counter);
if ($num!=strlen($counter)) {
flock($fd,LOCK_UN);
die("Error writing file");
}
//Unlock the file and close file handle
flock($fd,LOCK_UN);
fclose($fd);
printf ("Counter is now %05d",$counter);
?>
counter-sqlite.php:
<?php
//counter.sqlite3 was created with
//CREATE TABLE counter (counter NUMERIC)
//INSERT INTO counter VALUES (0)
//Open database
$dsn='sqlite:'.dirname(__FILE__).'/counter.sqlite3';
$db=new PDO($dsn);
if (!$db) die("Can't open SQlite database via DBO");
//Make exclusive
$sql="BEGIN EXCLUSIVE TRANSACTION";
if ($db->exec($sql)===false) die("Error starting exclusive transaction");
//Update counter
$sql="UPDATE counter SET counter=counter+1";
if (!$db->exec($sql)) die("Error inserting into database");
//Read value
$sql="SELECT counter FROM counter";
$result=$db->query($sql);
if (!$result) die("Error querying database");
foreach ($result as $row) $counter=$row['counter'];
//Commit
$sql="COMMIT TRANSACTION";
if (!$db->exec($sql)) die("Error committing to database");
//Print result
printf("Counter is now %05d",$counter);
?>
counter-mysql.php:
<?php
//mysql database was created with
//CREATE TABLE counter (counter INT NOT NULL)
//INSERT INTO counter VALUES (0)
//Open database connection and select database
$db=mysql_pconnect('127.0.0.1','redacted','redacted');
if (!$db) die("Can't open database");
if (!mysql_select_db('redacted', $db)) die("Can't select database");
//Update counter
$sql="UPDATE counter SET counter=counter+1";
$qry=mysql_query($sql,$db);
if (!$qry) die("Error updating database");
//Read value
$sql="SELECT counter FROM counter";
$qry=mysql_query($sql,$db);
if (!$qry) die("Error reading from database");
$counter=mysql_fetch_array($qry,MYSQL_ASSOC);
if (!$counter) die("Error reading result");
//Print result
printf("Counter is now %05d",$counter['counter']);
?>
As for the performance: I stand corrected. The SQLite implementation is 100 times slower than the two others - this is because I had to accept, that nothing else than START EXCLUSIVE TRANSACTION
would end a test of ab -n 1000 -c 50 http://127.0.0.1/stackoverflow/counter/counter-sqlite.php
with 1000 clicks counted.
My recommendation for the OP is to use the MySQL version - it is fast and will reliably save the counter over an OS crash. The file version has nearly the same performance characteristics, but it can quite easily be destroyed by an OS crash.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With