Перейти к содержимому

Фотография

Счетчик посещений (создания)

- - - - -

  • Авторизуйтесь для ответа в теме

#1
Joiner

Отправлено 17 ��� 2009 - 04:25

Joiner

    Новоприбывший

  • Пользователи
  • 18 сообщений
В данной статье будет рассмотрено создание самого обыкновенного счетчика посещений. Статья рассчитана на начинающих Web-программистов, поэтому в ней некоторые функции PHP описаны довольно подробно. Если вы знаете PHP, не теряйте время на чтение этой статьи. Предлагаемое мною решение никак не претендует на универсальность, но наша цель сначала разработать простой, но "правильный" счетчик, то есть такой, который не реагировал бы на Reload. Позже (в этой же статье) так же будет рассмотрен более сложный счетчик с протоколированием. К сожалению, объем статьи не позволит привести код для подсчета посещений для разных сайтов. Возможно, эту тему я рассмотрю в другой статье (точнее, в продолжении этой статьи).
Для разработки счетчика я использовал интерпретатор 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 освобождает память.

Использовать этот счетчик можно так: &ltimg 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.

Базу данных мы создавать не будем (у нас ведь только одна таблица), а воспользуемся уже существующей стандартной базой 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 (если они включены)