Для журнала "Мой компьютер". В журнале публиковалось под названием "Perl'ы для веб-мастера".

Perl'ы для веб-мастера

Евгений Гривастов (tw@tv-agent.net)

(...продолжение…)

Глава 11. Модернизация гостевой книги.

В прошлый раз мы сделали простейшую гостевую книгу. Теперь давайте научим наш скрипт работать с несколькими гостевыми книгами и выдавать на просмотр не все сообщения сразу, а пакетами по 20-ть штук. Чтобы все было понятней, сделаем два скрипта: один для просмотра гостевой книги, второй - для добавления записей. У нас уже есть директория gb, в которой лежит файл нашей первой гостевой книги. Создайте в этой директории новую поддиректорию, например firstgb - это будет директория первой из гостевых книг, обрабатываемых нашими новыми скриптами. В директорию firstgb положите два пустых файла - counts.txt и countf.txt. Назначте этим файлам права доступа 666. Их предназначение - хранить количество файлов с сообщениями гостевой книги (countf.txt) и количество записей в последнем из файлов (counts.txt). В директорию gb положите файл listgb.txt со следующей строкой:

first::firstgb

это будет файл - список гостевых книг, обслуживаемых нашими скриптами. Первым в строке идет идентификатор гостевой книги, вторым - название директории гостевой книги. Разделитель между ними - "::" (два двоеточия). Если вам понадобится добавить еще одну гостевую книгу - просто допишите в этот файл строку, с данными этой книги. Например:

second::secondgb

после чего создайте соответствующую директорию и положите в нее все те же два файла - counts.txt и countf.txt, назначив им права доступа 666. В результате получится такая структура директорий - рис. 1 (файл ris1.tif).

Сначала сделаем скрипт просмотра записей гостевой книги. Назовем его showgb.cgi. Сообщения у нас будут храниться в файлах по 20-ть штук. Имена файлов сделаем в виде номеров по порядку (1.txt, 2.txt и т.п.). Снова все комментарии - по ходу кода. Скрипт этот мы будем вызывать, указывая в качестве параметра (после знака "?") идентификатор гостевой книги и через "&" - номер двадцатки, сообщения из которой надо показывать. Т.е. примерно так:

http://www.your_domen.com/cgi-bin/showgb.cgi?first&1

причем второй параметр будет необязательным - при его отсутствии скрипт просто будет показывать последние записи в книге.

#!/usr/local/bin/perl

$gbid=$ENV{'QUERY_STRING'};
($gbid,$numzap)=split(/&/,$gbid);
# Здесь мы используем переменную $gbid два раза - сначала мы помещаем в нее полностью всю строку запроса, потом, разделив строку запроса на два параметра (с помощью функции split), присваиваем этой переменной значение идентификатора гостевой книги. В переменной $numzap будет номер файла с сообщениями, содержимое которого надо показать.

$dir='/home2/your_domen/public_html/gb/';
# В переменной $dir будет храниться полный путь на директорию gb.

$filename=$dir.'listgb.txt';
open (FLFL, "$filename");
foreach $stroka (<FLFL>) {
($id,$gbdir)=split(/::/,$stroka);
last if ($id eq $gbid);
}
close FLFL;
# Здесь мы открываем файл со списком гостевых книг и находим в нем запись нашей гостевой книги. В результате получим в переменной $gbdir имя директории гостевой книги, с которой работаем. Теперь, чтобы проверить, действительно ли мы нашли запись запрошеной гостевой книги или не нашли ничего - еще раз проверим, равно ли значение переменной $id запрошеному значинию идентификатора гостевой книги:

exit if ($id ne $gbid);
# Если в переменные $id и $gbid не равны - завершаем работу скрипта.

Отвлечемся немного, чтобы рассмотреть подробней операции сравнения, одну из которых мы использовали в последней строке.

Операция сравнения Числовое сравнение Строковое сравнение
Равно == Eq
Не равно != Ne
Меньше чем < Lt
Больше чем > Gt
Меньше чем или равно <= Le
Больше чем или равно >= Ge

Как видно, для чисел и строк предусмотрены собственные операции сравнения. Связано это с тем, что скалярная переменная в Perl может рассматриваться и как строка и как число. Поэтому надо указывать, в каком виде вы производите сравнение. В нашем случае - в строковом: мы проверяем, равно ли значение переменных $id и $gbid. Значит используем оператор "ne".

И немного об условном операторе if. Он проверяет условие, заданное в круглых скобках, и в зависимости от результата выполняет блок команд. Синтаксис его может быть такой:

if (выражение) {блок команд}

if (выражение) {блок команд} else {блок команд}

if (выражение) {блок команд} elsif (выражение) {блок команд} else {блок команд}

команда if (выражение);

Т.е. в случае, когда в результате выполнения условия необходимо выполнить только одну команду - используется последний вариант синтаксиса. Его мы и использовали для проверки, найдена ли запись с нужным нам идентификатором гостевой книги. Если такой записи не найдено - выполняется команда exit - скрипт завершает свою работу.

Вернемся к нашему скрипту. Для начала получим количество файлов с записями этой гостевой книги.

# Составим путь к файлу-счетчику и считаем из него цифру.
$filename=$dir.$gbdir.'/countf.txt';
open (FILE, "$filename");
$countf = <FILE>;
close(FILE);
# Если записей в этом файле нет, то впеременной $countf окажется значение 0 (ноль).

# Если номер файла с записями, требуемый скриптом, больше чем количество таких файлов - завершаем работу скрипта. А тот кто хотел найти в этом месте скрипта дыру - пусть отдыхает... ;-)
exit if ($countf<$numzap);

# Если в переменной $numzap ничего нет (т.е. этот параметр не передавался скриптом) - присвоим ему значение количества файлов с записями. Таким образом будем по умолчанию выводить последние записи.
$numzap=$countf if (! $numzap);

# Выведем в броузер соственно страницу гостевой книги
print "Content-Type: text/html\n\n";
print '<html><head></head><body><p><b>Моя гостевая книга</b></p><hr>';

# Выведем список для доступа к архиву записей гостевой книги
for ($iii=$countf; $iii>=1; $iii--) {
if ($iii==$numzap) {
print '<b>'.$iii.'</b> ';
} else {
print '<a href=http://www.your_domen.com/cgi-bin/showgb.cgi?'.$gbid.'&'.$iii.'>'.$iii.'</a> ';
} #if
} #for

# Теперь покажем записи из запрошенного файла
$filename=$dir.$gbdir.'/'.$numzap.'.txt';
open (FILE, "$filename");
@zapisi = <FILE>;
close(FILE);

foreach $stroka (@zapisi) {
($nik,$email,$message)=split(/|/,$stroka);
print '<hr>';
print '<a href=mailto:'.$email.'>'$nik.'</a><br>'.$message;
} #foreach

# Форма, для добавления новых записей
print '<hr>';
print '<form action="http://your_domen.com/cgi-bin/addgb.cgi" method="post">';
print '<input type="hidden" name="gbid" value='.$gbid.'><br>';
print 'Ник: <input type="text" name="nik" size="20"><br>';
print 'E-mail: <input type="text" name="email" size="20"><br>';
print 'Сообщение: <input type="text" name="message" size="20"><br>';
print '<input type="submit" value="Записать"></form>';

print '</body></html>';

exit;

Теперь сделаем скрипт, который будет добавлять записи в нашу гостевую книгу. Это будет тот самый addgb.cgi, который мы прописали в форме добавления новых записей в предыдущем скрипте.

#!/usr/bin/perl

# С помощью подпрограммы GetFormInput данные полученные от формы поместим в хэш %field
&GetFormInput;

# Присвоим данные полей html формы переменным $gbid, $nik, $email и $message
$gbid=$field{"gbid"};
$nik=$field{"nik"};
$email=$field{"email"};
$message=$field{"message"};

# В переменной $dir полный путь на директорию gb.
$dir='/home2/your_domen/public_html/gb/';

# Далее идет то, что мы уже проделывали в предыдущем скрипте просмотра гостевой книги
$filename=$dir.'listgb.txt';
open (FLFL, "$filename");
foreach $stroka (<FLFL>) {
($id,$gbdir)=split(/::/,$stroka);
last if ($id eq $gbid);
}
close FLFL;

exit if ($id ne $gbid);

# Составим путь к файлу-счетчику количества записей в последней из файлов гостевой книги и считаем из него цифру.
$filename=$dir.$gbdir.'/counts.txt';
open (FILE, "+<$filename");
flock (FILE,2);
$counts = <FILE>;

# Переместим указатель позиции в файле на его начало
seek (FILE,0,0);

# Усечем файл по указатель позиции (т.е. в нашем случае - очистим)
truncate(FILE,0);

if ($counts<20) {
# Если количество записей меньше 20-ти - то запишем новую запись в этот же файл.
# Заодно увеличим значение счетчика записей на единицу
$counts=$counts+1;
$filename=$dir.$gbdir.'/countf.txt';
open (FILEF, "$filename");
$countf = <FILEF>;
close(FILEF);
} else {
# Если количество записей не меньше 20-ти - то запишем новую запись в новый файл
# Заодно значение счетчика записей установим в единицу и увеличим значенике счетчика файлов гостевой книги
$counts=1;
$filename=$dir.$gbdir.'/countf.txt';
open (FILEF, "+<$filename");
flock (FILEF,2);
$countf = <FILEF>;
$countf=$countf+1;
seek (FILEF,0,0);
truncate(FILEF,0);
print FILEF "$countf";
close(FILEF);
}

$filename=$dir.$gbdir.'/'.$countf.'.txt';
chmod (0666, $filename);
open (FILEF, ">>$filename");
print FILEF $nik.'|'.$email.'|'.$message;
close(FILEF);

print FILE "$counts";
close(FILE);

# Перенаправим броузер на отредактированный файл нашей гостевой книги.
# Пропишите здесь правильный полный URL скрипта просмотра гостевых книг на вашем сайте.
print "Location: http://www.your_domen.com/cgi-bin/showgb.cgi?".gbid."\n\n";

exit;

# Подпрограмма, разбирающая полученные от формы данные и помещающая их в хэш %field
sub GetFormInput {
# Не будем ее здесь повторять, дабы не занимать напрасно место. Я уже писал, что эта подпрограмма стандартная для всех скриптов. Поэтому возмите ее код из скриптов, рассмотренных нами ранее.
}

Если вы последовательно читали все статьи этого цикла и разбирали все приведенные в тексте скрипты, то должны понять и то, что мы проделали в последнем скрипте. Ничего нового в нем нет. Однако остались некоторые операторы, назначение которых мы еще не рассматривали подробно. Это операторы цикла. Например, сегодня мы использовали оператор for. Основных таких операторов три: for, foreach и while.

Оператор цикла for имеет следующий синтаксис:

for (выражение1; выражение2; выражение3) {блок операторов}

выражение1 вычисляется в начале цикла и обычно задает начальное значение переменной-счетчика цикла;
выражение2 является логическим, если оно станет ложью, то цикл прерывается;
выражение3 вычисляется в конце каждого проходи цикла, перед проверкой истинности выражения1.
Если в начале цикла выражение2 оказывается ложью - блок операторов цикла не выполняется ни разу.

Оператор цикла foreach является подобной for и предназначен для использования, когда необходимо перебрать все данные из какого-нибудь спискаб массива или хэша. Его синтаксис такой:

foreach переменная (список) {блок операторов};

При каждом проходе цикла из списка извлекается очередное значение и присваивается переменной. После этого выполняется блок операторов цикла. Когда все значения списка перебраны - цикл завершается.

И синтаксис оператора while выглядит так:

while (выражение) {блок операторов};

Блок операторов будет выполняться пока выражение будет оставаться истинным. Поэтому будьте внимательны: если вы умудритесь сделатьтак, что выражение будет истинным всегда - то цикл не завершится никогда. :-) Удобно использовать оператор while в паре с оператором each для перебора всех записей хэша, что мы успешно применили в нашем скрипте countstat.cgi ("МК" №48 (115)).

(…продолжение следует…)