Раздел "Кодинг". Содержание:
Статьи:
Создание системы учета посещений
Использование шаблонов в PHP4
Построение дерева иерархии с помощью PHP / MySQL
IP адрес, подсети, маски
Использование XML Paser Functions при работе с шаблонами.
Раздел "Кодинг". Рассылка от ведущего раздела "Кодинг":
Раздел "Кодинг". Статьи:
Создание системы учета посещений
У вас может возникнуть вопрос, зачем это нужно? Свои услуги предлагают более десятка российских и огромное множество иностранных систем статистики. Так зачем лишний раз напрягаться, писать и отлаживать скрипты, создавать базы и, вообще, совершать какие-либо телодвижения, когда, потратив 10 минут, мы получим полноценную систему статистики, которая предлагает нам, просто, безграничное количество данных о пользователях нашего сайта?
Причин две. Во-первых, "внешняя" система статистики создают ощутимую задержку в загрузке страницы. Во-вторых, одному интересно узнать больше о графической системе пользователя, другому - о версиях браузера, а третьему - время проведенное пользователем на его сайте. Но, как оказывается, одна система дает инормацию о графической системе, вторая - о времени посещений, третья - вообще, не дает такой инормации, зато наиболее точно считает количество посетителей. Что делать? Вот и начинаем мы с вами ставить на страницу один, два, а потом и все пять счетчиков, после чего, время загрузки полезной информации составит не более 10% от времени загрузки сайта. Это приведет к тому, что посетитель плюнет и уйдет (интернет-то большой) или информации о нем не попадет в системы статистики. Вот тут-то мы и приходим к осознанию того, что система нужна своя.
Какие преимущества это дает? Во-первых, скорость загрузки. Цифры статистики можно вывести текстом, что не задержит загрузку, а обработка статистики будет производится на том же сервере, что и страница, что не внесет дополнительных задержек на установление связи с удаленным сервером. Во-вторых, такая система, изначально, будет соответствовать нашим запросам. Хотим - будем учитывать параметры графической системы пользователей, хотим - будем считать, сколько раз пришел за последние 15 секунд Вася Пупкин. В-третьих, так как такая система является неотъемлемой частью сайта, то не будет потеряно ни одного хита!
Здесь я не буду приводит конкретных скриптов, потому, что это будет очень громоздко, да и не нужно, вы, ведь, пришли разобраться во всем этом? Я изложу только основные принципы.
Для реализации подобной системы я использовал слудующее программное обеспечение:
Базы данных: mySQL Скрипт: PHP Вебсервер: Apache.
Сначала определимся, какие параметры мы хотим учитывать. Для себя я считаю важным знать, сколько пользователей пришло на сайт, сколько хитов они принесли, какие страницы посетили и откуда пришли и время каждого хита.
Из этих данных можно вывести довольно много статистической информации. Так что этот аскетичный набор меня вполне устраивает. Исходя из этого, я создал три таблицы в базе данных:
hits: Хранит подробную информацию о хитах за текущий день. Содержит следующие поля:
Имя | Комментарий |
host | буквенное имя домена пользователя |
addr | IP адрес пользователя |
referer | ссылка, по которой пришел пользователь |
page | на какую страницу сайта пришел пользователь |
timest | время хита. |
hitsbypage: хранит инормацию за весь период по посещениям страниц сайта. Содержит следующие поля:
Имя | Комментарий |
page | страница |
hits | количество хитов |
hosts | количество хостов |
referers: хранит информацию о ссылках, по которым приходят на сайт. Содержит следующие поля:
Имя | Комментарий |
href | собственно, ссылка |
hits | количество посещений с этой ссылки |
hitsbydate: хранит информацию о хитах и хостах по дням. Содержит следующие поля:
Имя | Комментарий |
date | дата |
hits | количество хитов |
hosts | количество хостов |
Возникает вопрос, откуда взять все эти данные? Вебсервер, при установлении сеанса устанавливает определенные переменные среды, которые доступны из скриптов на языке PHP. Прежде всего нас интересуют следующие:
Переменная | Значение |
$REQUEST_URI | адрес запрашиваемой страницы |
$REMOTE_HOST | домен пользователя (если установлен) |
$REMOTE_ADDR | IP адрес пользователя |
$HTTP_REFERER | Ссылка, по которой пришел пользователь (если таковая была, т.е. пользователь не набрал адрес сайта в браузере или выбрал из списка избранных сайтов) |
Теперь рассмотрим логику работы самой системы.
Проверяем, не является ли значение поля $HTTP_REFERER новым (не содержится в таблице referers). Если новое, то добавляем его в нужную таблицу и устанавливаем количество хитов для него в 1. Если такая ссылка уже была, то, просто, увеличиваем количество хитов.
Аналогичным образом проверяем адрес запрашиваемой страницы.
Далее проверяем, были ли хиты сегодня. Если хитов небыло, значит, начался новый день и это первое посещение сегодня. Следовательно, удаляем все данные из таблицы hits, так как хранить всю информацию в ней нерентабельно. Затем вносим новую дату в таблицу hitsbydate и устанавливаем количество хитов и хостов для данной даты в 1. Если же новый день еще не наступил, то, проверив, не является ли IP адрес уникальным на сегодня, увеличиваем поля hits и hosts в таблице hitsbydate.
И, наконец, заносим информацию в таблицу hits.
Вот и все. Вся необходимая информация хранится в базах на сервере и доступна в любой момент для проведения дальнейшего статистического анализа.
Результаты работы такой системы вы можете посмотреть на странице статистики моего сервера.
Использование шаблонов в PHP4
Многие программеры сталкивались с проблемой, когда дизайнеру "нужна
свобода" в написании html, а программеру "чистота" кода :) У меня такое
приключилось при написании виртуального веб-магазина. В общем, недолго
думая я стал искать различные PHP-классы для создания "динамических сайтов"
с использованием так называемых шаблонов. И нашел один, который отвечал
всем моим требованиям, но слишком уж он оказался "большим и тяжелым".
Называется этот класс FastTemplate (уж не помню, где я его скачал). Недолго
думая, я решил написать свой класс, взяв за основу функциональность
FastTemplate. Мои результаты в написании своего класса получились
практически такими как у FastTemplate, но, как мне кажется, ни чуть не
хуже (прим.: код я не копировал, а создавал сам с нуля).
Итак, для начала работы Вам необходимо скачать мой класс
template.
Скачали? Теперь можно пробовать на простом примере. Сделаем пример листинга
файлов текущего каталога с подсчетом кол-ва байт каждого файла, при этом
динамически создав таблицу. Итак, создайте следующие файлы:
main.htm
<html>
<head>
<title><!-- ABOUT --></title>
<link rel="stylesheet" type="text/css" href="/styles.css">
</head>
<body>
<p align="center" class="b"><!--
ABOUT
--><br>
Localtime is <!-- LOCALTIME -->
</p><br>
<div align="center"><table
STYLE="border-collapse:collapse" class="th">
<tr>
<td colspan="2" class="th"
align="center"
style="background-color:#000000; color:white">
File listing</td>
</tr>
<!--
TABLE_CONTENT
-->
<tr>
<td class="th" align="right"
style="background-color:#000000; color:white"> </td>
<td class="th"
style="background-color:#000000; color:white">
<!-- TOTAL --> </td>
</tr>
</table></div>
</body>
</html>
rows.htm
<tr>
<td align="right" class="th"
style="background-color:<!-- COLOR -->;color:black">
<!-- PWD --> </td>
<td class="th" style="background-color:<!-- COLOR -->; color:black">
<!-- FILESIZE --> </td>
</tr>
index.php
<?php
require ('templates.php'); // Включаем класс для работы с шаблонами
// Определяем теги
$meta = array( "ABOUT" => ":: template class example ::",
"LOCALTIME" => date("M-d-Y H:i:s"));
$t = new template;
// Инициализируем файлы и дескрипторы
$t->init(array( index => "main.htm", rows => "rows.htm"));
// Устанавливаем ограничители (delimiter-ы)
$t->delimiters("<!--", "-->");
// Определяем теги
$t->assign($meta);
$d = dir(".");
$colors = array("#d4d4d4", "#a0a0a0"); $i = 0;
$totalbytes = 0;
while ($entry = $d->read()) {
if (preg_match("/^(.|..)$/", $entry)) continue;
$color = $colors[$i];
$t->assign("FILENAME", $entry); // Определяем под тегом FILNAME имя файла
$t->assign("COLOR", $color); // под COLOR текущий цвет
$t->assign("PWD", realpath ($entry)); // Полный путь
if (($size = filesize($entry)) > 1024) {
$totalbytes += $size;
$size = sprintf("%0.2f Kbytes", ($size / 1024));
} else {
$totalbytes += $size;
$size .= " bytes";
}
$t->assign("FILESIZE", $size); // связываем с тегом FILESIZE длинну файла
// Обрабатываем файл с дескриптором rows, при этом полученные результаты
// закрепляем под тегом TABLE_CONTENT (путем добавления)
$t->parseit(rows, "TABLE_CONTENT");
$i = (++$i >= count($colors)) ? 0 : $i;
// Под этим тегом у нас будет общее кол-во байт найденых файлов
$t->assign("TOTAL", ( ($totalbytes > 1024) ?
sprintf("%0.2f Kbytes", ($totalbytes / 1024)) : $totalbytes." bytes"));
}
$d->close();
// Обрабатываем страницу с дескриптором index. Т.е. при обработке
// все попадающиеся теги будут заменены на определенное значение.
$t->parseit(index);
// Выводим все
$t->printit();
$t->freshall();
?>
Результаты выполнения можно увидеть здесь
http://null.magelan.ru/php/templates
Данный класс работает очень шустро, обработчик построен на основе регулярных
выражений.
Детальное описание
В классе определенны массивы:
- $filelist - ассоциативный массив дексрипторов и файлов
- $assign - ассоциативный массив определенных тегов
- $root - корневой каталог
- $arr - массив с результатом
- $delmiters - ограничители
// init - Инициализация шаблонов
// arr - ассоциативный массив (см.пример) с дескрипторами и файлами
// root - корневой каталог, где лежат файлы (по-умолчанию текущий)
// delimit - тут можно указать ограничитель из двух символов, например "{}"
function init($arr = "", $root = "", $delimit = "")
// Установка корневого каталога шаблонов
function setroot($root = "")
// Добавление дескрипторов и файлов для работы с шаблонами
// list - ассоциативный массив
function listit($list = "")
// Обработка шаблона
// $d - дескриптор файла ИЛИ МАССИВ!
// $temp - обработка в тег $temp (если не указать, данная ф-ия обработает
// декср.файла и добавит его к массиву с результатами)
function parseit($d = "", $temp = "")
// Получить обработанный массив, возвращает string
// $array - массив
function getparsed($array)
// Указать ограничители
// $d1 - левый, $d2 - правый
// Например $t->delimiters("<!--", "-->");
function delimiters($d1 = "", $d2 = "")
// Как listit, только добавить можно не ассоциативный массив, а
// простую строку
// $d - дескриптор файла
// $name - имя файла
function addtolist ($d = "", $name = "")
// Используется для очистки всех массивов в классе
function freshall()
// Используется для очистки результатов обработки
// Очищается $arr
function fresh()
// Вывести на экран.
// В v1.02 - если указан $tag, выводит тег $this->assign["$tag"]
function printit([$tag])
Если возникнут какие-либо комментарии, пишите на null@magelan.ru.
Также выслушаю замечания и поправки.
Огромное спасибо Fil (fil@apb.farlep.net) за некоторые дополнения и
замечания :) И всем тем, кто откликнулся!
Построение дерева иерархии с помощью PHP / MySQL
Рассмотрим пример построения дерева иерархии (в развернутом виде) на основе информации из базы данных с помощью PHP и MySQL. Ключ к решению данной задачи - использование рекурсивной функции. Иерархия разделов будет храниться в таблице базы данных MySQL.
Ниже на скриншоте показана данная таблица (catalogue):

- id - первичный ключ таблицы
- pid - id родительского раздела
Далее напишем следующий PHP-скрипт:
1. Файл dbopen.php (открывает соединение с MySQL)
<?php
$hostName = "";
$userName = "yura";
$password = "yura";
$databaseName = "tree";
if (!($link=mysql_connect($hostName,$userName,$password))) {
printf("Ошибка при соединении с MySQL !\n");
exit();
}
if (!mysql_select_db($databaseName, $link)) {
printf("Ошибка базы данных !");
exit();
}
?>
2. Файл index.php (основной скрипт)
<?php
include( "dbopen.php" );
function ShowTree($ParentID, $lvl) {
global $link;
global $lvl;
$lvl++;
$sSQL="SELECT id,title,pid FROM catalogue WHERE pid=".$ParentID." ORDER BY title";
$result=mysql_query($sSQL, $link);
if (mysql_num_rows($result) > 0) {
echo("<UL>\n");
while ( $row = mysql_fetch_array($result) ) {
$ID1 = $row["id"];
echo("<LI>\n");
echo("<A HREF=\""."?ID=".$ID1."\">".$row["title"]."</A>"." \n");
ShowTree($ID1, $lvl);
$lvl--;
}
echo("</UL>\n");
}
}
ShowTree(0, 0);
mysql_close($link);
?>
Всю работу выполняет рекурсивная функция ShowTree(). Ниже на скриншоте показан пример работы index.php:

IP адрес, подсети, маски
Есть некоторые вещи которые я не способен считать в уме, но зато без каких либо затруднений могу написать программу для их расчета. Одна из них - это перерасчет подсетей.
Задача: из 217.19.211.69/24 получить 217.19.211.0/255.255.255.0 и
217.19.211.0-217.19.211.255
Решение:
$ip=explode("/","XXX.XXX.XXX.XXX/24");
$mask=0xFFFFFFFF;
for ($j=0;$j<32-$ip[1];$j++) $mask=$mask<<1;
$lip=ip2long($ip[0]);
print "<P>Результат: ".long2ip($lip&$mask)."/".long2ip($mask)."</P>";
print "<P>Результат: ".long2ip($lip&$mask)."-".long2ip(($lip&$mask)+(~$mask))."</P>";
Живой пример
Использование XML Paser Functions при работе с шаблонами.
Несмотря на то, что идея разделения кода и данных не нова, она сохраняет свою актуальность. Удобство неоспоримо - так как люди изменяющие данные зачастую не должны иметь доступ к коду.
В PHP синтаксис языка основан не внедрении кода в данные и в этой статье мы рассмотрим один из достаточно удобных способов их разделения. Способ будет основываться на языке XML.
Рассмотрим следующую задачу: У нас есть много клиентов, и практически каждый из них, желает видеть на своем сайте гостевую книгу. Каждый раз изменять исходный текст гостевой книги нам уже поднадоело. И речи уже не идет о том, что ошибку, которую мы нашли, устанавливая гостевую книгу в восемнадцатый раз пришлось рукам исправлять в на предыдущих семнадцати сайтах.
Данные:
Для того, чтобы избежать подобной проблемы, необходимо данные отделить от кода. Нам бы хотелось чтобы внешний вид гостевой книги хранился в отдельном файле, динамические данные (записи) хранились в базе данных, а код в отдельном каталоге.
Так, мы могли бы быстро исправить допущенную ошибку простой заменой старого кода на новый, при это сохранилось бы оригинальное оформление.
Опишем шаблон гостевой книги с помощью XML следующим образом:
<?xml version="1.0" encoding="windows-1251"?>
<guestbook>
<include url="../top.html" />
<![CDATA[
<H3>Гостевая книга - место для трепа</H3>
<table width=100% cellspacing=5><tr><td>
<br><center>
]]>
<record><![CDATA[
<table cellpadding=7 cellspacing=0 bgcolor=#F0F8F8 width=95%>
<tr bgcolor=#E0F0F0><td>__NAME__
(<a href=mailto:__EMAIL__>__EMAIL__</a>)</tr>
<tr><td>__COMMENT__
</td></tr>
</table>
<br>
]]></record>
<![CDATA[
<br>
<center><BR><font face=Verdana size=2>
<A href=/add/>Добавить запись (Add record)</A>
</font></center>
</td></tr></table>
]]>
<include url="../bottom.htm />
</guestbook>
Каждый шаблон состоит их основной секции <guestbook></guestbook> внутрь которой может помещаться секция <records></record> о
писывающая одну запись в гостевой книге.
Кроме того, там может находится одиночный тег <include />, не место которого будет вставлен документ описанный с помощью свойства url, например:
<include url="../bottom.htm />
Ниже приведена сокращения схема описанного документа:
<?xml version="1.0" encoding="windows-1251"?>
<guestbook>
<include url="../top.html" />
<record>
тело одной записи
</record>
<include url="../bottom.htm />
</guestbook>
Код:
Осталось малость - написать программу, которая превратит описанный выше шаблон в HTML документ, содержащий как внешнее оформление, так и динам
ически изменяемые данные (записи в гостевой книге)
Для обработки шаблона мы будем использовать XML Parser functions (http://www.php.net/manual/en/ref.xml.php). Это расширение PHP предоставляет доступ к функциям библиот
еки Expat, автором которой является Джеймс Кларк (James Clark). Библиотека Expat написана на языке C и
предназначенная для разбора XML документов основанного на событиях. Она не проверят XML документ на ошибки и не работает с объектой моделью XML док
умента, так как это делают некоторые другие библиотеки (например tinyXML)
В версии PHP 4.3.1 (а возможно, что и в более ранних) XML Parser functions поддерживаются по умолчанию.
Перейдем непосредственно к написанию программы - обработчика шаблона.
Сначала считаем шаблон в переменную $xmldata:
<?
$xmldata=implode("",file("template.xml"));
// Ассоциативный массив - хранит данные, соответствующие тегам.
$TMPL=Array();
// Обрабатываемый тег
$ce="";
/*
...
пропущено подключение к серверу MySql
...
*/
$result=mysql_query("SELECT * FROM guestbook_database");
В переменную $html мы будет выводить результат работы нашего скрипта. В самом конце мы сделаем просто print $html;
$html="";
Создаем объект обработчик XML документа
$xml_parser = xml_parser_create();
Задаем ему опции и обработчики. Функция startElement() будет вызываться, когда в XML документе встретится открывающийся тег. Функция endElement()
будет вызываться, когда будет встречен закрывающий тег. Для данных (то, что внутри тега) будет вызываться функция characterData()
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
Вызовем обработчик XML документа
if (!xml_parse($xml_parser, $xmldata)) {
$error=sprintf("Ошибка в шаблоне: %s at line %d",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser));
die();
}
xml_parser_free($xml_parser);
Вывод данных
print $html;
Теперь рассмотрим основную часть - это три функции-обработчика:
В глобальной переменной $ce запомним название обрабатываемого тега, чтобы в обработчике characterData() знать к какому элементу массива $TMPL доп
исывать содержимое.
function startElement($parser, $name, $attrs) {
GLOBAL $ce,$TMPL,$html;
switch ($name) {
case "INCLUDE":
$html.=@implode("",@file($attrs["URL"]));
break;
}
$TMPL[$name]="";
$ce=$name;
}
function endElement($parser, $name) {
GLOBAL $ce,$TMPL,$result,$html;
switch ($name) {
case "RECORD":
while ($D=mysql_fetch_array($result)) {
$t=$TMPL["RECORD"];
$t=str_replace("__NAME__",$D["name"],$t);
$t=str_replace("__EMAIL__",$D["email"],$t);
$t=str_replace("__COMMENT__",$D["comment"],$t);
$html.=$t;
}
break;
}
$ce="";
}
function characterData($parser, $data) {
GLOBAL $ce,$TMPL,$html;
switch ($ce) {
case "RECORD":
$TMPL[$ce].=$data;
break;
default:
$html.=$data;
}
}
Вот вроде и все. Если вы разобрались с "XML parser functions",
то рекомендую изучить "XSLT
functions" и "DOM
XML functions". Они вам помогут решить подобные задачи.
|