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