Скрипт форума на PHP своими руками. Часть 3


Прислал: Евгений Токмаков [ 03.07.2008 @ 12:50 ]
Раздел:: [ Статьи по PHP ]


Мы разобрались с показом главной страницы, отдельного форума и конкретной темы. Теперь займемся пользователями сайта. И первый вопрос, который мы рассмотрим - регистрация нового пользователя. За это отвечают три функции: getAddNewUserForm(), addNewUser() и activateUser(). Рассмотрим их все по порядку.

Функция getAddNewUserForm() возвращает html формы для регистрации нового пользователя:

<?php
// Функция возвращает форму для регистрации нового пользователя на форуме
function getAddNewUserForm()
{
  if ( isset( 
$_SESSION['captcha_keystring'] ) ) unset( $_SESSION['captcha_keystring'] );
  
$html '';
  
// Если при заполнении формы были допущены ошибки
  
if ( isset( $_SESSION['addNewUserForm'] ) ) {
    
$info file_get_contents'./templates/infoMessage.html' );
    
$info str_replace'{infoMessage}'$_SESSION['addNewUserForm']['error'], $info );
    
$html $html.$info."n";
    
$name      htmlspecialchars$_SESSION['addNewUserForm']['name'] );
    
$email     htmlspecialchars$_SESSION['addNewUserForm']['email'] );
    
$timezone  $_SESSION['addNewUserForm']['timezone'];
    
$icq       htmlspecialchars$_SESSION['addNewUserForm']['icq'] );
    
$url       htmlspecialchars$_SESSION['addNewUserForm']['url'] );
    
$about     htmlspecialchars$_SESSION['addNewUserForm']['about'] );
    
$signature htmlspecialchars$_SESSION['addNewUserForm']['signature'] );
    unset( 
$_SESSION['addNewUserForm'] );
  } else {
    
$name      '';
    
$email     '';
    
$timezone  0;
    
$icq       '';
    
$url       '';
    
$about     '';
    
$signature '';
  }
  
  
// Считываем в переменную файл шаблона, содержащего 
  // форму для добавления нового пользователя
  
$tpl file_get_contents'./templates/addNewUserForm.html' );
  
$action $_SERVER['PHP_SELF'].'?action=addNewUser';
  
$tpl str_replace'{action}'$action$tpl);
  
$tpl str_replace'{name}'$name$tpl);
  
$tpl str_replace'{email}'$email$tpl);
  
$tpl str_replace'{icq}'$icq$tpl);
  
$tpl str_replace'{url}'$url$tpl);
  
$tpl str_replace'{about}'$about$tpl);
  
$tpl str_replace'{signature}'$signature$tpl);
  
$kcaptcha './kcaptcha/kc.php?'.session_name().'='.session_id();
  
$tpl str_replace'{kcaptcha}'$kcaptcha$tpl);
  
$tpl str_replace'{keystring}'''$tpl);
  
  
$options '';
  for ( 
$i = -12$i <= 12$i++ ) {
    if ( 
$i 
      
$value $i.' часов';
    else
      
$value '+'.$i.' часов';
    if ( 
$i == $timezone )
      
$options $options '<option value="'.$i.'" selected>'.$value.'</option>'."n";
    else
      
$options $options '<option value="'.$i.'">'.$value.'</option>'."n";
  }
  
$tpl str_replace'{options}'$options$tpl);
  
$tpl str_replace'{servertime}'date"d.m.Y H:i:s" ), $tpl );
  
  
$html $html $tpl;
  return 
$html;
}
?>

Файл шаблона addNewUserForm.html:

<form name="addNewUserForm" action="{action}" method="POST" enctype="multipart/form-data">

<table class="showTable">
<tr>
  <th colspan="2">Регистрация</th>
</tr>
<tr>
  <td width="40%"><strong>Имя:</strong></td>
  <td width="60%"><input type="text" name="name" maxlength="30" value="{name}" /></td>
</tr>
<tr>
  <td><strong>Адрес e-mail:</strong></td>
  <td><input type="text" name="email" maxlength="60" value="{email}" /></td>
</tr>
<tr>
  <td><strong>Пароль:</strong></td>
  <td><input type="password" name="password" maxlength="30" value="" /></td>
</tr>
<tr>
  <td><strong>Подтвердите пароль:</strong></td>
  <td><input type="password" name="confirm" maxlength="30" value="" /></td>
</tr>
<tr>
  <td><strong>Номер в ICQ:</strong></td>
  <td><input type="text" name="icq" maxlength="12" value="{icq}" /></td>
</tr>
<tr>
  <td><strong>Домашняя страничка:</strong></td>
  <td><input type="text" name="url" maxlength="60" value="{url}" /></td>
</tr>
<tr>
  <td><strong>Интересы:</strong></td>
  <td><textarea name="about" cols="50" rows="3">{about}</textarea></td>
</tr>
<tr>
  <td>
  <strong>Подпись:</strong><br/>
  Это текст, который будет добавлен к размещаемым вами сообщениям.
  </td>
  <td><textarea name="signature" cols="50" rows="3">{signature}</textarea></td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td><img src="{kcaptcha}" alt="" /></td>
</tr>
<tr>
  <td><strong>Код подтверждения:</strong></td>
  <td><input type="text" name="keystring" maxlength="6" value="{keystring}" /></td>
</tr>
<tr>
  <td>
  <strong>Коррекция временных зон:</strong><br/>
  Введите необходимую разницу в часах относительно времени на форуме, и все время будет отображаться с учетом
  разницы в вашей временной зоне.
  </td>
  <td>
  <select name="timezone">
  {options}
  </select>
  Текущее время на сервере: {servertime}
  </td>
</tr>
<tr>
  <td>
  <strong>Аватар:</strong><br/>
  Показывает небольшое изображение под информацией о вас в сообщениях.
  </td>
  <td>
  <input type="file" name="avatar" />
  </td>
</tr>
<tr>
  <td colspan="2" align="center"><input type="submit" name="submitForm" value="Отправить" /></td>
</tr>
</table>

<form>

Далее в нашем списке - функция addNewUser(), которая добавляет нового пользователя (т.е. новую запись в таблицу TABLE_USERS):

<?php
// Функция добавляет нового пользователя форума (запись в таблице БД TABLE_USERS)
function addNewUser()
{

  
// Если не переданы данные формы - значит функция была вызвана по ошибке
  
if ( !isset( $_POST['name'] ) or
       !isset( 
$_POST['password'] ) or
       !isset( 
$_POST['confirm'] ) or
       !isset( 
$_POST['email'] ) or
       !isset( 
$_POST['timezone'] ) or
       !isset( 
$_POST['icq'] ) or
       !isset( 
$_POST['url'] ) or
       !isset( 
$_POST['about'] ) or
       !isset( 
$_POST['signature'] ) or
       !isset( 
$_POST['keystring'] ) or
       !isset( 
$_FILES['avatar'] ) 
    )
  {
    
header'Location: '.$_SERVER['PHP_SELF'].'?action=addNewUserForm' );
    die();
  }

  
// Обрезаем переменные до длины, указанной в параметре maxlength тега input
  
$name      substr$_POST['name'], 030 );
  
$password  substr$_POST['password'], 030 );
  
$confirm   substr$_POST['confirm'], 030 );
  
$email     substr$_POST['email'], 060 );
  
$icq       substr$_POST['icq'], 012 );
  
$url       substr$_POST['url'], 060 );
  
$about     substr$_POST['about'], 01000 );
  
$signature substr$_POST['signature'], 0500 );
  
$keystring substr$_POST['keystring'], 0);

  
// Обрезаем лишние пробелы
  
$name      trim$name );
  
$password  trim$password );
  
$confirm   trim$confirm );
  
$email     trim$email );
  
$icq       trim$icq );
  
$url       trim$url );
  
$about     trim$about );
  
$signature trim$signature );
  
$keystring trim$keystring );

  
// Проверяем, заполнены ли обязательные поля
  
$error '';
  if ( empty( 
$name ) ) $error $error.'<li>не заполнено поле "Имя"</li>'."n";
  if ( empty( 
$password ) ) $error $error.'<li>не заполнено поле "Пароль"</li>'."n";
  if ( empty( 
$confirm ) ) $error $error.'<li>не заполнено поле "Подтвердите пароль"</li>'."n";
  if ( empty( 
$email ) ) $error $error.'<li>не заполнено поле "Адрес e-mail"</li>'."n";
  if ( empty( 
$keystring ) ) $error $error.'<li>не заполнено поле "Код"</li>'."n";
  
// Проверяем, не слишком ли короткий пароль
  
if ( !empty( $password ) and strlen$password ) < MIN_PASSWORD_LENGTH )
    
$error $error.'<li>длина пароля должна быть не меньше '.MIN_PASSWORD_LENGTH.' символов</li>'."n";
  
// Проверяем, совпадают ли пароли
  
if ( !empty( $password ) and !empty( $confirm ) and $password != $confirm 
    
$error $error.'<li>не совпадают пароли</li>'."n";
  
// Проверяем поле "код"
  
if ( !empty( $keystring ) ) {
    
// Проверяем поле "код" на недопустимые символы
    
if ( !ereg"[23456789abcdeghkmnpqsuvxyz]+"$keystring ) )
      
$error $error.'<li>поле "Код" содержит недопустимые символы</li>'."n";
    
// Проверяем, совпадает ли код с картинки
    
if ( !isset( $_SESSION['captcha_keystring'] ) or $_SESSION['captcha_keystring'] != $keystring )
      
$error $error.'<li>не совпадает код с картинки</li>'."n";
  }
  unset( 
$_SESSION['captcha_keystring'] );

  
// Проверяем поля формы на недопустимые символы
  
if ( !empty( $name ) and !preg_match"#^[- _0-9a-zа-я]+$#i"$name ) )
    
$error $error.'<li>поле "Имя" содержит недопустимые символы</li>'."n";
  if ( !empty( 
$password ) and !preg_match"#^[-_0-9a-z]+$#i"$password ) )
    
$error $error.'<li>поле "Пароль" содержит недопустимые символы</li>'."n";
  if ( !empty( 
$confirm ) and !preg_match"#^[-_0-9a-z]+$#i"$confirm ) )
    
$error $error.'<li>поле "Подтвердите пароль" содержит недопустимые символы</li>'."n";
  if ( !empty( 
$icq ) and !preg_match"#^[0-9]+$#"$icq ) )
    
$error $error.'<li>поле "ICQ" содержит недопустимые символы</li>'."n";
  if ( !empty( 
$about ) and !preg_match"#^[-[].;:,?!/)(_"s0-9а-яa-z]+$#i"$about ) )
    
$error $error.'<li>поле "Интересы" содержит недопустимые символы</li>'."n";
  if ( !empty( 
$signature ) and !preg_match"#^[-[].;:,?!/)(_"s0-9а-яa-z]+$#i"$signature ) )
    
$error $error.'<li>поле "Подпись" содержит недопустимые символы</li>'."n";

  
// Проверяем корректность e-mail
  
if ( !empty( $email ) and !preg_match"#^[0-9a-z_-.]+@[0-9a-z-.]+.[a-z]{2,6}$#i"$email ) )
    
$error $error.'<li>поле "Адрес e-mail" должно соответствовать формату somebody@somewhere.ru</li>'."n";
    
  
// Проверяем корректность URL домашней странички    
  
if ( !empty( $url ) and !preg_match"#^(//)?(www.)?[-0-9a-z]+.[a-z]{2,6}/?$#i"$url ) )
    
$error $error.'<li>поле "Домашняя страничка" должно соответствовать формату //www.homepage.ru</li>'."n";

  
// Выясняем не зарегистрировано ли уже это имя
  // Возможно три ситуации, которые необходимо предотвратить:
  // 1. Вводится ник, полностью совпадающий с уже существующим
  // 2. Вводится уже существующий кирилический ник, в котором
  //    одна или несколько букв заменены на латинские
  // 3. Вводится уже существующий латинский ник, в котором
  //    одна или несколько букв заменениы на кирилические
  
  // Массив кирилических букв
  
$rus = array( "А","а","В","Е","е","К","М","Н","О","о","Р","р","С","с","Т","Х","х" );
  
// Массив латинских букв
  
$eng = array( "A","a","B","E","e","K","M","H","O","o","P","p","C","c","T","X","x" );
  
$new_name preg_replace"#[^- _0-9a-zа-я]#i"""$name );
  
// Заменяем русские буквы латинскими
  
$eng_new_name str_replace$rus$eng$new_name ); 
  
// Заменяем латинские буквы русскими
  
$rus_new_name str_replace$eng$rus$new_name ); 
  
// Формируем SQL-запрос
  
$query "SELECT * FROM ".TABLE_USERS.
            WHERE name LIKE '"
.mysql_real_escape_string$new_name )."' OR
                  name LIKE '"
.mysql_real_escape_string$eng_new_name )."' OR
                  name LIKE '"
.mysql_real_escape_string$rus_new_name )."';";
  
$res mysql_query$query );

  if ( !
$res ) {
    
$msg 'Ошибка при регистрации нового пользователя';
    
$err 'Ошибка при выполнении запроса: <br/>'.
           
$query.'<br/>'.mysql_errno().':&nbsp;'.mysql_error().'<br/>'.
           
'(Файл '__FILE__ .', строка '__LINE__ .')';
    return 
showErrorMessage$msg$errtrue'' );
  }

  if ( 
mysql_num_rows$res ) > )
    
$error $error.'<li>имя "'.$new_name.'" уже зарегистрировано</li>'."n";

  if ( !empty( 
$_FILES['avatar']['name'] ) ) {
    
$ext strrchr$_FILES['avatar']['name'], "." );
    
$extensions = array( ".jpg"".gif"".bmp"".png" );
    if ( !
in_array$ext$extensions ) ) 
      
$error $error.'<li>недопустимый формат файла аватара</li>'."n";
    if ( 
$_FILES['avatar']['size'] > MAX_AVATAR_SIZE 
      
$error $error.'<li>размер файла аватора больше '.(MAX_AVATAR_SIZE/1024).' Кб</li>'."n";
  }

  
$timezone = (int)$_POST['timezone'];
  if ( 
$timezone < -12 or $timezone 12 $timezone 0;

  
// Если были допущены ошибки при заполнении формы - перенаправляем посетителя на страницу регистрации
  
if ( !empty( $error ) ) {
    
$_SESSION['addNewUserForm'] = array();
    
$_SESSION['addNewUserForm']['error'] = '<p class="errorMsg">При заполнениии формы были допущены ошибки:</p>'.
    
"n".'<ul class="errorMsg">'."n".$error.'</ul>'."n";
    
$_SESSION['addNewUserForm']['name'] = $name;
    
$_SESSION['addNewUserForm']['email'] = $email;
    
$_SESSION['addNewUserForm']['timezone'] = $timezone;
    
$_SESSION['addNewUserForm']['icq'] = $icq;
    
$_SESSION['addNewUserForm']['url'] = $url;
    
$_SESSION['addNewUserForm']['about'] = $about;
    
$_SESSION['addNewUserForm']['signature'] = $signature;
    
header'Location: '.$_SERVER['PHP_SELF'].'?action=addNewUserForm' );
    die();
  }

  if ( !empty( 
$url ) and substr($url07) != '//' $url '//'.$url;
  
  
// Уникальный код для активации учетной записи
  
$code md5uniqidrand(), ) );
  
// Все поля заполнены правильно - продолжаем регистрацию
  
$query "INSERT INTO ".TABLE_USERS."
            (
            name,
            passw,
            email,
            timezone,
            url,
            icq,
            about,
            signature,
            photo,
            puttime,
            last_visit,
            themes,
            status,
            activation
            )
            VALUES
            (
            '"
.mysql_real_escape_string$name )."',
            '"
.mysql_real_escape_stringmd5$password ) )."',
            '"
.mysql_real_escape_string$email )."',
            "
.$timezone.",
            '"
.mysql_real_escape_string$url )."',
            '"
.$icq."',
            '"
.mysql_real_escape_string$about )."',
            '"
.mysql_real_escape_string$signature )."',
            '',
            NOW(),
            NOW(),
            0,
            'user',
            '"
.$code."'
            );"
;
  
$res mysql_query$query );
  if ( !
$res ) {
    
$msg 'Ошибка при регистрации нового пользователя';
    
$err 'Ошибка при выполнении запроса: <br/>'.
           
$query.'<br/>'.mysql_errno().':&nbsp;'.mysql_error().'<br/>'.
          
'(Файл '__FILE__ .', строка '__LINE__ .')';
    return 
showErrorMessage$msg$errtrue'' );
  }

  
$id mysql_insert_id();
  if ( !empty( 
$_FILES['avatar']['name'] ) and 
       
move_uploaded_file $_FILES['avatar']['tmp_name'], './photo/'.$id ) ) chmod'./photo/'.$id0644 );

  
// Посылаем письмо пользователю с просьбой активировать учетную запись
  
$headers "From: ".$_SERVER['SERVER_NAME']." <".ADMIN_EMAIL.">n";
  
$headers $headers."Content-type: text/html; charset="windows-1251"n";
  
$headers $headers."Return-path: <".ADMIN_EMAIL.">n";
  
$message '<p>Добро пожаловать на форум '.$_SERVER['SERVER_NAME'].'!</p>'."n";
  
$message $message.'<p>Пожалуйста сохраните это сообщение. Параметры вашей учётной записи таковы:</p>'."n";
  
$message $message.'<p>Логин: '.$name.'<br/>Пароль: '.$password.'</p>'."n";
  
$message $message.'<p>Для активации вашей учетной записи перейдите по ссылке:</p>'."n";
  
$link '//'.$_SERVER['SERVER_NAME'].$_SERVER['PHP_SELF'].'?action=activateUser&code='.$code;  
  
$message $message.'<p><a href="'.$link.'">Активировать учетную запись</a></p>'."n";
  
$message $message.'<p>Не забывайте свой пароль: он хранится в нашей базе в зашифрованном 
             виде, и мы не сможем вам его выслать. Если вы всё же забудете пароль, то сможете 
             запросить новый, который придётся активировать таким же образом, как и вашу 
             учётную запись.</p>'
."n";
  
$message $message.'<p>Спасибо за то, что зарегистрировались на нашем форуме.</p>'."n";
  
$subject 'Регистрация на форуме '.$_SERVER['SERVER_NAME'];
  
$subject '=?koi8-r?B?'.base64_encode(convert_cyr_string($subject"w","k")).'?=';
  
mail$email$subject$message$headers );
  
  
$msg 'На Ваш e-mail выслано письмо с просьбой подтвердить регистрацию. 
          Чтобы завершить регистрацию и активировать учетную запись, зайдите 
          по адресу, указанному в письме.'
;
  
$html file_get_contents'./templates/infoMessage.html' );
  
$html str_replace'{infoMessage}'$msg$html );
  return 
$html;
}
?>

Последняя в нашем списке - функция activateUser(). Как нетрудно догадаться, она активирует учетную запись нового пользователя:

<?php
function activateUser()
{
  
// Если не передан параметр $code - значит функция вызвана по ошибке
  
if ( !isset( $_GET['code'] ) ) {
    
header'Location: '.$_SERVER['PHP_SELF'] );
    die();
  }
  
// Т.к. код зашифрован с помощью md5, то он представляет собой 
  // 32-значное шестнадцатеричное число
  
$code substr$_GET['code'], 032 );
  
$code preg_replace"#[^0-9a-f]#i"''$code );

  
$query "SELECT id_author 
            FROM "
.TABLE_USERS.
            WHERE activation='"
.mysql_real_escape_string$code )."' LIMIT 1";
  
$res mysql_query$query );
  if ( !
$res ) {
    
$msg 'Ошибка при активации учетной записи. Обратитесь к администратору.';
    
$err 'Ошибка при выполнении запроса: <br/>'.
           
$query.'<br/>'.mysql_errno().':&nbsp;'.mysql_error().'<br/>'.
          
'(Файл '__FILE__ .', строка '__LINE__ .')';
    return 
showErrorMessage$msg$errtrue'' );
  }
  if ( 
mysql_num_rows$res ) > ) {
    
$id mysql_result$res0);
    
$query "UPDATE ".TABLE_USERS.
              SET activation='', last_visit=NOW() 
              WHERE id_author="
.$id;
    
$res mysql_query$query );
    if ( !
$res ) {
      
$msg 'Ошибка при активации учетной записи. Обратитесь к администратору.';
      
$err 'Ошибка при выполнении запроса: <br/>'.
             
$query.'<br/>'.mysql_errno().':&nbsp;'.mysql_error().'<br/>'.
             
'(Файл '__FILE__ .', строка '__LINE__ .')';
      return 
showErrorMessage$msg$errtrue'' );
    }
  }
  return 
showInfoMessage'Ваша учетная запись активирована.''action=loginForum' );
}
?>

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

<?php
// Вспомогательная функция - после выполнения пользователем каких-либо действий
// выдает информационное сообщение и делает редирект на нужную страницу с задержкой
function showInfoMessage$message$queryString )
{
  if ( !empty( 
$queryString ) ) $queryString '?'.$queryString;
  
header'Refresh: '.REDIRECT_DELAY.'; url='.$_SERVER['PHP_SELF'].$queryString );
  
$html file_get_contents'./templates/infoMessage.html' );
  
$html str_replace'{infoMessage}'$message$html );
  return 
$html;
}
?>

Шаблон infoMessage.html

<table class="showTable">
<tr>
  <th>Информация</th>
</tr>
<tr>
  <td>
  {infoMessage}
  </td>
</tr>
</table>

Продолжение следует...