Рассылка закрыта
При закрытии подписчики были переданы в рассылку "Заметки Дизайнера" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Построй свой сайт на PHP!
Информационный Канал Subscribe.Ru |
Построй свой сайт на PHP! |
||
Здравствуйте, уважаемые читатели!
Перед вами восьмой выпуск нашей рассылки. Сегодня мы продолжим тему сбора статистики и вас ждет довольно много интересного кода. Тех, кто не читал первую часть, отсылаю к архиву рассылки. Из новостей хотелось бы отметить приведение в работоспособность модуля статей сайта phpdevelop.info, на данный момент там расположено три статьи. Постепенно, естетсвенно, список будет пополняться. Следующий выпуск будет также посвящен сттаистики и, надеюсь, им мы эту тему и закроем :-). Ну а сегодня - наслаждайтесь второй частьи статьи, а я наконец-то пойду отдыхать. :-) The Wanderer. phpdevelop.info. ICQ: 233661333 admin@never-invited.com
Откройте, перепись населения! Приготовьтесь к веселью. Сейчас мы будем собирать статистику и ни один пользователь не останется непосчитанным. Прежде всего, стоит определиться с реализацией. Я предлагаю класс, потому что... Ну, потому что мне просто нравится ООП. :-) Итак, сам класс и наш основной метод: class statistics{ var $dbHost = 'localhost'; var $dbUserName = 'root'; var $dbPassword = ''; var $dbName = 'statistics'; var $dbPrefix = 'stat'; var $res = NULL; function gather() { $ip = $this->getIP(); if(is_array($ip)) { list($proxy, $ip) = $ip; } else $proxy = ''; $referer = $this->getReferer(); if(is_array($referer)) { list($searchEngine, $searchQuery, $referer) = $referer; } else { $searchEngine = ''; $searchQuery = ''; } $browserSign = $this->parseBrowserSign(); list($os, $browser, $browserVer, $browserString) = $browserSign; $geoData = $this->getGeoData($ip); $country = $geoData[0]; $city = $geoData[1]; $page = addslashes(urldecode($_SERVER['REQUEST_URI'])); if($this->res=$res=mysql_connect($this->dbHost, $this->dbUserName, $this->dbPassword)) { if(mysql_select_db($this->dbName)) { $query = 'UPDATE '.$this->dbPrefix.'_count SET hits=hits+1'; if($this->isHost($ip)) { $host = 1; $query .= ', hosts=hosts+1'; if($this->isUser()) { $user = 1; $query .= ', users=user+1'; } else $user = 0; } else $host = 0; $query .= ' WHERE year='.date('Y').' AND month='.date('m').' AND day='.date('d').' AND hour='.date('H').''; mysql_query($query) or die(mysql_error($res)); if(mysql_affected_rows($res)<=0) { $query = "INSERT INTO ".$this->dbPrefix."_count values('1', '$host', '$user', '".date('Y')."', '".date('m')."', '".date('d')."', '".date('H')."')"; mysql_query($query) or die(mysql_error($res)); } $query = "INSERT INTO ".$this->dbPrefix."_data values('', '$ip', '$referer', '$proxy', '$page', '$searchEngine', '$searchQuery', '$os', '$browser', '$browserVer','$browserString', '$country', '$city', '".date('m')."','".date('d')."', '".date('H')."', ".date('Y').")"; mysql_query($query) or die(mysql_error($res)); } else die(mysql_error()); } } } Не могу cказать, что это в крайней степени эффективно и что это вообще эффективно, но это всего лишь пример, написанный за полчаса, так что на то, что у нас только в этом методе два запроса к БД можно закрыть глаза. :-) Комментировать тут особенно нечего: получаем данные, соединяемся с СУБД, выбираем нашу БД, делаем запросы, попутно проверяя не случилось ли у нас каких-то проблем и если что-то пошло не так, хватаемся за средце, говорим последние слова в виде строки с ошибкой MySQL и падаем. Впрочем, отдельного внимания заслуживает фрагмент, в котором мы производим второй запрос к БД. Да-да, вот это hits = hits+1 и т.д. Думаю, стоит пояснить, что это и почему. Прежде всего, мы увеличиваем число “хитов”. Происходит это всегда, так как хитом мы в данном случае считаем загрузку страницы сайта. Впрочем, возможно, вы не захотите считать хиты в пределах сайта, тогда достаточно просто сделать проверку на адрес вашего сайта в переменной $_SERVER['HTTP_REFERER']. К примеру, так: if(!stristr($_SERVER['HTTP_REFERER'], 'mysite.ru')){ //Увеличиваем hits } else { //Оставляем все как было. } Естественно, лучшей идеей было бы объявить какой-нибудь член класса, вроде $siteString и вместо 'mysite.ru' написать $this->siteString. Это очень поможет, если вы собираетесь использовать подобный класс не на одном, а на нескольких сайтах: все в одном месте, быстро заменили, что надо – и поехали. Далее мы вызываем функцию, проверяющую, есть новый хост или нет и в случае положительного ответа, увеличиваем значение соответсвующего поля. Хостом считается уникальный ip-адрес, запросивший одну из страниц вашего сайта. И, наконец, проверяем, является ли пользователь новым пользователем и, если необходимо, увеличиваем значение соответствующего поля. О том, что понимается под новым пользователем, я расскажу чуть позже. Теперь давайте создадим таблицы, где будут храниться все наши данные. Итак: CREATE TABLE stat_data ( id int(11) NOT NULL auto_increment, ip varchar(15) NOT NULL default '', referer varchar(255) NOT NULL default '', proxy varchar(15) NOT NULL default '', page varchar(255) NOT NULL default '', searchEngine varchar(30) NOT NULL default '', searchQuery varchar(255) NOT NULL default '', os varchar(10) NOT NULL default '', browser varchar(15) NOT NULL default '', browserVer varchar(10) NOT NULL default '', browserString varchar(255) NOT NULL default '', country varchar(20) NOT NULL default '', city varchar(20) NOT NULL default '', month char(2) NOT NULL default '', day char(2) NOT NULL default '', hour char(2) NOT NULL default '', year year(4) NOT NULL default '0000', PRIMARY KEY (id) ) CREATE TABLE stat_count ( hits int(11) NOT NULL default '0', hosts int(11) NOT NULL default '0', users int(11) NOT NULL default '0', year year(4) NOT NULL default '0000', month char(2) NOT NULL default '', day char(2) NOT NULL default '', hour char(2) NOT NULL default '' ) Таблицы созданы, теперь давайте перейдет к нашему следующему методу – getIP(). Здесь все просто: function getIP(){ if(key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) { $ip[] = $_SERVER['REMOTE_ADDR']; $ip[] = $_SERVER['HTTP_X_FORWARDED_FOR']; } elseif($_SERVER['REMOTE_ADDR']) $ip = $_SERVER['REMOTE_ADDR']; else $ip = 'unknown'; return $ip; } Мы проверяем, использует ли пользователь неанонимный прокси. Если использует, мы узнаем его реальный ip-адрес и возвращаем его и адрес прокси в массиве. Иначе, мы возвращаем просто ip-адрес. Ну а если ip-адреса вдруг не окажется (что, в принципе, довольно невероятно), возвращается строка unknown. При использовании анонимного прокси, мы бессильны определить реальный адрес. Ну и ладно :-) function getReferer(){ if(key_exists('HTTP_REFERER', $_SERVER)) { switch(TRUE) { case(stristr($_SERVER['HTTP_REFERER'], 'google.')): preg_match("#q=(.*?)($|&)#is", $_SERVER['HTTP_REFERER'], $match); $engine = 'Google'; $query=$this->utf8win1251(urldecode($match[1])); return array($engine, $query, $_SERVER['HTTP_REFERER']); case(stristr($_SERVER['HTTP_REFERER'], 'msn.')): preg_match("#q=(.*?)($|&)#is", $_SERVER['HTTP_REFERER'], $match); $engine = 'MSN'; $query=$this->utf8win1251(urldecode($match[1])); return array($engine, $query, $_SERVER['HTTP_REFERER']); case(stristr($_SERVER['HTTP_REFERER'], 'altavista.')): preg_match("#q=(.*?)($|&)#is", $_SERVER['HTTP_REFERER'], $match); $engine = 'AltaVista'; $query=$this->utf8win1251(urldecode($match[1])); return array($engine, $query, $_SERVER['HTTP_REFERER']); case(stristr($_SERVER['HTTP_REFERER'], 'yahoo.')): preg_match("#p=(.*?)($|&)#is", $_SERVER['HTTP_REFERER'], $match); $engine = 'Yahoo'; $query=$this->utf8win1251(urldecode($match[1])); return array($engine, $query, $_SERVER['HTTP_REFERER']); case(stristr($_SERVER['HTTP_REFERER'], 'lycos.')): preg_match("#query=(.*?)($|&)#is", $_SERVER['HTTP_REFERER'], $match); $engine = 'Lycos'; $query=urldecode($match[1]); return array($engine, $query, $_SERVER['HTTP_REFERER']); case(stristr($_SERVER['HTTP_REFERER'], 'yandex.')): preg_match('#text=(.*?)($|&)#is', $_SERVER['HTTP_REFERER'], $match); $engine = 'Yandex'; if(@!$match[1]) { preg_match('#qs=(.*?)($|&)#is', $_SERVER['HTTP_REFERER'], $match); $query = convert_cyr_string(urldecode($match[1]), 'k', 'w'); } else $query = urldecode($match[1]); return array($engine, $query, $_SERVER['HTTP_REFERER']); case(stristr($_SERVER['HTTP_REFERER'], 'go.mail.ru')): $engine = 'Mail.ru'; preg_match('#\&(q|words)=(.*?)($|&)#is', $_SERVER['HTTP_REFERER'], $match); $query = urldecode($match[1]); return array($engine, $query, $_SERVER['HTTP_REFERER']); case(stristr($_SERVER['HTTP_REFERER'], 'rambler.')): $engine = 'Rambler'; preg_match('#words=(.*?)($|&)#is', $_SERVER['HTTP_REFERER'], $matches); $query = urldecode($match[1]); return array($engine, $query, $_SERVER['HTTP_REFERER']); case(stristr($_SERVER['HTTP_REFERER'], 'aport')): $engine = 'Aport'; preg_match('#(r|FindRequest)=(.*?)($|&)#is', $_SERVER['HTTP_REFERER'], $matches); $query = urldecode($match[1]); return array($engine, $query, $_SERVER['HTTP_REFERER']); default: return $_SERVER['HTTP_REFERER']; } } else return ''; } Вот тут-то и начинается все веселье. Мы проверяем поле HTTP_REFERER на предмет выявления всех известных скрипту поисковиков. В данный момент, как можно заметить, функция распознает запросы с Google, MSN, AltaVista, Yahoo, Lycos, Yandex, Mail.ru, Rambler и Aport. Делается это с помощью регулярных выражений, которые я не вижу смысла объяснять: гораздо проще найти какой-нибудь справочник и разобраться с ними раз и навсегда. Я же хочу обратить ваше внимание на перекодировку запросов. Дело в том, что западные поисковики передают запросы в большинстве своем в UTF8. Так как стандартная функция utf8_decode() не работает с русскими буквами, пришлось искать функцию конвертации самостоятельно. Нашлась она, естественно, в комментариях пользователей к руководству по PHP (собственно, оттуда я и начал поиски). function utf8win1251($s){ $out=""; $c1=""; $byte2=false; for ($c=0; $c<strlen($s); $c++){ $i=ord($s[$c]); if ($i<=127) $out.=$s[$c]; if ($byte2) { $new_c2=($c1&3)*64+($i&63); $new_c1=($c1>>2)&5; $new_i=$new_c1*256+$new_c2; if ($new_i==1025) $out_i=168; else if ($new_i==1105) $out_i=184; else $out_i=$new_i-848; $out.=chr($out_i); $byte2=false; } if (($i>>5)==6) {$c1=$i;$byte2=true;} } return $out; } Мораль: комментарии к руководству – крайне полезная вещь. C русскими поисковиками все обстоит другим образом. Из них только Yandex отличается тем, что по непонятным причинам передает запрос в koi8-r. Впрочем, лезть в комментарии к руководству не пришлось, ибо существует прекрасная функция convert_cyr_string(), которой мы и воспользовались. Ниже расположена функция parseBrowserSign(). Комментировать, здесь, думаю, нечего: function parseBrowserSign(){ $os = $this->getOS(); $browser = $this->getBrowser(); return array($os, $browser[0], $browser[1], $_SERVER['HTTP_USER_AGENT']); } Следующие функции очень похожи на функцию определения переходов с поисковиков. Первая опреляет операционную систему пользователя, а вторая – браузер и его (браузера) версию. Чтобы избежать лишних вопросов, скажу, что я не тестировал работоспособность для абсолютно всех ОС и браузеров, которые поддерживаются данными функциями, поэтому некоторые regexp'ы строились лишь на теоретических знаниях. Так что если вы вдруг используете Safari как ваш веб-браузер или BeOS как ОС, и что-то не работает, отправьте мне письмецо. function getOS(){ switch(TRUE) { case(preg_match("#wi(n|ndows)[ \-]?nt[ /]?5\.1#is", $_SERVER['HTTP_USER_AGENT']) || stristr($_SERVER['HTTP_USER_AGENT'], 'Windows XP')): return 'Windows XP'; case(preg_match("#wi(n|ndows)[ \-]?(2000|nt[ /]?5\.0)#is", $_SERVER['HTTP_USER_AGENT'])): return 'Windows 2000'; case(preg_match("#wi(n|ndows)[ \-]?me#is", $_SERVER['HTTP_USER_AGENT']) || stristr($_SERVER['HTTP_USER_AGENT'], 'win 9x 4.90')): return 'Windows ME'; case(preg_match("#linux[ /\-]([a-z0-9._]{1,10})#is", $_SERVER['HTTP_USER_AGENT']) || stristr($_SERVER['HTTP_USER_AGENT'],'linux')): return 'Linux'; case(preg_match("#Mac[ ]?OS[ ]?X#is", $_SERVER['HTTP_USER_AGENT'])): return 'MacOS X'; case(preg_match("#wi(n|ndows)[ \-]?95#is", $_SERVER['HTTP_USER_AGENT'])): return 'Windows 95'; case(preg_match("#free[ \-]?bsd[ /]([a-z0-9._]{1,10})#is", $_SERVER['HTTP_USER_AGENT'])||preg_match('free[ \-]?bsd', $_SERVER['HTTP_USER_AGENT'])): return 'FreeBSD'; case(preg_match("#beos[ a-z]*([0-9.]{1,10})#is", $_SERVER['HTTP_USER_AGENT'])): return 'BeOS'; case(preg_match("#Darwin[ ]?([0-9.]{1,10})#is", $_SERVER['HTTP_USER_AGENT'])): return 'Darwin'; case(preg_match("#net[ \-]?bsd#is", $_SERVER['HTTP_USER_AGENT'])): return 'NetBSD'; case(preg_match("#wi(n|ndows)[ \-]?nt[ /]?([0-4][0-9.]{1,10})#is", $_SERVER['HTTP_USER_AGENT'])||preg_match('wi(n|ndows)[ \-]?nt', $_SERVER['HTTP_USER_AGENT'])): return 'Windows NT'; case(preg_match("#wi(n|ndows)[ \-]?98#is", $_SERVER['HTTP_USER_AGENT'])): return 'Windows 98'; case(preg_match("#wi(n|n32|ndows)#is", $_SERVER['HTTP_USER_AGENT'])): return 'Windows'; default: return 'Other'; } } function getBrowser() { switch(TRUE) { case(preg_match("#\(compatible; MSIE[ /]([0-9.]{1,10})#is", $_SERVER['HTTP_USER_AGENT'], $matches)): return array('Internet Explorer', $matches[1]); case(preg_match("#Firefox/([0-9.+]{1,10})#is", $_SERVER['HTTP_USER_AGENT'], $matches)): return array('Firefox', $matches[1]); case(preg_match("#opera[ /]([0-9.]{1,10})#is", $_SERVER['HTTP_USER_AGENT'], $matches)): return array('Opera', $matches[1]); case(preg_match("#netscape[0-9]?/([0-9.]{1,10})#is", $_SERVER['HTTP_USER_AGENT'], $matches)||preg_match('^mozilla/([0-4]\.[0-9.]{1,10})', $_SERVER['HTTP_USER_AGENT'], $matches)): return array('Netscape Navigator', $matches[1]); case(preg_match("#^mozilla/[5-9]\.[0-9.]{1,10}.+rv:([0-9a-z.+]{1,10})#is", $_SERVER['HTTP_USER_AGENT'], $matches)||preg_match('^mozilla/([5-9]\.[0-9a-z.]{1,10})', $_SERVER['HTTP_USER_AGENT'], $matches)): return array('Mozilla', $matches[1]); case(preg_match("#konqueror/([0-9.]{1,10})#is", $_SERVER['HTTP_USER_AGENT'], $matches)): return array('Konqueror', $matches[1]); case(preg_match("#safari/([0-9.]{1,10})#is", $_SERVER['HTTP_USER_AGENT'], $matches)): return array('Safari', $matches[1]); case(preg_match("#galeon/([0-9.]{1,10})#is", $_SERVER['HTTP_USER_AGENT'], $matches)): return array('Galeon', $matches[1]); case(preg_match("#lynx/([0-9a-z.]{1,10})#is", $_SERVER['HTTP_USER_AGENT'], $matches)): return array('Lynx', $matches[1]); case(preg_match("#Links[ /]\(([0-9.]{1,10})#is", $_SERVER['HTTP_USER_AGENT'], $matches)): return array('Links', $matches[1]); default: return array('unknown', ''); } } Функция getGeoData() получает через API системы GeoIP ифнормацию о стране и городе посетителя. Данный код ориентирован на то, что файлы обертки для работы с базой и сама база находятся в той же директории, что и наш скрипт. Впрочем, рекомендуется использовать полный путь, так как если вы разместите класс сбора статистики в какой-либо поддиректории сайта, а потом включите его с помощью include() этот код работать не будет. Почетную возможность доработки я оставляю вам :-) function getGeoData($ip){ include("geoipcity.inc"); $gi = geoip_open("GeoIPCity.dat",GEOIP_STANDARD); $record = geoip_record_by_addr($gi,$ip); return array($record->country_name, $record->city); } function isUser() { if(key_exists('w_visited', $_COOKIE) && $_COOKIE['w_visited']==true) { return false; } else { if(12 == $m = date('n') & 31 == $d = date('j')) { $y = date('Y') + 1; $m = 1; $d = 1; } else if(date($t)==$d) { $y = date('Y'); $m = date('n')+1; $d = 1; } else { $y = date('Y'); $m = date('n'); $d = date('j')+1; } setcookie('w_visited', true, mktime(0, 0, 0, $m, $d, $y)); return true; } } Итак, задача этой функции состоит в определении является ли посетитель новым пользователем. Для этого ему ставиться cookie, который уничтожается в конце текущего дня (точнее, в начале следующего дня). Точно таким же образом можно сделать функцию определения количества новых пользователей сайта, то есть тех, кто зашел на сайт (условно) впервые. В этом случае нужно просто увеличить время жизни cookie до, скажем, года. Естественно, точность здесь весьма условная: cookies могут быть запрещены. Итак, что мы делаем. Мы проверяем наличие cookie w_visited, в случае обнаружения сразу возвращаем false (то есть это не новый пользователь). Если же такого cookie нет, то мы сначала проделываем хитроумную проверку, не 31 ли декабря сегодня и не последний ли сегодня день месяца. Соответствующим образом генерируется время уничтожения cookie, устанавливается он сам и возвращается true. function isHost($ip){ $query = "SELECT id FROM ".$this->dbPrefix."_data WHERE year='".date('Y')."' AND month='".date('m')."' AND day='".date('d')."' AND ip='".$ip."'"; if(!$this->res) { $res = mysql_connect($this->dbHost, $this->dbUserName, $this->dbPassword) or die(mysql_error()); mysql_select_db($this->dbName, $res); } else $res = $this->res; $result=mysql_query($query, $res) or die(mysql_error($res)); if(0==mysql_num_rows($result)) { return true; } else return false; } Эта последняя функция очень проста: мы проверяем, посещал ли нас сегодня пользователь с заданным ip, и возвращаем необходимое значение. |
Copyright © 2004-2005 Построй свой сайт на PHP! Перепечатка возможна только с сохранением авторства. |
Выпуск #8: 2005-04-27 |
http://subscribe.ru/
http://subscribe.ru/feedback/ |
Подписан адрес: Код этой рассылки: comp.soft.prog.phpdev |
Отписаться |
В избранное | ||