|
Гостевая книга
Гостевая книга - это такая страничка на вашем сайте, где пользователи смогут оставлять свои сообщения, общаться, высказывать предложения и пожелания (самое, пожалуй, неприятное :) ). Для тех же целей служат и форумы, где одновременно может обсуждаться несколько тем, но написание форума - дело не самое простое. Итак - гостевая книга.
Между показами, сообщения, оставленные посетителями, нужно где-то хранить. Хотя в нашем случае, скорее "как-то", поскольку хранить их мы точно будем в файле. Варианта у меня два. Во-первых, можно обрабатывать полученное сообщение и сразу же дописывать его в файл в оформленом виде. Во-вторых, можно просто сохранять в файл сами данные, и оформлять их когда пользователь запросит страницу гостевой. У каждого метода есть свои плюсы и минусы, но об этом позже.
Как это все будет выглядеть? Пользователь вписывает в поля формы свое имя (ник), тему сообщения, само сообщение и адрес электронной почты (по желанию). После нажатия кнопки "Добавить", данные передаются скрипту по методу POST. Скрипт так или иначе сохраняет данные и отсылает пользователю новую страничку. Начнем:
Гостевая книга Вариант-1
<?php
define('FileName', 'gbdata.txt'); //зададим константы: файл с записями
define('bkcolor', '#b0e0e6'); //цвет фона
define('frontcolor', '#87ceeb'); //еще один цвет
function SaveMsg($author, $email, $topic, $msg) //эта функция сохраняет сообщене
{
$oldfile = file(FileName); //читаем все записи в массив
if (get_magic_quotes_gpc()): //если magic_quotes_gpc включено
$author = stripslashes($author); //заменяем все escape
$email = stripslashes($email); //последовательности
$topic = stripslashes($topic); //(удаляем слэши)
$msg = stripslashes($msg);
endif;
$search = array('<', '>'); //заменяем в полученых данных знаки
$replace = array('<', '>'); //'<' и '>' на '<', '>'
$author = str_replace($search, $replace, $author); //чтобы не было проблем с тегами
$email = str_replace($search, $replace, $email); //которые кто-нибудь обязательно введет
$topic = str_replace($search, $replace, $topic);
$msg = str_replace($search, $replace, $msg);
$fd = @fopen(FileName, 'w') //открываем фал для перезаписи
or die('<B>Не удалось открыть файл сообщений для записи.</b>');
//Если указан адрес электронной почты, оформляем имя автора как ссылку
$a = ($email != '') ? "<A href = mailto:$email>$author</a>" : "$author";
//иначе оставляем как есть
//начинаем сохранять сообщение
fwrite($fd, "<TR bgcolor='".frontcolor."'> //оформляем как таблицу
<TD width = '18%' align = 'center'><B>".date('d.m.Y-H:i')."</b></td> //дата
<TD width = '17%' align = 'center'>$a</td><TD width = '65%'>$topic</td> //автор и тема
</tr>
<TR bgcolor='".bkcolor."'>
<TD colspan = '3'><TABLE width = '98%' align = 'center'><TR><TD>");
//сам текст сообщения
$res = preg_match_all("/.+/", $msg, $found); //разбиваем сообщение на строки
for($i = 0; $i < $res; $i++): //каждую строку сохраняем как отдельный абзац
fwrite($fd, "<P>". trim($found[0][$i]) ."</P>\r\n");
endfor;
fwrite($fd, " </td></tr></table></td> //закрываем ячейку таблицы
</tr>");
fwrite($fd, implode('', $oldfile)); //вставляем старые записи
}
function PrintForm() //эта функция печатает форму и
{ ?> //все записи гостевой книги
<html> //выходим из обработки PHP
<head> //эта часть страницы не содержит переменных
<TITLE>Гостевая книга.</title> //так что ее можно выводить без обработки
</head>
<SCRIPT> //однако интеграция
<!--- Главное, чтобы этого не увидел древний браузер //чтобы проверить, заполнены ли поля формы
function CheckEmpty(f, theName) { //воспользуемся функцией на java-script
if (f.value.length == 0) {
alert('Не заполнено поле \'' + theName + "\' ");
f.focus();
f.select();
return false;
} else {
return true;
}
}
-->
</SCRIPT>
<body>
<?php //а вот теперь начинаем использовать переменные
print("<FORM action='{$_SERVER['PHP_SELF']}' method='POST' //форма передает параметры этому же скрипту
onSubmit = \"return (CheckEmpty(this.author, 'Автор')
&& CheckEmpty(this.topic, 'Тема') && CheckEmpty(this.msg, 'Сообщение'))\">
//проверили, все ли необходимые поля заполнил пользователь
//вывод формы
<TABLE border = '0' width = '760' align = 'center'>
<TR bgcolor='".frontcolor."'>
<TD width = '80'> Имя: </td>
<TD> <input type='text' name='author' size='45' tabindex='1'></td>
<TD width = '80'> e-mail: </td>
<TD> <input type='text' name='email' size='45' tabindex='2'></td>
</tr>
<TR bgcolor='".bkcolor."'>
<TD> Тема: </td>
<TD colspan = '3' align = 'left'> <input type='text' name='topic' size='108' tabindex='3'></td>
</tr>
<TR bgcolor='".frontcolor."'>
<TD colspan = '4'> <textarea name='msg' rows='10' cols='92' tabindex='5'></textarea></td>
</tr>
<TR bgcolor='".bkcolor."'>
<TD colspan = '4' align = 'left'> <input type='submit' name='submit' value='Добавить' tabindex='6'></td>
</tr>
</table>
<INPUT type='hidden' name='action' value='submitted'>
</form>
");
print("<TABLE border = '0' width = '760' align = 'center'>\r\n"); //теперь печатаем сохраненные сообщения
print(implode('', file(FileName))); //внутри файла уже все оформлено, можно спокойно его выводить
print("</table>\r\n");
print("</body>
</html>");
}
//все функции оформлены, основная часть
//если есть параметр 'action' и он равен 'submitted', значит пользователь передал данные
if (isset($_POST['action']) && $_POST['action'] == 'submitted'):
SaveMsg($_POST['author'], $_POST['email'], $_POST['topic'], $_POST['msg']); //сохраняем сообщение в файл
//посылаем браузеру заголовок перехода, чтобы он вновь загрузил страницу с книгой
header("Location: http://".$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF']);
//на тот случай, если браузер нас не понял.
print("<p>Сообщение успешно добавлено</p>");
print('<a href="http://' . $_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'>Возврат</a>');
else:
PrintForm(); //а если пользователь не передал данные, просто выводим форму.
endif;
?>
Что нового:
- define(строка имя, значение [, логическое регистронезависимая]) - создает константу с указаным именем и значением. Если дополнительный параметр не задан, считается регистрозависимой. Имена констант и переменных определены в разных пространствах имен и могут совпадать.
- file(строка имя_файла [, целое использовать_include_path]) - возвращает содержимое файла в виде массива. Элемент массива - строка из файла с символами конца строки. Если возникает ошибка, возвращается FALSE. Если использовать_include_path = '1', то указаный файл ищется так же в каталогах, заданых настройкой include_path.
- get_magic_quotes_gpc() - показывает, включена ли опция magic_quotes_gpc в настройках PHP. Если она включена, во всех данных, получаемых скриптом спецсимволы (одинарная и двойная кавычки и т.п.) будут предваряться обратным слешем ('\'). Это нужно для записи в базы данных.
- stripslashes(строка стр) - удаляет '\' перед спецсимволами. Возвращает результирующую строку.
- str_replace(найти_что, заменить_на, искать_где) - заменяет части строки или массива. Аргументами служат строки и массивы. Если найти_что и заменить_на - массивы одинаковой длины, замена осуществляется на соответствующий элемент. Подробнее см. Руководство.
- preg_match_all(строка шаблон, строка стр, массив результат) - функция, работающая с регулярными выражениями в стиле Perl. В массиве результат содержатся все найденые совпадения. В нашем случае, это последовательность, содержащая один или более ('+') любых символов, кроме символа конца строки ('.'). То есть строка. Поскольку в разных операционных системах конец строки определяется разными символами ('\n' - Unix, '\r\n' - Windows), и в файл сохраняются строки, завершающиеся '\r\n', после получения строки вызывается trim()
- implode(строка связка, массив части) - возвращает строку состоящую из элементов массива части, разделенных строкой связка.
- $_SERVER['PHP_SELF'] - содержит относительный адрес скрипта относительно сервера. Т.е. для www.site.ru/zrrr/test.php результат будет zrrr/test.php
- $_POST - суперглобальный массив, содержащий все параметры, переданые скрипту методом POST. Ключем служит имя параметра.
- $_SERVER['HTTP_HOST'] - содержит адрес сервера, взятый из заголовка запроса.
Итак, что происходит, когда пользователь переходит на страницу с нашей книгой? Скрипт проверяет, установлен ли параметр 'action' и равен ли он 'submitted'. Естественно нет, так как пользователь зашел первый раз, и скрипт выводит форму. Далее, пользователь заполняет поля формы и жмет 'Ok'. Функция на java-script проверяет, заполнил ли он нужные поля, если нет, то выводит сообщение и передача данных не происходит. Передача данных производится методом POST и все данные из заполненых полей формы будут доступны через суперглобальный массив $_POST. Обработкой данных занимается этот же скрипт (<FORM action='{$_SERVER['PHP_SELF']}' method='POST'), но теперь обработчик пойдет по другой ветке и сохранит сообщение в файл. Дальше мы, с помощью заголовка перехода header("Location: http://".$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF']); просим браузер клиента загрузить наш скрипт еще раз. Зачем? Дело в том, что если гостевая будет выведена сразу же просле обработки полученых данных, а пользователь захочет обновить страницу, его браузер снова пошлет все данные. Скрипт снова их обработает и у нас будет 2 одинаковых сообщения, вместо одного. Или больше. Это называется флуд (от англ. flood - наводнение). Можно сделать это иначе, с помощью тега HTML <META>. Для этого пользователю нужно выдать страницу с сообщением об успешной отправке и внутри блока <HEAD></head> вставить <meta http-equiv="refresh" content="задержка" url="http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'"> Задержка - время в секундах, после которого браузер запросит указанную страницу. Недостаток - надо красифо оформлять страницу с сообщением :)
Кроме того, при сохранении мы заменяем все символы '<' и '>' на их коды: '<' и '>' соответственно. Это сделано для того, чтобы теги HTML, введенные пользователем отображались, а не рассматривались браузером как элементы кода со всеми вытекающими последствиями.
Этот скрипт быстрее загружает данные (не надо их оформлять) за счет чуть более медленного сохранения. Однако у него есть один недостаток: однажды сохраненные данные сложно изменить. Только если передавать весь файл. Далее будет рассмотрен скрипт, сохраняющий данные в чистом виде.
Гостевая книга Вариант-2
//Приведены функции сохранения и загрузки сообщений, а так же часть PrintForm()
function SaveMsg($author, $email, $topic, $msg)
{
$oldfile = file(FileName); //загружаем старые сообщения в массив
if( $oldfile != FALSE && count($oldfile) > 0): //если файл считался без ошибок и массив не пуст
$reccount = $oldfile[0] + 1; //номер новой записи - номер старой +1
else: //(номер всегда в первой строке)
$reccount = 1; //иначе это первая запись
endif;
if (get_magic_quotes_gpc()):
$author = stripslashes($author); //все как всегда
$email = stripslashes($email);
$topic = stripslashes($topic);
$msg = stripslashes($msg);
endif;
$search = array('<', '>');
$replace = array('<', '>');
$author = str_replace($search, $replace, $author);
$email = str_replace($search, $replace, $email);
$topic = str_replace($search, $replace, $topic);
$msg = str_replace($search, $replace, $msg);
$fd = @fopen(FileName, 'w')
or die('<B>Не удалось открыть файл сообщений для записи.</b>');
fwrite($fd, "$reccount\r\n"); //сохраняем номер сообщения
fwrite($fd, "$author\r\n"); //автора
fwrite($fd, "$email\r\n"); //адрес почты
fwrite($fd, "$topic\r\n"); //тему
fwrite($fd, date('d.m.Y-H:i')."\r\n"); //дату сообщения
fwrite($fd, "$msg\r\n"); //сообщение
fwrite($fd, ">>>\r\n"); //метку конца сообщения
fwrite($fd, implode('', $oldfile)); //дописываем остальной файл
fclose($fd);
}
function GetMessage($fd) //Функция извлекает из файла сообщение
{
$recnum = fgets($fd); //считываем номер записи
$author = fgets($fd); //автора
$mail = fgets($fd); //"мыло"
$topic = fgets($fd); //тему
$date = fgets($fd); //дату
//если хоть одно поле не считалось, загрузка неудачна, выходим, возвращаем FALSE
if ($recnum == FALSE || $author == FALSE || $mail == FALSE ||
$topic == FALSE || $date == FALSE): return(FALSE); endif;
$tmp = fgets($fd); //читаем строку
while ($tmp != ">>>\r\n" && $tmp != FALSE): //пока читается и прочитали не метку
$msg[] = trim($tmp); //обрезаем символы конца строки и вносим в массив
$tmp = fgets($fd); //считываем новую строку и повторяем процесс
endwhile;
$res = array('recnum' => trim($recnum), //собираем все в массив
'author' => trim($author),
'mail' => trim($mail),
'topic' => trim($topic),
'date' => trim($date),
'msg' => $msg
);
return($res); //и возвращаем
}
//часть функции PrintForm(). Нужно еще выводить саму форму и последние теги.
print("<TABLE border = '0' width = '760' align = 'center'>\r\n");
$fd = @fopen(FileName, "r") //открываем файл для чтения
or die('<B>Не удалось открыть файл сообщений.</b>');
$item = GetMessage($fd); //получаем первое сообщение
while ($item != FALSE): //пока сообщения "получаются"
$a = ($item['mail'] != "") ? "<A href = 'mailto:" . $item['mail'] . "'>" .
$item['author'] . "</a>" : $item['author'];
print("<TR bgcolor='".frontcolor."'> //красиво их выводим
<TD width = '18%' align = 'center'><B>{$item['date']}</b></td>
<TD width = '17%' align = 'center'>{$a}</td><TD width = '65%'>{$item['topic']}</td>
</tr>
<TR bgcolor='".bkcolor."'>
<TD colspan = '3'><TABLE width = '98%' align = 'center'><TR><TD>\r\n");
foreach ($item['msg'] as $m):
print(" <P>". $m ."</p>\r\n");
endforeach;
print(" </td></tr></table></td>
</tr>");
$item = GetMessage($fd); //берем следующее сообщение
endwhile;
Вот так. Теперь по номеру можно однозначно определять сообщение, и, к примеру, загружать его в форму для редактирования. Или просто удалить. Кроме того, можно сделать постраничный вывод, поскольку файл явно разбивается на сообщения. Правда возрасли расходы на оформление текста. Кстати, есть идея использовать в качестве меток комментарии HTML. Таким образом можно и сохранять уже оформленые сообщения и неплохо различать их. Попробуете сделать? :)
| |