После создания сайта у меня возникла необходимость сделать учёт посещаемости. Надо сказать, что на хостинге и так стоит WebAlizer, который работает на стороне сервера и собирает довольно обширную статистику. Но и у него есть свои недостатки, самый главный - невозможность точно сказать, сколько человек действительно было на сайте и смотрело страницы. Суть проблемы заключается в том, что помимо пользователей есть ещё и различные программы, например, поисковые пауки или программы взлома, которые также запрашивают страницы сайта, но не отображают их пользователю. Ещё некоторые клиенты запрашивают файлы напрямую, что, в принципе, не является посещением самого сайта. В связи с этим появилось желание поставить обычный графический счётчик, который учитывает именно посетителей.
Среди большого количества бесплатных счётчиков, которые предоставляют сервис статистики, я выбрал HotLog. Этот выбор был обусловлен тем, что HotLog, в общем, неплохой сервис и предоставляет то самое, чего не хватает WebAlizer. Их графический счётчик показывает общее число посещений и прибавку посетителей за текущий день.
Однако, после некоторого времени использования счётчика, наблюдалась следующая ситуация - сервер статистики, который выдаёт картинку, был недоступен огромное количество времени. Это и понятно - бесплатный сервис работает по остаточному принципу. Но разница, которая была между хотлоговским счётчиком и WebAlizer, меня не устраивала. Так родилась идея написать очень простой и быстрый счётчик, который бы учитывал общее число посетителей, прибавку за день и количество посещённых страниц.
Разработать графический счётчик посещений, который бы учитывал общее количество посещений, прибавку посещений за день и количество просмотренных страниц.
От постановки задачи уже можно перейти к условностям :-). Решение программных задач очень схоже с решением уравнений. Итак, пускай посетитель у нас будет хостом, просмотр страницы хитом. Пускай хостом будет считаться браузер, который не передал секретную куку (cookie - печенье), или в переданной куке содержится вчерашняя дата. Вообще, в куке будет храниться дата, когда она (кука) была установлена счётчиком. Это нужно, чтобы разделять посетителей по дням, то есть, если пользователь зашёл вчера и посетил 10 страниц, то должны насчитаться 1 хост и 10 хитов. Если этот же пользователь зашёл уже сегодня и посетил 5 страниц, то это 1 хост и 5 хитов. То есть, статистика пользователей должна быть посуточной. Хитом в этом случае будет считаться любое посещение пользователя.
Для хранения статистики я выбрал обычный текстовый файл, где будет храниться всего одна строка. Это будет самый быстрый вариант хранения данных. Конечно, много информации так не сохранишь, но мне это и не требуется.
Поскольку счётчик графический, то самое главное, что нельзя забывать, необходимо проверить доступность функций для работы с графикой. Эти функции не являются обязательными для PHP и зависят от библиотеки gd. Поэтому нужно всегда проверять, доступны ли они.
Второй момент - графический фон счётчика. Всё-таки намного эстетичней, когда счётчик сам по себе не портит дизайн сайта и вписывается в него органично. Поэтому нужно предусмотреть два варианта, когда есть подложка и когда файл-подложка недоступен. Если файла с фоном нет, то его (фон) нужно нарисовать.
И третье, что нужно учесть - блокировка файла на время обработки. Блокировать нужно на тот случай, если будет несколько обращений к файлу статистики одновременно. Если же файл не блокировать, то вполне вероятно, что будет ситуация, когда либо сам файл повредится, либо некоторые данные будут теряться.
Помня обо всём этом, можно приступать к написанию скрипта.
Графический счётчик - index.php |
|
1 |
<? /** * Графический счётчик посещений * Схема работы: * 1. устанавливаем куку * 2. считываем файл с данными посещений * 3. прибавляем хиты * 4. если кука не установлена или в ней вчерашняя дата, то прибавляем хосты * 5. записываем данные в файл * 6. выводим картинку со статистикой * * @author zg (//anton-pribora.ru) * @package imageCounter */ // Выключаем вывод ошибок error_reporting(0); // Запрет кэширования header('Expires: Mon, 11 Jul 1991 03:00:00 GMT'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); // Объявляем некоторые константы define('COOKIE_NAME', '__count_date'); define('SPLITTER' , ' '); define('STAT_FILE' , 'stat.txt'); define('TODAY_TIME' , time()); define('TODAY_DATE' , date('Y-m-d') ); define('COUNTER_IMAGE_FILE', 'counter.gif'); define('COUNTER_IMAGE_TYPE', 'gif'); // Получение куки $userTime = isset($_COOKIE[ COOKIE_NAME ]) ? (int) $_COOKIE[ COOKIE_NAME ] : null; // Установка куки setcookie(COOKIE_NAME, TODAY_TIME, TODAY_TIME + 60 * 60 * 24); // Проверка на хост (хостом пусть будет браузер без куки) if ( $userTime ) $isNewHost = date('Y-m-d', $userTime) !== TODAY_DATE; else $isNewHost = true; // Хитом будем называть просто показ страницы $isHit = true; // Обнуляем суммарные значения счтёчика $totalHosts = (int) $isNewHost; $totalHits = (int) $isHit; $todayHosts = (int) $isNewHost; $todayHits = (int) $isHit; // Открываем файл статистики для чтения и записи if ( $fp = fopen(STAT_FILE, 'a+b') ) { // Блокируем файл, чтобы не дать другим процессам переписать файл до его обработки if ( flock($fp, LOCK_EX) ) { // Файл успешно блокирован, выполняем его обработку // Переводим указатель на начало файла fseek($fp, 0, SEEK_SET); // Подготавливаем переменные для подсчёта хитов и хостов $totalHostsTemp = 0; $todayHostsTemp = 0; $totalHitsTemp = 0; $todayHitsTemp = 0; $todayTemp = null; // Будем думать, что в файле первая строка содержит нужные данные $line = fgets($fp); // Пускай в первой строке содержатся: хосты, хиты, хосты за сегодня, // хиты за сегодня, дата записи if ( $line ) @list($totalHostsTemp, $totalHitsTemp, $todayHostsTemp, $todayHitsTemp, $todayTemp) = split(SPLITTER, $line); // Проверка даты if ( $todayTemp !== TODAY_DATE ) { // Дата в файле ститистики устарела, обнуляем сегодняшие хосты и хиты $todayHostsTemp = 0; $todayHitsTemp = 0; } // Прибавляем данные $totalHosts += $totalHostsTemp; $todayHosts += $todayHostsTemp; $totalHits += $totalHitsTemp; $todayHits += $todayHitsTemp; // Переводим указатель на начало файла fseek($fp, 0, SEEK_SET); // Урезаем файл до нулевой длины ftruncate($fp, 0); // Записываем данные - сначало хосты, хиты, хосты за сегодня, // хиты за сегодня, дата fputs($fp, join(SPLITTER, array($totalHosts, $totalHits, $todayHosts, $todayHits, TODAY_DATE))); // Снимаем блокировку, но можно и не снимать, если верить мануалу flock($fp, LOCK_UN); } // Обработка файла завершена, закрываем файловый указатель fclose($fp); } // Функция для создания картинки $createImageFunction = 'imagecreatefrom'. COUNTER_IMAGE_TYPE; $outputImageFunction = 'image' . COUNTER_IMAGE_TYPE; // Теперь необходимо вывести данные статистики if ( function_exists($createImageFunction) && function_exists('imagecreatetruecolor') ) { $yOffset = 2; // Если фоновая картинка есть, то используем её if ( !($im = $createImageFunction(COUNTER_IMAGE_FILE)) ) { // Если фоновой картинки нет, то создаём её $imageWidth = 88; $imageHeight = 31; $yOffset = 8; $im = imagecreatetruecolor($imageWidth, $imageHeight); $bgColor = imagecolorallocate($im, 220, 220, 220); $borderColor = imagecolorallocate($im, 120, 120, 120); // Рисуем фоновую картинку imagefill($im, 0, 0, $bgColor); imagerectangle($im, 0, 0, $imageWidth - 1, $imageHeight - 1, $borderColor); imageline($im, $imageWidth - 8, 0, $imageWidth, 8, $borderColor); imagefill($im, $imageWidth - 2, 2, $borderColor); } // Если изображение успешно создано, то выводим счётчик if ( $im ) { header('Content-Type: image/'. COUNTER_IMAGE_TYPE); // Устанавливаем цвет, шрифт и размер $fontColor = imagecolorallocate($im, 50, 50, 50); $fontFamily = 'Verdana'; $fontSize = '2'; imagestring($im, $fontSize, 8 , $yOffset, $totalHosts, $fontColor); imagestring($im, $fontSize, 55, $yOffset, '+'. $todayHosts, $fontColor); $outputImageFunction($im); imagedestroy($im); } } else { // Увы, функций для работы с картинками нет ?> Нет функций для работы с картинками <? } ?> |
Этот скрипт я назвал index.php и поместил в папку counter
.
Для большего удобства и эстетики я написал ещё небольшой mod_rewrite
движок, который позволит обращаться к счётчику как картинке.
Графический счётчик - .htaccess |
|
1 |
## ------------------------------------------------ ## Описание: Файл настроек счётчика ## Автор : zg (//anton-pribora.ru), 2008 год ## ------------------------------------------------ RewriteEngine On Options FollowSymlinks ## Текущее расположение данного файла относительно корня сайта #RewriteBase / ## Favicon RewriteCond %{REQUEST_URI} /cnt.gif$ RewriteRule .* index.php [L] |
Теперь можно обращаться к счётчику по ссылке counter/cnt.gif
.
Последним штрихом, я написал файл, который отображает краткую статистику счётчика в виде html-страницы.
Графический счётчик - stat.php |
|
1 |
<? /** * Файл для показа статистики счётчика * * @author zg * @package imageCounter */ // Выключаем вывод ошибок //error_reporting(0); // Запрет кэширования header('Expires: Mon, 11 Jul 1991 03:00:00 GMT'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); // Объявляем некоторые константы define('SPLITTER' , ' '); define('STAT_FILE' , 'stat.txt'); // Переменные статистики $totalHosts = 0; $totalHits = 0; $todayHosts = 0; $todayHits = 0; if ( file_exists(STAT_FILE) && ($fp = fopen(STAT_FILE, 'rb')) ) { // Файл статистики существует и доступен для чтения // Считываем первую строку $line = fgets($fp); fclose($fp); // Пускай в первой строке содержатся: хосты, хиты, // хосты за сегодня, хиты за сегодня, дата записи if ( $line ) @list($totalHosts, $totalHits, $todayHosts, $todayHits) = split(SPLITTER, $line); } ?> <html> <head> <title>Статистика посещений anton-pribora.ru</title> <style> body { font-family: Verdana; font-size: 0.8em; } table { font-size: 1em; } th { background-color: #ddd; font-weight: normal; } td.stat { border-bottom: 1px #aaa solid; border-right: 1px #aaa solid; } td.today { font-weight: bold; } </style> </head> <body> <table cellpadding="4" border="0" align="center"> <caption>Статистика посещений</caption> <colgroup> <col align="left" /> <col align="center" /> <col align="center" /> </colgroup> <tr> <td></td> <th>Cегодня</th> <th>Всего</th> </tr> <tr> <th>Посетителей</th> <td class="stat hosts today"><?=$todayHosts ? '+' : ''?><?=myNumeric($todayHosts)?></td> <td class="stat hosts total"><?=myNumeric($totalHosts)?></td> </tr> <tr> <th>Страниц</th> <td class="stat hits today"><?=$todayHits ? '+' : ''?><?=myNumeric($todayHits)?></td> <td class="stat hits total"><?=myNumeric($totalHits)?></td> </tr> </table> <center> <a href="javascript:window.close()">Закрыть окно</a> </center> </body> </html> <? function myNumeric($num) { return number_format($num, null, null, ' '); } ?> |
После размещения файлов на сервере, я вставил такой код в шаблон:
Графический счётчик - ссылка на статистику |
|
<a href="javascript:;" onclick="window.open('/counter/stat.php','stat','height=180,width=300').focus()" title="Статистика посещений"> <img border="0" src="/counter/cnt.gif" width="88" height="31" alt="ZG's counter" /></a> |
В результате получил счётчик, почти как у HotLog :-), а может и лучше, ведь он доступен всегда, когда доступен сайт!
Я привёл пример очень простой реализации графического счётчика. Без особых усилий к нему можно добавить более детальную статистку, которая бы могла рассчитывать не только суточные показатели, но и по месяцам. Это довольно легко реализуется на текстовых файлах, однако, если же вы захотите хранить полные списки ip адресов, информацию о браузерах, разрешение экрана и т.п., то одними текстовыми файлами тут не обойтись.