"Кто онлайн?" виджет с помощью PHP, MySQL и jQuery


В этом уроке мы сделаем полезный виджет, который будет отслеживать сколько посетителей в данный момент находится на сайте. Более он также будет способен определять страну, возраст, и заработную плату (шутка) посетителя. Страна будет определяться с помощью бесплатного сервиса http://www.hostip.info/. Если быть более точным, то с помощью АПИ сервиса. Шаг 1 - XHTML Как всегда мы начинаем с фундамента. Кода совсем немного. Основная работа будет вестись над серверной частью. Наш виджет будет состоять из красивой кнопки с отображением количества человек, при нажатии на которую всплывает более подробная таблица с флагами стран. demo.html <div class="onlineWidget"> <div class="panel"> <!-- Спарсено с помощью AJAX: --> <div class="geoRow"> <div class="flag"><img src="who-is-online/img/famfamfam-countryflags/us.gif" width="16" height="11"></div> <div class="country" title="UNITED STATES">UNITED STATES</div> <div class="people">2</div> </div> <div class="geoRow"> <div class="flag"><img src="who-is-online/img/famfamfam-countryflags/uk.gif" width="16" height="11"></div> <div class="country" title="UNITED KINGDOM">UNITED KINGDOM</div> <div class="people">1</div> </div> </div> <div class="count">8</div> <div class="label">online</div> <div class="arrow"></div> </div> Как Вы можете заметить из разметки выше, главный контейнер - "onlineWidget" содержит выезжающую панель (div с классом "panel"), общее количество посетителей онлайн (div "count"), ярлык "online" и зеленую стрелку справа. div выезжающей панели наполняется динамически с помощью AJAX. Содержание этой панели по умолчанию - вращающийся файл gif, который вскоре заменяется гео информацией после обработки AJAX запроса. К этому мы еще вернемся. Шаг 2 - База данных Вся информация виджета хранится в таблице tz_who_is_online. Она состоит из шести полей (или колонок). Первое - ID (первичный ключ + автодобавление), второе - IP (сохраняет IP адрес, который преобразован в целое число с помощью функции ip2long). Далее следуют три поля, которые берутся из Hostip API - Country, CountryCode и City. Виджет на данный момент не использует поле CITY (город), но на случай если кто-то захочет доработать его - оно пригодится. Последнее поле - DT - временной штамп, который обновляется при каждой загрузки страницы и позволяет определить кто онлайн (пользователи, которые не обновляют страницу на протяжении 10 минут скорее всего уже покинули сайт). Шаг 3 - CSS Виджет практически не использует картинок. Давайте взглянем на стили: who-is-online/styles.css - часть 1 .onlineWidget,.panel{ background-color:#F9F9F9; border:2px solid #FFFFFF; height:25px; padding:4px 8px; position:relative; width:130px; cursor:pointer; /* CSS3 правила для закругленных углов и теней: */ -moz-border-radius:6px; -webkit-border-radius:6px; border-radius:6px; -moz-box-shadow:0 0 3px #CCCCCC; -webkit-box-shadow:0 0 3px #CCCCCC; box-shadow:0 0 3px #CCCCCC; text-shadow:0 2px 0 white; } .onlineWidget:hover{ background-color:#fcfcfc; } .onlineWidget:hover .arrow{ /* Меняем фоновое изображение для зеленой стрелки при наведении: */ background-position:bottom center; } .count{ /* Общее количество человек онлайн - div */ color:#777777; float:left; font-size:26px; font-weight:bold; margin-top:-3px; text-align:center; width:30px; } .label{ /* Ярлык - online */ float:left; font-size:10px; padding:7px 0 0 7px; text-transform:uppercase; } В первом шаге выше Вы можете увидеть как мы придаем стили нашему виджету и выезжающей панели единовременно. Это для того, чтобы мы были уверены, что у них единый стиль, который в последствии легко можно будет поменять по желанию. who-is-online/styles.css - часть 2 .arrow{ /* Зеленая стрелочка справа */ background:url(img/arrow.png) no-repeat top center; position:absolute; right:6px; width:25px; height:25px; } .panel{ /* Выезжающая панель */ position:absolute; cursor:default; bottom:50px; left:0; height:auto; display:none; margin:-2px; z-index:1000; } .preloader{ /* Вращающийся прелоадер gif */ display:block; margin:10px auto; } .geoRow{ /* div, который содержит каждую страну */ height:16px; overflow:hidden; padding:2px 0; } .flag{ float:left; margin:0 4px; } .country, .people{ float:left; font-size:10px; padding:2px; } .country{ width:85px; overflow:hidden; } .people{ font-weight:bold; } Во второй части стилей мы описываем то, как должна выглядеть информации после получения ее от AJAX запросов. Шаг 4 - PHP Именно здесь происходит вся магия. PHP должен сохранять базу данных посетителей онлайн постоянно обновленной, а также извлекать гео информацию от Hostip API. В дальнейшем это кешируется для дальнейшего использования в виде "куки" на компьютере пользователя. who-is-online/online.php require "connect.php"; require "functions.php"; // Мы не хотим, чтобы поисковые боты искажали статистику: if(is_bot()) die(); $stringIp = $_SERVER['REMOTE_ADDR']; $intIp = ip2long($stringIp); // Проверяем отмечен ли посетитель как "онлайн": $inDB = mysql_query("SELECT 1 FROM tz_who_is_online WHERE ip=".$intIp); if(!mysql_num_rows($inDB)) { // Этого пользователя нет в БД, значит мы должны // извлечь всю гео информацию и вставить в таблицу: if($_COOKIE['geoData']) { // "geoData" cookie установлена ранее скриптом, и мы будем ей пользоваться list($city,$countryName,$countryAbbrev) = explode('|',mysql_real_escape_string(strip_tags($_COOKIE['geoData']))); } else { // Делаем API запрос Hostip: $xml = file_get_contents('http://api.hostip.info/?ip='.$stringIp); $city = get_tag('gml:name',$xml); $city = $city[1]; $countryName = get_tag('countryName',$xml); $countryName = $countryName[0]; $countryAbbrev = get_tag('countryAbbrev',$xml); $countryAbbrev = $countryAbbrev[0]; // делаем cookie wс информацией, который будет действителен в течении месяца: setcookie('geoData',$city.'|'.$countryName.'|'.$countryAbbrev, time()+60*60*24*30); } $countryName = str_replace('(Unknown Country?)','UNKNOWN',$countryName); mysql_query(" INSERT INTO tz_who_is_online (ip,city,country,countrycode) VALUES(".$intIp.",'".$city."','".$countryName."', '".$countryAbbrev."')"); } else { // Если пользователь онлайн, просто внести изменения в значение поля dt: mysql_query("UPDATE tz_who_is_online SET dt=NOW() WHERE ip=".$intIp); } // убираем записи, которые не обновлялись в течении 10 минут: mysql_query("DELETE FROM tz_who_is_online WHERE dt<SUBTIME(NOW(),'0 0:10:0')"); // Подсчитываем всех посетителей онлайн: list($totalOnline) = mysql_fetch_array(mysql_query("SELECT COUNT(*) FROM tz_who_is_online")); // Выводим число как простой текст: echo $totalOnline; Этот PHP скрипт вызывается с помощью jQuery для вставки текущего количества человек онлайн в соответствующее место. "За кулисами" этот скрипт записывает IP адрес пользователя в базу данных и уточняет всю необходимую гео информацию. Это лучшая стратегия организации серверной части, так как мы запросы к АПИ распределяются каждому пользователя при их первом посещении. Вы можете сделать запрос на Hostip API обращаясь по УРЛ типа http://api.hostip.info/?ip=128.128.128.128. И получите валидный XML ответ, который будет содержать разного рода инфомрацию, включая страну, название города связанного с этим IP, аббревиатуру страны, а также абсолютные координаты. Мы извлекаем эту информацию с помощью функции PHP file_get_contents() и выбираем необходимую нам информацию. who-is-online/geodata.php require "connect.php"; require "functions.php"; // Не хотим, чтобы боты искажали статистику: if(is_bot()) die(); // Выбираем топ 15 стран с большинством посетителей: $result = mysql_query(" SELECT countryCode,country, COUNT(*) AS total FROM tz_who_is_online GROUP BY countryCode ORDER BY total DESC LIMIT 15"); while($row=mysql_fetch_assoc($result)) { echo ' <div class="geoRow"> <div class="flag"><img src="who-is-online/img/famfamfam-countryflags/'.strtolower($row['countryCode']).'.gif" width="16" height="11" /></div> <div class="country" title="'.htmlspecialchars($row['country']).'">'.$row['country'].'</div> <div class="people">'.$row['total'].'</div> </div> '; } Geodata.php прочитывается с помощью jQuery для публикации информации в выезжающую панель. Файл делает запрос в БД с запросом GROUP BY, который группирует пользователей по стране и выстраивает их по количеству посетителей (чем больше с одной страны - тем выше в списке). Для иконок флагов используется набор от famfamfam. Большой плюс Hostip API в том, что также он выдает аббревиатуры страны в формате 2-х букв, что также поддерживается и в иконках. Таким образом скрипту очень просто найти необходимый флаг при его поиске в папке. Шаг 5 - jQuery JavaScript управляет AJAX запросами и выездом панели. Давайте взглянем на код: $(document).ready(function(){ // Функция выполняется только после загрузки документа // Кешируем jQuery селекторы: var count = $('.onlineWidget .count'); var panel = $('.onlineWidget .panel'); var timeout; // Загружаем количество пользователей онлайн в специальный div с помощью метода AJAX: count.load('who-is-online/online.php'); $('.onlineWidget').hover( function(){ // задаем пользовательское событие 'open' для выезжающей панели: clearTimeout(timeout); timeout = setTimeout(function(){panel.trigger('open');},500); }, function(){ // пользовательское событие 'close': clearTimeout(timeout); timeout = setTimeout(function(){panel.trigger('close');},500); } ); var loaded=false; // предотвращает множественное количество запросов AJAX для geodata.php; // Привет! Вызываем функции к пользовательским событиям: panel.bind('open',function(){ panel.slideDown(function(){ if(!loaded) { // Загружаем страны и флаги // после открытия выезжающей панели: panel.load('who-is-online/geodata.php'); loaded=true; } }); }).bind('close',function(){ panel.slideUp(); }); }); setTimeout используется для задержки загрузки выезжающей панели после наведения мышки на кнопку. Таким образом, случайные движения и наведения на кнопку ни к чему не приведут и не будут лишний раз запускать весь процес. Вот и все готово! Думаю, подобная фишка пригодится многим! Настройка на своем сервере В исходниках находится SQL код, который необходимо выполнить после создания БД. Он создаст таблицу, которая используется виджетом. После этого Вам необходимо загрузить все файлы на Ваш сервер и подключить widget.js в шапку документа (вместе с jQuery фреймворком). После этого не забудьте внести Ваши данные для соединения с БД в файле connect.php. И в самом конце добавьте разметку из файла demo.html в необходимом месте Вашего документа.