Для разработки счетчика я использовал интерпретатор PHP4, который можно совершенно бесплатно скачать с официального сайта PHP - http://www.php.net.
Часть 1. Простой счетчик
Сейчас попробуем разработать простой счетчик, который будет подсчитывать посещения только одного (нашего) сайта. Определим требования к нашему счетчику:
1. он должен подсчитывать только уникальных посетителей
2. счетчик не должен реагировать на нажатие Reload в броузере
3. счетчик не должен вести учет наших посещений (с локального хоста)
Считать уникальных посетителей можно по-разному. Идентификация узлов в Internet осуществляется с помощью IP-адересов. Первое, что приходит в голову: записывать все уникальные IP-адреса в какой-нибудь файл (или базу данных). Но такой метод нам совершенно не подходит. Во-первых, IP-адрес может быть динамический и пользователь может зайти на сайт во второй раз, но уже используя другой адрес. В этом случае не выполняется первое требование, предъявленное к нашему счетчику: уникальность посетителя. Во-вторых, в случае с файлом поиск в файле является довольно медленной операцией, не говоря уже об операции записи в файл с использованием блокировки (flock()), особенно учитывая, что к нашему сайту может в одно время обращаться несколько пользователей. К тому же рано или поздно, размер нашего файла станет неприлично большим. В случае с базой данной этот счетчик уже не будет простым. Я должен все-таки, заметить, что сервер баз данных MySQL, который мы также будем использовать в этой статье, довольно быстро обрабатывает запросы и его целесообразно использовать даже при большой нагрузке на сервер. Но это только в случае со счетчиком или другой системой статистики! На мой взгляд, MySQL не нужно использовать, если вы собираетесь разработать какой-нибудь серьезный проект.
Нам остается использовать только Cookies. Алгоритм работы счетчика такой: когда пользователь заходит на ваш сайт, сценарий проверяет, установлены ли определенные (наши) Cookies. Если нет, то посетитель считается уникальным и счетчик увеличивается, в противном случае возвращается предыдущее значение (то которое было уже когда-то записано в файл). Естественно, после инкремента счетчика нужно установить Cookies, "пометив" этого пользователя - он уже был на сайте. Счетчик действительно оправдывает свое название - простой: всего 17 строк кода (см. листинг 1).
Листинг 1. Простой счетчик
1. <?
2. $f=fopen("counter.dat","a+") or die("Cannot to open file");
3. flock($f,2);
4. $count=fread($f,100);
5. if(!IsSet($Was))
6. {
7. $Was=1;
8. SetCookie("Was",$Was,0x7FFFFFFF);
// Не засчитываем посещения со своего узла
9. $REMOTE_HOST=gethostbyaddr($REMOTE_ADDR);
10. if(!($HTTP_HOST===$REMOTE_HOST)) @$count=$count+1;
11. ftruncate($f,0);
12. fwrite($f,$count);
13. }
14. flock($f,3);
15. fclose($f);
16. echo $count;
17. ?>
Теперь немного опишу принцип работы счетчика. В строке 2 открываем файл counter.dat. Причем используется режим a+. В этом режиме файл открывается как для чтения, так для записи. Указатель файла устанавливается на его конец. В отличие от режима "а", в режиме "а+", если файл не существует, он создается. Затем мы блокируем файл (стр. 3). Последний параметр функции flock() определяет вид блокировки. В данном случае - это режим 2 (LOCK_EX -исключительная блокировка): никто, кроме нас, не может производить с файлом никаких операций. Если еще какому-нибудь процессу нужно прочитать информацию из файла или записать что-нибудь в него, он вынужден будет подождать, пока мы завершим работу с файлом.
Затем мы читаем текущее значение из файла в переменную count. Потом мы проверяем, установлен ли Cookie Was. Для этого нам всего лишь нужно проверить существование переменной $Was с помощью функции IsSet(). Если Cookie Was не установлен (строки 6-13), мы его устанавливаем. Функция SetCookie устанавливает Cookie Was и записывает в него значение одноименной переменной $Was. Третий параметр функции - это время существования Cookie (TTL) - на нашу жизнь хватит 6 :-).
В строке 9 мы доменное имя клиента с помощью переменной окружения $REMOTE_ADDR. Если значение, возвращенное функцией gethostbyaddr($REMOTE_ADDR), совпадает со значением переменной окружения $HTTP_HOST, то сценарий запущен с локального хоста и мы не защитываем это посещение. В принципе, проверку на запуск с локального хоста можно было бы и не производить, так как все равно устанавливаются Cookies и не увеличите значение счетчика более чем на единицу. Без зазрений совести строку 10 можно заменить на $count=$count+1 (или на $count++). Всю эту проверку я описал только ради демонстрации полезной функции gethostbyaddr() и не менее полезных переменных окружения.
В строке 12 мы записываем новое значение переменной $count, даже если оно не изменилось. Вы можете добавить какой-нибудь флаг изменения переменной $count и записывать новое значение в зависимости от состояния этого флага, например, 0 - изменений не было и, следовательно, записывать нечего, 1 - нужно обновить данные. Это довольно не сложно и я оставляю вам это в качестве домашнего задания...
Попробуйте запустить этой счетчик. Если вы запускаете его для отладки на своей домашней машине, закомментируйте строку 10 и замените ее на строку @$count=$count+1;
Это нужно сделать, потому что, как я уже замечал, сценарий не учитывает посещения с локального хоста. При первом запуске вы должны увидеть на экране 1, при повторном посещении или нажатии на кнопку Reload (в т.ч. и на Shift+Reload) значение счетчика не изменится. Если вы используете Netscape 6, для просмотра установленных Cookies выберите команду меню Edit, Preferences, а потом Advanced, Cookies.
К недостаткам нашего счетчика нужно отнести то, что если на сайт заходит пользователь с отключенными Cookies, счетчик воспринимает его каждый раз как уникального посетителя, даже если он уже был на сайте. И это логично - Cookies ведь отключены.
Теперь можно немного и усложнить наш счетчик. Дело в том, что текстовые счетчики практически никто не использует - в основном выводится какое-нибудь графическое изображение. Вот сейчас и займемся созданием графического счетчика. Это увеличит объем исходного текста счетчика всего лишь на 7 строк. Вместо echo $count, то есть вывода результата (см. листинг 1), мы выведем рисунок:
16. $img = ImageCreateFromPng("images/template.png");
17. $color = ImageColorAllocate($img, 220, 210, 60);
18. $x = (ImageSx($img)-6.5*strlen("$count"))/2;
19. ImageString($img,3,$x,9,"Visits: $count",$color);
20. Header("Content-type: image/png");
21. ImagePng($img);
22. ImageDestroy($img);
23. ?>
Как видите, все очень просто. Строка 16 создает рисунок из файла images/template.png (должен существовать). То есть функция ImageCreateFromPng загружает фон для нашего счетчика. Потом (строка 17) мы определяем цвет букв - в данном случае должен получиться желтый (оранжевый) цвет. В строке 19 выводим в рисунок строку "Visits: число". Строка будет расположена под наклоном. В строке 20 происходит самое главное: мы передаем броузеру тип рисунка - PNG. Потом сохраняем рисунок в стандартный вывод - в броузер (строка 22). Строка 23 освобождает память.
Использовать этот счетчик можно так: <img src="counter.php">
Вы пытаетесь выполнить данный пример, и получатете сообщение об ошибке... Примерно такое: Fatal error: Call to undefined function: imagecreatefrompng() in ...
Оказывается, как всегда, есть одно НО. Для работы с графикой у вас должна быть установлена библиотека GD. Если вы работаете под Linux, вы должны установить PHP с поддержкой библиотеки GD. Даже если у вас эта библиотека и установлена, вы должны пересобрать PHP для поддержки библиотеки GD. При работе под Windows убедитесь, что у вас есть файл php_gd.dll. Если вы все настроили и протестировали сценарий на своей домашней машине, то это не значит, что он будет работать у вашего хостинг-провайдера. Некоторые хостинг-провайдеры не включают (или забывают включить) поддержку библиотеки GD.
Это еще пол беды. При работе с PHP (и включенной поддержкой GD) у вас просто может не быть функции ImageCreateFromPng(). В этом случае попробуйте функции ImageCreateFromJpeg() или ImageCreateFromGif(). Естественно, в зависимости от типа изображения, нужно корректно установить Content-type (строка 20) и использовать нужную функцию в строке 21 (ImageJpeg() - для Jpeg или ImageGif() - для Gif).
Часть 2. Протоколирование посещений.
Допустим, мы хотим знать, кто и когда посещал наш сайт, а также броузер пользователя. Потом эти данные можно будет использовать для фомирования статистики посещений. Для этого мы будем использовать сервер баз данных MySQL.
Здесь можно пойти двумя путями: по пути наименьшего сопротивления и по немного аскетичному пути. Первый заключается в том, что, не усложняя себе жизнь, мы будем записывать в базу данных всех подряд - даже если пользователь уже был на сайте. Второй путь заключается, что мы будем вносить в базу только уникальных пользователей. Как вы догадались, мы пойдем по второму пути. Вариантов решения данной проблемы множество. Вы можете использовать предложенный мой метод, а можете написать свой.
Так как счетчик уже считает уникальных посетителей, то можно было бы просто добавить один запрос SQL, который бы вносил в базу нужную нам информацию. Но этот способ я считаю некорректным. Во-первых, мы будем использовать таблицу с индексами (такой подход в дальнейшем ускорит операции описка по таблице), а пользователь может отключить Cookies или попросту удалить старые Cookies и он уже не будет уникальным. Таким образом, мы попытаемся внести еще одну запись, которая уже есть в таблице, выражаясь языком BDE, мы получим Key Violation, то есть нарушение индекса. Во-вторых, даже если мы будем проверять, есть ли пользователь в базе, мы получим довольно большую погрешность в подсчете уникальных посетителей: по статистике около 30% пользователей отключают Cookies.
Немного непонятно? Я так, и знал. Сейчас все станет на свои места. Мы будем использовать таблицу с такими полями: IP char(15), vdate char(8), browser char (127)
Поле IP (IP-адрес клиента), естественно должно быть уникальным, то есть по нему будем строить индекс. Поле vdate - дата визита (формат mm.dd.yy), поле browser - наименование клиента броузера. 127 символом для этого поля, конечно же много, но а вдруг Microsoft выпустит IE7 включив в его описание имена всех разработчиков
![ab.gif](https://www.shram.kiev.ua/forum/style_emoticons/default/ab.gif)
Базу данных мы создавать не будем (у нас ведь только одна таблица), а воспользуемся уже существующей стандартной базой test. Сейчас нужно создать таблицу. Для этого запустите программу mysql test
Под Linux вам, скорее всего, нужно будет использовать пароль: mysql test -p Введите такой запрос (вы должны иметь право на создание таблицы):
create table stat(ip char(15) primary key, vdate char(8), browser char(127));
После того, как мы создали таблицу нужной структуры, приступим к написанию самого сценария. Данный код нужно вставить после установки Cookies (то есть после строки 8, листинг 1).
9. <?
// подключаемся к базе данных
10. mysql_connect("localhost");
11. mysql_select_db("test");
// считаем, что хост - уникальный
12. $Unique = 1;
13. $R=@mysql_query("SELECT * FROM stat") or die("Invalid SQL query");
14. while($Rw=mysql_fetch_row($R))
15. { if($Rw[0]===$REMOTE_ADDR)
16. { $Unique = 0; break(1); }
17. }
// если хост уникальный...
18. if($Unique)
19. {
20. $vdate=date("d.m.y");
21. @mysql_query("INSERT INTO stat VALUES('$REMOTE_ADDR','$vdate','$HTTP_USER_AGENT')");
22. }
23. else
24. {
25. $vdate=date("d.m.y");
26. mysql_query("UPDATE stat SET vdate='$vdate', browser='$HTTP_USER_AGENT' WHERE ip='$REMOTE_ADDR'");
27. }
28. ?>
В строке 10 происходит подключение к базе данных test, в которой мы создали таблицу stat. Мы подключаемся, используя имя пользователя по умолчанию. Скорее всего, на вашей домашней машине такой способ будет работать. А вот в случае с хостинг-провайдером вам так просто не подключиться. Для этого нужно использовать еще два дополнительных параметра: имя пользователя и пароль. Имя пользователя и пароль вы можете уточнить у вашего хостинг-провайдера, возможно оно будет совпадать с вашим логином при входе в систему:
mysql_connect("localhost","user","password");
В вышеприведенном примере я подключаюсь как пользователь user с паролем password. Еще раз напомню, что вы должны иметь право на создание таблицы (не говоря уже о целой базе данных - именно поэтому я предпочел использовать базу данных test).
Теперь рассмотрим алгоритм работы сценария. После подключения мы считаем, что хост уникальный. И это правильно, так как наши Cookie не установлены на машине клиента. Мы могли бы, как я уже писал, просто занести хост в базу данных, но пользователь может и отключить Cookies. Для этого нужно проверить есть ли он в нашей базе. Этим занимаются строки сценария 13-17: сначала мы получаем все уникальные хосты, которые есть в базе (строка 13), потом обрабатываем их (строки 14-17). Если хост не уникальный, мы сбрасываем флаг уникальности (Unique) и прерываем цикл. Если хост уникальный, мы вносим данные в базу, в противном случае мы обновляем информацию о последнем визите и броузере пользователя.
Теперь приведу полный исходный текст нашего, уже не простого, счетчика. Нужно сделать небольшое замечание: после того, как мы добавили операции с базой данных, потребность в строке 10 листинга 1 просто отпадает. Вместо нее строку $count=$count+1 нужно добавить в блок обработки уникального хоста (после одной из строк 19-21).
Полный исходный текст счетчика:
1. <?
2. $f=fopen("counter.dat","a+") or die("Cannot to open file");
3. flock($f,2);
4. $count=fread($f,100);
5. if(!IsSet($Was))
6. {
7. $Was=1;
8. SetCookie("Was",$Was,0x7FFFFFFF);
// подключаемся к базе данных
10. mysql_connect("localhost");
11. mysql_select_db("test");
// считаем, что хост - уникальный
12. $Unique = 1;
13. $R=@mysql_query("SELECT * FROM stat") or die("Invalid SQL query");
14. while($Rw=mysql_fetch_row($R))
15. { if($Rw[0]===$REMOTE_ADDR)
16. { $Unique = 0; break(1); }
17. }
// если хост уникальный...
18. if($Unique)
19. {
20. $vdate=date("d.m.y"); $count=$count+1;
21. @mysql_query("INSERT INTO stat VALUES('$REMOTE_ADDR','$vdate','$HTTP_USER_AGENT')");
22. }
23. else
24. {
25. $vdate=date("d.m.y");
26. mysql_query("UPDATE stat SET vdate='$vdate', browser='$HTTP_USER_AGENT' WHERE ip='$REMOTE_ADDR'");
27. }
28. ftruncate($f,0);
29. fwrite($f,$count);
30. }
31. flock($f,3);
32. fclose($f);
33. echo $count;
34. ?>
И напоследок одно важное замечание: наверняка вам захочется вынести подключение к серверу MySQL в начало сценария. Этого не следует делать! Строки 10 и 11 могут породить какой-нибудь текст в случае ошибки подключения и тогда мы не сможем установить Cookies. Информация о Cookies должна содержатся в заголовке, а если мы отправим какой-нибудь текст в броузер, то уже не сможем отправить продолжение заголовка, содержащего информацию о Cookies. В случае ошибки мы хотя бы успеем установить Cookies (если они включены)