Pull to refresh

MODх — Учет посетителей сайта и график посещений

Reading time 7 min
Views 14K
Как и многие программисты, я страдаю некоторой степенью подозрительности к чужим сервисам, и предпочитаю делать все сам.
К чужим сервисам, в частности относится liveinternet и другие счетчики посещений. Я им как то не доверяю, знаете ли.

Сейчас я вам расскажу (и покажу) как нетрудно сделать учет посетителей сайта с помощью modx.


Этап 1. Пишем лог.


Для начала нам нужно создать таблицу для хранения посещений. Делаем sql запрос как вам удобно. Например, в phpmyadmin.
CREATE TABLE IF NOT EXISTS `modx_visitors_log` (
  `index` int(10) NOT NULL AUTO_INCREMENT,
  `ip` varchar(15) COLLATE utf8_unicode_ci NOT NULL,
  `host` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `url` text COLLATE utf8_unicode_ci NOT NULL,
  `datetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `referer` text COLLATE utf8_unicode_ci NOT NULL,
  `browser` varchar(255) NOT NULL,
  PRIMARY KEY (`index`),
  KEY `ip` (`ip`,`host`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;

Таблица готова, теперь нам нужен плагин, для фиксирования посещений.

Большинство пользователей знают про сниппеты и чанки в modx, а вот про плагины обычно не в курсе. Рассказываю: плагин в modx, если по простому — это сниппет, который работает без вызова из документа, сам по себе, реагируя на системные события, коих в движке предусмотрено довольно много. Нас интересует событие OnLogPageHit.

Идем в
Элементы->Управление элементами->Плагины->Создать плагин

Называем новый плагин dbLog и вставляем в него следующий код:
/* Основные настройки*/
// Таблица для лога посетителей по умолчанию это modx_visitors_log
$db = '`modx_visitors_log`';
// Не логировать этих юзеров и IP. Если логировать всех - должны быть пустые массивы
$not_log_ip = array('192.168.100.1', '192.168.100.2');
$not_log_user = array('bezumkin');
/* Конец настроек */

// Выставляем переменные
$login = $_SESSION['webShortname']; // Если у вас юзеры авторизуются на сайте. Если нет - будет пустое поле.
$ip = $_SERVER['REMOTE_ADDR']; // IP запрашивающего адреса. Проверки на прокси нет.
$host = ''; // Здесь будет имя хоста, принадлежащего IP юзера. Пока не трогаем.
$url = $_SERVER['REQUEST_URI']; // Запрашиваемая страница.
$referer = urlencode($_SERVER['HTTP_REFERER']); // Откуда пришел юзер, по какой ссылке.
$browser = mysql_real_escape_string($_SERVER['HTTP_USER_AGENT']); // Браузер юзера. Я использую класс browscap, а вы как хотите. Для примера сойдет и так.

// Пишем в лог
if (!in_array($ip, $not_log_ip) and !in_array($login, $not_log_login)) {
    $modx->db->query("INSERT INTO $db (`login`, `ip`, `host`, `url`, `referer`,`browser`)
             VALUES ('$login','$ip','$host', '$url','$referer','$browser')");
    }

Переключаем вкладку Системные события и выставляем OnLogPageHit, затем жмакаем сохранить. Обратите внимание: при оформлении плагина в редакторе админки не должно быть <?php вначале и ?> в конце. У сниппетов должно, а у плагинов — нет. Не знаю почему так сделано, но при написании своего первого пагина я много матерился.
Все, плагин уже должен фиксировать всех ваших посетителей. Если вы, конечно, нигде не ошиблись!

Кстати, есть еще вариант повешать срабатывание плагина на событие OnWebPageComplete. Тогда нужно будет в настройках админки включить «Регистрировать посещения» и заработает аналогичный тырчик в свойствах документов. Тоже не плохой вариант, но я его еще не обкатывал. Подробнее о событиях можно глянуть тут.

Если вы хотите узнавать имена хостов, которые зарегистрированы на ip ваших посетителей, можно сделать еще один сниппет (Ip2Host) и запускать его по расписанию.
Почему так? Это сэкономит силы серверу, ибо, если узнавать хост юзера при каждом запросе страницы — нагрузка будет нешуточная. Лучше запускать раз в сутки, ночью.
<?php
$db = '`modx_visitors_log`';

// Выбираем все записи, с пустым полем host
$sql = $modx->db->query("SELECT `ip` FROM $db WHERE `host` = '' GROUP BY `ip`");
$arr = $modx->db->makeArray($sql);

// Перебираем их все
foreach ($arr as $v) {
    $ip = $v['ip'];
    // Делаем обычный nslookup. Если сайт хостится на Windows, во-первых, мне вас жаль, а во-вторых - эту строчку придется поменять на что-то другое.
    $host = `nslookup "$ip" | grep 'name =' | awk '{print $4}'`;
    // Если имени мы не получили - пишем, что хост не известен, чтобы повторно его не выбирать в следующий раз.
    if (empty($host)) {$host = 'unknown';}
    //Сохраняем результат в базу.
    $modx->db->query("UPDATE $db SET `host` = '$host' WHERE `ip` = '$ip' AND `host` = '';");
}
?>

Нужно оформить вызов на какой-нибудь скрытой от посторонних странице:
[!Ip2Host!]

и дергать ее cron`ом:
10 2 * * * * user wget localhost/secret_page.html


Этап второй. Вывод лога на экран.


В принципе, ваши посещения уже в базе, и выводить их оттуда можно как удобно. Лично мне приятно наблюдать это в виде симпатичного графика и отдельной табличке с циферками.

Так и сделаем.

Пишем сниппет для забора данных из БД и вывода их в понятную таблицу, график будем делать позже. Комментарии как обычно, внутри кода.
<?php
/* Основные настройки */
if (empty($db)) {$db = '`modx_visitors_log`';} // База данных с логом
if (empty($days)) {$days = 14;} // Кол-во дней для вывода таблицы
if (empty($daysText)) {$daysText = 'дней';} // Текст после кол-ва дней в чанке, не обязательно
if (empty($tpl)) {$tpl = 'visitStat.tpl';} // Шаблон для таблицы
if (empty($act)) {$act = 'graph';} // Режим вывода таблицы, всего их два, graph & table
if (empty($dateFormat)) {$dateFormat = '%d.%m';} // вывод даты, формат strftime()
if (empty($int)) {$int = 2;} // Интервал вывода даты в режиме graph. по умолчанию - каждая вторая.
/* Конец настроек */

// Выясняем, за какое время выбирать данные
$cur = time();
$end = date("Y-m-d");
$start = strftime("%Y-%m-%d", ($cur - ($days * 86400)));

// Выбираем
$sql = $modx->db->query("SELECT DATE(`datetime`) as `date`,
      COUNT(distinct `ip`) as `host`, 
      COUNT(`ip`) as `hit` 
      FROM $db WHERE DATE(`datetime`) BETWEEN '$start' AND '$end' 
      GROUP BY DATE(`datetime`) ORDER BY `datetime` ASC");
$result = $modx->db->makeArray($sql);

// Разбираем результат на 3 разных массива, дату, хосты и хиты    
    foreach($result as $v) {
        $date[] = $v['date'];
        $host[] = $v['host'];
        $hit[] = $v['hit'];
    }

// В режиме graph выводится каждая $int дата, в режиме таблицы - все даты
        $i = 1;
    foreach ($date as $v) {  
        if ($act == 'graph') {
            if ($i == $int) {
                $i = 0;
                $date2 .= '<th></th>';
            } 
            else {
                $date2 .= '<th>'.strftime($dateFormat, strtotime($v)).'</th>';
            }
            $i++;
        }
        else if ($act == 'table') {
            $date2 .= '<th>'.strftime($dateFormat, strtotime($v)).'</th>';
        }
    }
    foreach ($host as $v) {
        $host2 .= '<td>'.$v.'</td>';
    }
    foreach ($hit as $v) {
        $hit2 .= '<td>'.$v.'</td>';
    }

$placeholders = array('[+stat.days+]','[+stat.days.text+]','[+stat.date+]','[+stat.host+]','[+stat.hit+]');
$values = array($days, $daysText, $date2, $host2, $hit2);

$html = $modx->getChunk($tpl);

echo str_replace($placeholders, $values, $html);
?>

Создаем сниппет, называем его, например, generateStatGraph, копируем туда код и сохраняем.

Теперь нам нужно как то вывести на экран результат нашей бурной деятельности.
Нам понадобится два чанка-шаблона. первый, просто для таблицы, второй посложнее — для построения графика. Их можно будет использовать независимо друг от друга.

Первый шаблон, для вывода таблицы (tpl.StatTable).
<div style='margin: auto;text-align: center;'>
    <table id='chart_table'>
    <caption>Статистика посещений сайта за последние [+stat.days+] [+stat.days.text+]</caption>
        <thead>
            <tr><td></td>[+stat.date+]</tr>
        </thead>
        <tbody>
            <tr><th>Посетители</th>[+stat.host+]</tr>
            <tr><th>Просмотры</th>[+stat.hit+]</tr>
        </tbody>
    </table>
</div>


Второй шаблон, для построения графика (tpl.StatGraph).
<link type='text/css' rel='stylesheet' href='[(site_url)]inc/css/visualize.css' /> 
<script type='text/javascript' src='[(site_url)]inc/js/visualize.jquery.js'></script>
<script type='text/javascript'>
$(document).ready(function(){
    $('#chart').visualize({
        type: 'area',
        width: '570',
        height: '300'
    });   
});
</script>
<div style='margin: auto;'>
<table id='chart' style='display: none;'>
    <caption>Статистика посещений сайта за последние [+stat.days+] [+stat.days.text+]</caption>
        <thead>
            <tr><td></td>[+stat.date+]</tr>
        </thead>
        <tbody>
            <tr><th>Посетители</th>[+stat.host+]</tr>
            <tr><th>Просмотры</th>[+stat.hit+]</tr>
        </tbody>
    </table>
</div>


Как вы можете заметить, эти два шаблона отличаются наличием во втором подключения замечательного плагина jquery jQuery Visualize, который позволяет нам строить графики из таблиц. А также оформление этого графика.
Отрисовка графика запускается следующим кодом (подробнее про параметры jquery.visualize, а также примеры оформления — по ссылке выше):
$(document).ready(function(){
    $('#chart').visualize({
        type: 'area',
        width: '570',
        height: '300'
    });   
});

Уффф… Уже почти все.

Этап 3. Запуск!


Создаем новый документ modx и запускаем в нем сниппет (отдельно таблицу и график).
[!generateStatGraph?
&days=`20`
&tpl=`tpl.StatTable`
&act=`table`
&dateFormat=`%d <i>%b</i>`
!]

[!generateStatGraph?
&days=`20`
&daysText=`дней`
&tpl=`tpl.StatGraph`
&act=`graph`
&dateFormat=`%d`
!]


Хотелось бы отметить, что здесь мы рассмотрели только показ анонимной статистики посещений, а в БД у нас хранится информация об ip, времени посещений, браузерах юзеров и тд. Эту информацию вы можете просматривать или напрямую из БД, или написать простенький сниппет + чанк оформления для вывода на сайт, в защищенный раздел.

Приложение.


Кому любопытно — можно посмотреть результат в работе, заодно воочию зацените хабраэффект.
Кстати, видно некислое различие статистики, которую собираю я, и которую показывает liveinternet на счетчике внизу каждой страницы.

Параметры сниппета generateStatGraph:
&db
по умолчанию: `modx_visitors_log`
значение: [string]
описание: Имя существующей таблицы, можно вместе с БД (`modx`.`modx_log`).

&dateFormat
по умолчанию: '%d %b %Y %H:%M'
значение: переменные strftime()
описание: Формат даты.

&days
по умолчанию: 14
значение: [int]
описание: Количество дней выборки посещений от сейчас.

&$daysText
по умолчанию: 'дней'
значение: [string]
описание: Текст подписи для кол-ва дней выборки, не обязательно, используется только в шаблонах.

&tpl
по умолчанию: 'tpl.StatGraph'
значение: Имя существующего шаблона modx
описание: Шаблон для вывода таблицы.

&act
по умолчанию: 'graph'
значение: graph, table
описание: Режим обработки таблицы при выводе. graph пропускает даты.

&int
по умолчанию: 2
значение: [int]
описание: Если &act=`graph`, &int указывает, каждую какую дату показывать. по умолчанию - каждую вторую.


Плэйсхолдеры сниппета eventsCalendar:
[+stat.days+]
Количество дней выборки
[+stat.days.text+]
Подпись к дням.
[+stat.date+]
Дата в заданном формате.
[+stat.host+]
Количество уникальных посещений за день (хосты).
[+stat.hit+]
Количество не уникальных посещений за день (хиты).


Ссылки на нужные сайты.


Багрепорты засылать на bezumkin@yandex.ru, или оставлять в этой теме.
Tags:
Hubs:
+17
Comments 40
Comments Comments 40

Articles