Посещает счетчик без базы данных с PHP

У меня есть одна веб-страница, и я хотел бы отслеживать, сколько раз она была посещена без использования базы данных.

Я думал о XML, обновляя файл каждый раз, когда пользователь посещает страницу:

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

Тогда я подумал, что лучше было бы объявить счетчик PHP в отдельном файле, а затем обновлять его каждый раз, когда пользователь посещает страницу.

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); ?> 

При этом каждый раз, когда update_counter.php посещается, переменная в файле counter.php увеличивается.

Во всяком случае, я заметил, что если файл counter.php имеет $counter = 5 а файл update_counter.php посещают 1000 пользователей в одно и то же время, файл считывается 1000 раз в одно и то же время (так что значение 5 читается во всех запросах), файл counter.php будет обновлен со значением 5+1 (=6) вместо 1005 .

Есть ли способ заставить его работать без использования базы данных?

Вы можете использовать flock() который заблокирует файл, чтобы другие процессы не записывали в файл.

Изменить: обновлено, чтобы использовать fread() вместо 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); 
 <?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); ?> 

Это звучит просто, но его действительно трудно решить. Причина – условия гонки .

Каковы условия гонки?
Если вы открываете файл счетчика, читаете содержимое, увеличиваете количество обращений и записываете хиты в содержимое файла, многое может произойти между всеми этими шагами через других посетителей, открывающих один и тот же скрипт на вашем сайте одновременно. Подумайте о ситуации, когда первый запрос посетителей (поток) пишет «484049», обращается к символу-счетчику с помощью char и в миллисекунде, в то время как «484» записывается, второй поток считывает это значение и увеличивает его до «485», теряя большую часть ваши хорошие хиты.

Не используйте глобальные блокировки!
Возможно, вы думаете о решении этой проблемы с помощью LOCK_EX . При этом второй поток должен ждать, пока первый не закончит запись в файл. Но «ожидание» – это то, чего вы действительно не хотите. Это означает, что каждый поток и я действительно подразумеваю, что каждый поток должен ждать других потоков. Вам нужны только некоторые бушующие боты на вашем веб-сайте, многие посетители или временная проблема с i / o на вашем диске, и никто не может загрузить ваш сайт до тех пор, пока все записи не будут завершены … и что произойдет, если посетитель не сможет открыть ваш сайт … он обновит его, вызывая новые ожидания / блокирующие потоки … узкое место!

Использовать блокировки на основе потоков
Единственное безопасное решение – создать мгновенно новый файл счетчика для одновременного запуска потоков:

 <?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); ?> 

Теперь у вас есть count/count.1 , count/count.2 и т. Д. В вашей папке счетчика, в то время как count.1 будет захватывать большинство хитов. Причиной этого является то, что условия гонки не происходят все время. Они происходят только в том случае, если два потока одновременно.

Примечание. Если вы видите (много) более двух файлов, это означает, что ваш сервер работает очень медленно по сравнению с количеством посетителей, которое у вас есть.

Если вам сейчас нужны полные хиты, вам нужно их убрать (в этом примере случайным образом):

 <?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 включить sleep(3) и посмотреть в папку счетчика, чтобы имитировать медленный сервер, и вы видите, как быстро растут файлы с несколькими счетчиками.

 <?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 ?>