web - tec / PHP 5 для начинающи
.pdf472 Глава 11
Теперь по мере того как пользователи будут перемещаться по сайту, все посеще+ ния страниц будут протоколироваться.
Рассмотрим объявление функции, которая проверяет отправку пользователем своего идентификатора. Если пользователь не передал идентификатор, то необходи+ мо вызывать функцию login_form(), ту же самую, которая использовалась в auth_user.php (нужно просто перенести код этой функции в данный файл; здесь ее код не показывается):
function do_authentication() {
global $default_dbname, $user_tablename, $access_log_tablename; global $MYSQL_ERROR, $MYSQL_ERRNO;
global $filename; global $PHP_SELF;
if(!isset($_POST['userid'])) { login_form();
exit;
Если сценарий получил переданные пользователем идентификатор и пароль, то эти значения используются в запросе к базе данных. Здесь также определяются пере+ менные сеанса. Переменные сеанса сохраняются между запросами страниц.
}else {
$_SESSION['userpassword'] = $_POST['userpassword']; $_SESSION['userid'] = $_POST['userid']; }
$userid = $_POST['userid']; $userpassword = $_POST['userpassword']; $link_id = db_connect($default_dbname);
$query = "SELECT username FROM $user_tablename
WHERE userid = '$userid' AND userpassword = password('$userpassword')"; $result = mysql_query($query);
}
Если запрос был выполнен неудачно (например, если пользователь вводит только свое имя), то сеанс аннулируется и пользователю выводится сообщение о том, что он не прошел авторизацию:
if(!mysql_num_rows($result)) {
session_unregister("userid");
echo "Попытка авторизации неудачна. " .
"Вы должны ввести допустимый идентификатор пользователя и пароль. " . "Чтобы попытаться снова, щелкните следующую ссылку.<BR>\n";
echo "<A HREF=\"$PHP_SELF\"> Вход в систему</A><BR>";
echo "Если Вы еще не зарегистрированы, для регистрации щелкните следующую ссылку." .
"<BR>\n";
echo "<A HREF= \"register.php\">Зарегистрироваться</A>"; exit;
В случае успешного выполнения запроса необходимо проверить, посещал ли поль+ зователь данную страницу раньше, и либо вставить, либо обновить соответствующие значения в таблице:
}else{
$query = "SELECT userid FROM $access_log_tablename WHERE page = '$filename' AND userid = '$userid'"; $result = mysql_query($query); if(!mysql_num_rows($result)) {
$query = "INSERT INTO $access_log_tablename VALUES ('$filename', '$userid', 1, NULL)";
Использование PHP для управления информацией в базах данных MySQL 473
}else{
$query = "UPDATE $access_log_tablename
SET visitcount = visitcount + 1, accessdate = NULL WHERE page = '$filename' AND userid = '$userid'";
}
mysql_query($query);
$num_rows = mysql_affected_rows($link_id); if($num_rows != 1) die(sql_error());
}
}
Если вызвать этот сценарий в браузере, то будет выведена пустая страница (предполагается, что пользователь вводит корректный идентификатор и пароль). Од+ нако если изучить информацию в таблице access_log, то станет ясно, что это посе+ щение было запротоколировано. В приведенном ниже примере администратор сайта открыл страницу access_logger.php, чтобы убедиться в ее работоспособности:
mysql> select * from access_log; +———------------———-+----——--+—--------———+——----------———-+ | page | userid | visitcount | accessdate | +------------——————-+----——--+——----------------------————-+
| stats.html |
| Brian |
| |
1 |
| 20040304143102 | |
|
| score.html |
| Pads |
| |
2 |
| 20040312160114 | |
|
| refs.html |
| Nicrot | |
5 |
| 20040124122744 |
| |
|
| log.html |
| Nicrot | |
3 |
| 20040124122642 |
| |
|
| log.html |
| Greeny | |
4 |
| 20040124112654 |
| |
|
| access_logger.php |
| Dodge |
| |
1 |
| 20040314192430 |
| |
+------------——————-+----——--+——--------——+——--------------+ 6 rows in set (0.00 sec)
Создание сценария для управления пользователями
Наконец, можно собрать все примеры сценариев вместе и усовершенствовать сцена+ рий просмотра записей (созданный в предыдущей главе) так, чтобы он позволял управ+ лять данными в связанных таблицах. Используя следующий сценарий userman.php, можно редактировать и удалять регистрационные записи пользователей и/или соот+ ветствующие записи о посещении страниц.
Файл userman.php
Данный сценарий начинается, как и register.php, с выборки вариантов для
ENUM+поля userposition:
<?php
//userman.php
include_once "./common_db.inc";
$link_id = db_connect(); mysql_select_db("sample_db");
$position_array = enum_options('userposition', $link_id); mysql_close($link_id);
474 Глава 11
Функция user_message()
Функция user_message() сообщает о результатах заданной операции. Если ей был передан необязательный аргумент URL, то она загружает определенную страницу:
function user_message($msg, $url =")
{
html_header();
if(empty($url)){
echo "<SCRIPT>alert(\"$msg\");history.go(-1)</SCRIPT>"; }else{
echo "<SCRIPT>alert(\"$msg\");self.location.href='$url'</SCRIPT>";
}
html_footer(); exit;
}
Функция list_records()
В переработанной функции list_records() появляется возможность удалять определенную запись. Остальная часть функции такая же, как и в предыдущей ее вер+ сии (в главе 10):
function list_records() {
...
echo "<td WIDTH=\"25%\" ALIGN=\"CENTER\">
<a href=\"javascript:open_window('$PHP_SELF?action=view_record&
userid=$userid');\">
Просмотреть запись</a>
<a href=\"$PHP_SELF?action=delete_record&userid=$userid\"
onClick=\"return confirm('Вы уверены?');\">
Удалить запись</a>
</td>\n"; echo "</tr>\n";
...
}
Функция delete_record()
Функция delete_record() удаляет запись определенного пользователя из таб+ лицы user, а также соответствующие ей записи из таблицы access_log:
function delete_record()
{
global $default_dbname, $user_tablename, $access_log_tablename; $userid = $_GET['userid'];
if(empty($userid)){
error_message('Введите идентификатор пользователя!');
}
$link_id = db_connect($default_dbname); if(!$link_id){
error_message(sql_error());
}
$query = "DELETE FROM $user_tablename WHERE userid = '$userid'"; $result = mysql_query($query);
if(!$result){ error_message(sql_error());
Использование PHP для управления информацией в базах данных MySQL 475
}
$num_rows = mysql_affected_rows($link_id);
if($num_rows != 1){
error_message("Пользователя $userid не существует");
}
$query = "DELETE FROM $access_log_tablename WHERE userid = '$userid'"; $result = mysql_query($query);
user_message("Все записи, относящиеся к пользователю $userid удалены!");
}
Функция edit_record()
Функция edit_record() обновляет запись определенного пользователя в табли+ це user. Если изменяется идентификатор пользователя, то она также обновляет свя+ занные записи в таблице access_log, отражая это изменение:
function edit_record()
{
global $default_dbname, $user_tablename, $access_log_tablename; $PHP_SELF = $_SERVER['PHP_SELF'];
$userid = $_GET['userid']; $newuserid = $_GET['new_userid']; $username = $_GET['username'];
$userpassword = $_GET['userpassword']; $userposition = $_GET['userposition']; $useremail = $_GET['useremail']; $userprofile = $_GET['userprofile'];
if(empty($userid)){
$userid = $_GET['new_userid'];
}
$link_id = db_connect($default_dbname); if(!$link_id){
error_message(sql_error());
}
$field_str = ";
Действительно, значение поля userid тоже можно изменить. С точки зрения ад+ министраторов баз данных, как правило, недопустимо давать пользователю возмож+ ность изменять свой идентификатор, так как он (идентификатор) выполняет функ+ ции номера социального страхования (Social Security number) ++++++ он уникален и одновременно неизменен. Тем не менее, если бы пользователь настаивал на изме+ нении своего идентификатора, то пришлось бы изменить каждую запись в каждой связанной таблице независимо от количества этих записей, или, в конце концов, в ба+ зе данных накопилось бы множество бесполезных записей, не имеющих владельца.
Новый идентификатор пользователя следует сравнить с существующим и в случае необходимости обновить переменную $field_str:
if($userid != $new_userid) $field_str = " userid = '$newuserid', ";
Если переменная $userpassword содержит какое+либо значение, то необходимо обновить поле userpassword:
if(!empty($userpassword)) {
$field_str .= " userpassword = password('$userpassword'), ";
}
476 Глава 11
Подобная проверка не позволит администратору случайно оставить в поле userpassword пустое значение. Затем необходимо обновить остальные поля и про+ верить, все ли идет по плану:
$field_str .= " username = '$username', "; $field_str .= " userposition = '$userposition', "; $field_str .= " useremail = '$useremail', "; $field_str .= " userprofile = '$userprofile'";
$query = "UPDATE IGNORE $user_tablename SET $field_str WHERE userid = '$userid'"; $result = mysql_query($query);
if(!$result){ error_message(sql_error());
}
$num_rows = mysql_affected_rows($link_id);
if(!$num_rows){
error_message("Ни одна запись не изменена!");
}
Следует отметить использование в данном запросе оператора IGNORE. Поле userid является частью первичного ключа и MySQL препятствует его изменению, а оператор IGNORE позволяет продолжить выполнение запроса и внести изменения. Кроме того, чтобы база данных была согласованной, для отражения изменений необходимо обно+ вить все остальные таблицы.
Если значение поля userid изменяется, то функция edit_record соответствую+ щим образом обновляет все связанные записи в таблице access_log:
if($userid != $new_userid){
$query = "UPDATE $access_log_tablename SET userid = '$newuserid' WHERE userid = '$userid'";
$result = mysql_query($query); if(!$result){
error_message(sql_error());
}
user_message("Все записи, связанные с $userid, были изменены!", "$PHP_SELF?action=view_record&userid=$newuserid");
}else{
user_message("Все записи, связанные с $userid, были изменены!", "$PHP_SELF?action=view_record&userid=$userid");
}
}
Если поле userid было изменено, то вызывается функция user_message(), вто+ рой аргумент которой определяет часть URL userid=$new_userid, так как возвра+ щение в сценарий со старым значением userid привело бы к неожиданному резуль+ тату ++++++ выводу формы для редактирования данных несуществующего пользователя.
Функция edit_log_record()
Функция edit_log_record() обновляет в таблице access_log записи о посе+ щениях страниц для заданного пользователя:
function edit_log_record() {
global $default_dbname, $access_log_tablename; $userid = $_GET['userid'];
$newpage = $_GET['new_page']; $visitcount = $_GET['visitcount'];
Использование PHP для управления информацией в базах данных MySQL 477
$accessdate = $_GET['accessdate']; $orgpage = $_GET['org_page']; $PHP_SELF = $_SERVER['PHP_SELF'];
if(empty($userid)){
error_message('Введите идентификатор пользователя!');
}
$link_id = db_connect($default_dbname); if(!$link_id){
error_message(sql_error());
}
$field_str = ";
$field_str .= " page = '$newpage', "; $field_str .= " visitcount = '$visitcount', "; $field_str .= " accessdate = '$accessdate' ";
Данная функция работает почти так же, как функция edit_record(). Основное отличие заключается в том, что функция edit_log_record() использует для выбора соответствующих записей таблицы access_log два поля: userid и page.
Существующее значение поля page сохраняется в переменной $org_page на слу+ чай, если администратор изменит значение этого поля в форме для редактирования:
$query = "UPDATE $access_log_tablename SET $field_str WHERE userid = '$userid' AND page = '$orgpage'";
$result = mysql_query($query); if(!$result){
error_message(sql_error());
}
Если ни одна запись не обновилась, то это значит, что администратор нажал кноп+ ку Отправить, ничего не изменив в соответствующей форме для редактирования.
Функция mysql_affected_rows() возвращает 0, так как в результате предыду+ щей UPDATE+операции ни одна запись не изменилась. Команда UPDATE не выполняет никаких изменений, если новая запись содержит те же значения, что и существующая:
$num_rows = mysql_affected_rows($link_id);
if(!$num_rows){
error_message("Данные не изменены!");
}
user_message("Все записи, относящиеся к пользователю $userid, были изменены!", "$PHP_SELF?action=view_record&userid=$userid");
}
Функция view_record()
Усовершенствованная версия функции view_record() позволяет администрато+ ру редактировать пользовательские записи:
function view_record() {
global $default_dbname, $user_tablename, $access_log_tablename; global $position_array; $userid = $_GET['userid']; $PHP_SELF = $_SERVER['PHP_SELF'];
if(empty($userid)){
error_message('Введите идентификатор пользователя!');
478 Глава 11
}
$link_id = db_connect($default_dbname);
if(!$link_id){ error_message(sql_error());
}
$query = "SELECT usernumber, userid, username, userposition, useremail, userprofile FROM $user_tablename WHERE userid = '$userid'";
$result = mysql_query($query); if(!$result){
error_message(sql_error());
}
$query_data = mysql_fetch_array($result); $usernumber = $query_data["usernumber"]; $userid = $query_data["userid"]; $username = $query_data["username"];
$userposition = $query_data["userposition"]; $useremail = $query_data["useremail"]; $userprofile = $query_data["userprofile"];
Наконец, отображается несколько форм, с помощью которых администратор мо+ жет редактировать пользовательские записи и данные о посещении страниц:
html_header();
echo "<center><H3>
Запись для пользователя №. $usernumber - $userid($username) </h3></center>";
?>
<form method="get" action="<?php echo $PHP_SELF ?>"> <input type="hidden" name="action" value="edit_record">
<input type="hidden" name="userid" value="<? echo $userid ?>"> <div align="center"><center>
<table border="1" width="90%" cellpadding="2">
<tr>
<th width="30%" nowrap>Идентификатор</th>
Скрытое поле new_userid используется в случае, если администратор изменяет значение поля userid:
<td width="70%">
<input type="text" name="new_userid" value="<?php echo $userid ?>" size="8" maxlength="8"></td>
</tr>
<tr>
<th width="30%" nowrap>Пароль</th>
Зашифрованный пароль не выводится, так как он не используется:
<td width="70%"><input type="text" name="userpassword" size="15"></td> </tr>
<tr>
<th width="30%" nowrap>Полное имя</th>
<th width="70%"><input type="text" name="username"
value="<?php echo $username ?>" SIZE="20"></td>
</tr>
<tr>
<th width="30%" nowrap>Позиция</th>
<td width="70%"><select name="userposition" size="1">
<?php
Использование PHP для управления информацией в базах данных MySQL 479
Для создания выпадающего списка возможных позиций используется переменная $position_array:
for($i=0; $i < count($position_array); $i++) { if(!isset($userposition) && $i == 0) {
echo "<OPTION SELECTED VALUE=\"". $position_array[$i] . "\">" . $position_array[$i] . "</OPTION>\n";
}else if($userposition == $position_array[$i]) {
echo "<OPTION SELECTED VALUE=\"". $position_array[$i] . "\">" . $position_array[$i] . "</OPTION>\n";
}else{
echo "<OPTION VALUE=\"". $position_array[$i] . "\">" . $position_array[$i] . "</OPTION>\n";
}
}
?>
</select></td>
</tr>
<tr>
<th width="30%" nowrap>E-mail-адрес</th>
<td width="70%"><input type="text" name="useremail" size="20" value="<?php echo $useremail ?>"></td>
</tr>
<tr>
<th width="30%" nowrap>Профиль</th>
Функция htmlspecialchars() гарантирует, что любые специальные HTML+ символы в переменной $userprofile выводятся в виде HTML+последовательностей и не способны нарушить окружающую разметку:
<td width="70%">
<textarea rows="5" cols="40" name="userprofile"> <?php echo htmlspecialchars($userprofile) ?>
</textarea>
</td>
</tr>
<tr>
<th width="100%" colspan="2" nowrap> <input type="submit" value="Записать"> <input type="reset" value="Очистить">
</th>
</tr>
</table>
</center></div>
</form>
<?php
echo "<HR SIZE=\"2\" WIDTH=\"90%\">\n";
Каждая запись из таблицы access_log представляется в отдельной форме:
$query = "SELECT page, visitcount, accessdate, date_format(accessdate, '%M, %e, %Y') as formatted_accessdate FROM $access_log_tablename WHERE userid = '$userid'";
$result = mysql_query($query);
if(!$result){ error_message(sql_error());
}
if(!mysql_num_rows($result)){
echo "<center>У пользователя $userid ($username) нет записей о посещении страниц.</center>";
}else{
echo "<center>Записи о посещении страниц пользователем $userid ($username).</center>";
480 Глава 11
}
?>
<div align="center"><center>
<table border="1" width="90%" cellpadding="2">
<tr>
<th width="20%" nowrap>Страница</th> <th width="20%" nowrap>Посещения</th>
<th width="30%" nowraP>Последнее посещение</th> <th width="30%" nowrap>Действие</th>
</tr>
<?php
Результаты данного запроса выводятся в хорошо отформатированной таблице, которую администратор может использовать для изменения access_log+значений:
while($query_data = mysql_fetch_array($result)){ $page = $query_data["page"];
$visitcount = $query_data["visitcount"]; $accessdate = $query_data["accessdate"];
$formatted_accessdate = $query_data["formatted_accessdate"];
echo "<FORM METHOD=\"GET\" ACTION=\"$PHP_SELF\">";
echo "<INPUT TYPE=\"HIDDEN\" NAME=\"action\"VALUE=\"edit_log_record\">"; echo "<INPUT TYPE=\"HIDDEN\" NAME=\"userid\" VALUE=\"$userid\">";
echo "<INPUT TYPE=\"HIDDEN\" NAME=\"org_page\" VALUE=\"$page\">"; echo "<TR>\n";
echo "<TD WIDTH=\"20%\"><INPUT TYPE=\"TEXT\"NAME=\"new_page\" SIZE=\"30\" VALUE=\"$page\"></TD>\n";
echo "<TD WIDTH=\"20%\" ALIGN=\"CENTER\">
<INPUT TYPE=\"TEXT\" NAME=\"visitcount\" SIZE=\"3\" VALUE=\"$visitcount\"></TD>\n";
echo "<TD WIDTH=\"30%\" ALIGN=\"CENTER\">
<INPUT TYPE=\"TEXT\" NAME=\"accessdate\" SIZE=\"14\" MAXLENGTH=\"14\" VALUE=\"$accessdate\"> <BR>$formatted_accessdate</TD>\n";
echo "<TD WIDTH=\"30%\" ALIGN=\"CENTER\"> <INPUT TYPE=\"SUBMIT\" VALUE=\"Записать\">
<INPUT TYPE=\"RESET\" VALUE=\"Очистить\"></TD>\n"; echo "</TR>\n";
echo "</FORM>\n";
}
?>
</tr>
</table>
</center></div>
<?php html_footer();
}
Выбор действия
Наконец, чтобы определить, какая функция должна быть вызвана далее, использу+ ется переменная $action:
if (empty($_GET['action'])){ $_GET['action'] = "";
}
switch($_GET['action']) { case "edit_record": edit_record();
break;
case "edit_log_record": edit_log_record(); break;
Использование PHP для управления информацией в базах данных MySQL 481
case "delete_record": delete_record(); break;
case "view_record": view_record(); break;
default: list_records();
break;
}
?>
Предположим, что администратору нужно вывести список записей, отсортиро+ ванных по номеру пользователя. На рис. 11.11 показана страница после нажатия на заголовок соответствующего поля:
Рис. 11.11.
Если щелкнуть на ссылке Просмотреть запись, то откроется новое окно, в кото+ ром будут показаны записи, относящиеся к пользователю, связанному с данной ссыл+ кой (рис. 11.12).