Visits a counter without a database with PHP

I have one web page and I would like to track how many times it was viewed without using a database.

I was thinking about XML, updating the file every time the user visits the page:

<?xml version='1.0' encoding='utf-8'?> <counter>8</counter> 

Then I thought it would be better to declare the PHP counter in a separate file, and then update it every time the user visits the page.

counter.php

 <?php $counter = 0; ?> 

update_counter.php:

 <?php include "counter.php"; $counter += 1; $var = "<?php\n\t\$counter = $counter;\n?>"; file_put_contents('counter.php', $var); ?> 

At the same time, every time you visit update_counter.php variable in the counter.php file increases.

Anyway, I noticed that if the counter.php file has $counter = 5 and the update_counter.php file update_counter.php visited by 1000 users at the same time, the file is read 1000 times at the same time (so the value 5 read in all requests), the counter.php file will be updated with a value of 5+1 (=6) instead of 1005 .

Is there a way to make it work without using a database?

+4
source share
4 answers

You can use flock() , which locks the file so that other processes do not write to the file.

Edit: updated to use fread() instead of include()

 $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); 
+6
source
 <?php /** * Create an empty text file called counterlog.txt and * upload to the same directory as the page you want to * count hits for. * * Add this line of code on your page: * <?php include "text_file_hit_counter.php"; ?> */ // Open the file for reading $fp = fopen("counterlog.txt", "r"); // Get the existing count $count = fread($fp, 1024); // Close the file fclose($fp); // Add 1 to the existing count $count = $count + 1; // Display the number of hits // If you don't want to display it, comment out this line echo "<p>Page views:" . $count . "</p>"; // Reopen the file and erase the contents $fp = fopen("counterlog.txt", "w"); fwrite($fp, $count); // Close the file fclose($fp); ?> 
+3
source

It sounds simple, but it is very difficult to solve. The reason is racing conditions .

What are racial conditions?
If you open the counter file, read the contents, increase the number of hits and write hits to the contents of the file, many things can happen between all these steps through other visitors opening the same script on your site at the same time. Think about the situation when the first request of visitors (stream) writes β€œ484049”, gets to the char counter file in char and in milliseconds, while β€œ484” is written, the second stream reads this value and increases it to β€œ485” to lose most your pleasant blows.

Do not use global locks!
Perhaps you decided to solve this problem using LOCK_EX . In this case, the second thread must wait until the first one finishes writing to the file. But "waiting" is what you really don't want. This means that every thread and I really mean that every thread has to wait for other threads. You only need some raging bots on your website, many visitors or a temporary problem with i / o on your drive, and no one can load your site until all entries are complete ... and what happens if the visitor will not be able to open your site ... he will update it, causing new expectations / blocking threads ... bottleneck!

Use thread based locks
The only safe solution is to instantly create a new counter file to start threads simultaneously:

 <?php // settings $count_path = 'count/'; $count_file = $count_path . 'count'; $count_lock = $count_path . 'count_lock'; // aquire non-blocking exlusive lock for this thread // thread 1 creates count/count_lock0/ // thread 2 creates count/count_lock1/ $i = 0; while (file_exists($count_lock . $i) || !@mkdir ($count_lock . $i)) { $i++; if ($i > 100) { exit($count_lock . $i . ' writable?'); } } // set count per thread // thread 1 updates count/count.0 // thread 2 updates count/count.1 $count = intval(@file_get_contents($count_file . $i)); $count++; //sleep(3); file_put_contents($count_file . $i, $count); // remove lock rmdir($count_lock . $i); ?> 

Now you have count/count.1 , count/count.2 , etc. in your counter folder, while count.1 will capture most hits. The reason for this is that race conditions do not occur all the time. They occur only if there were two flows at the same time.

Note. If you see (many) more than two files, this means that your server is very slow compared to the number of visitors that you have.

If you now need full hits, you need to remove them (in this example randomly):

 <?php // tidy up all counts (only one thread is able to do that) if (mt_rand(0, 100) == 0) { if (!file_exists($count_lock) && @mkdir($count_lock)) { $count = intval(@file_get_contents($count_file . 'txt')); $count_files = glob($count_path . '*.*'); foreach ($count_files as $file) { $i = pathinfo($file, PATHINFO_EXTENSION); if ($i == 'txt') { continue; } // do not read thread counts as long they are locked if (!file_exists($count_lock . $i) && @mkdir($count_lock . $i)) { $count += intval(@file_get_contents($count_file . $i)); file_put_contents($count_file . $i, 0); rmdir($count_lock . $i); } } file_put_contents($count_file . 'txt', $count); rmdir($count_lock); } } // print counter echo intval(@file_get_contents($count_file . 'txt')); ?> 

PS enable sleep(3) and look in the counter folder to simulate a slow server, and you can see how quickly files with several counters grow.

+1
source
 <?php $File = "counter.txt"; //This is the text file we keep our count in, that we just made $handle = fopen($File, 'r+') ; //Here we set the file, and the permissions to read plus write $data = fread($handle, 512) ; //Actully get the count from the file $count = $data + 1; //Add the new visitor to the count print "You are visitor number ".$count; //Prints the count on the page ?> 
0
source

Source: https://habr.com/ru/post/1497069/


All Articles