Пишем регистрацию на PHP и JavaScript — Часть 1: PHP

Всем привет! Сегодня я открываю новую рубрику под названием «Сайт с нуля», в которой буду описывать различные этапы создания сайта. В этой статье я расскажу, как можно при помощи PHP написать простую регистрацию для своего сайта. В первую очередь, данная статья пригодится новичкам, только решившим научиться писать свои сайты. Весь код, рассмотренный в данной статье, используется и в нашем самописном проекте, о котором я рассказывал на странице О чем этот блог?, и речь о котором я буду вести и в дальнейших публикациях.

Постановка задачи

Необходимо создать страницу по адресу http://ваш_сайт/registration, при входе на которую пользователю, если он ещё не зарегистрирован, выводится форма для ввода данных, необходимых для регистрации. Если же пользователь уже зарегистрирован и авторизирован на сайте, перенаправим его на главную страницу сайта так, что форму регистрации он и не увидит.

Поля для заполнения:

  • логин;
  • пароль;
  • подтверждение пароля;
  • email;
  • согласие с правилами.

Для каждого из всех полей есть правила валидации, но о них позже. Сейчас стоит заметить лишь то, что если пользователь введёт некорректные данные или не введёт их вовсе, посредством JavaScript будет выведено сообщение об ошибке.

При успешной регистрации, на страницу, по тому же адресу, будет выведено сообщение об успехе и 2 ссылки (на главную страницу сайта и на страницу заполнения профиля).

Подготовка

Для начала, создадим необходимую иерархию папок и файлов. Выглядеть она должна так:

Сейчас мы будем работать исключительно с папкой registration. Внутри неё расположен подкаталог template, в котором будут находиться все файлы, отвечающие за внешний вид модуля Регистрации (html шаблоны, css файлы и различные изображения). В этой папке создаём файл registration.php. Он будет содержать саму форму регистрации. Вкратце, она должна выглядеть так:

1
2
3
4
5
6
7
8
<form method="post" action="index.php">
Логин: <input id="login" type="text" name="login" /><br />
Пароль: <input id="pass" type="password" name="password" /><br />
Подтверждение: <input id="re_pass" type="password" name="password2" /><br />
Email: <input id="mail" type="text" name="mail" /><br />
<label><input id="no_xyz" type="checkbox" name="lic" value="ok" /> Обязуюсь не творить хуйни!<br /></label><br />
<input type="submit" name="GO" value="Регистрация">
</form>

Вся форма передаётся методом POST, что необходимо для безопасной передачи данных. Кнопка отправки имеет name=”GO”, по которому мы и будем отлавливать отправку данных пользователем. В этом файле также присутствует некоторый php-код. Но об этом позже.

В каталоге registration создаём корневой файл index.php, который и будет запускаться при входе на страницу регистрации. Он является контроллером, обрабатывающим действия пользователя и решающий, как будет выглядеть страница (в этом файле будет подключаться шаблон из подкаталога template, в зависимости от различных ситуаций). Смысл такого разделения подробно расписан в статье про MVC. Сейчас стоит отметить, что в файле index.php не должно быть никакого вывода, то есть в коде не должно быть ни строки html-кода и ни одного оператора echo или print.

Все данные о зарегистрированных пользователях мы будем хранить в базе данных MySQL. Для этого нам необходимо создать, например, через phpMyAdmin базу данных (БД), а затем таблицу с необходимыми полями. В данной статье, для примера, я буду использовать название БД следующее: db_name. Название таблицы – users. Далее добавим в таблицу следующие поля:

  • id (при добавлении записи в таблицу данное поле у новой записи будет проставляться автоматически, увеличиваясь на единицу по сравнению с предыдущей добавленной записью; для этого необходимо поствить галочку в поле AI (AUTO_INCREMENT)). Тип данных – int.
  • login – собственно, сам логин юзера, varchar(25).
  • password – хэшированный пароль (об этом позже), varchar(32).
  • salt – «соль», используемая для «примеси» к паролю, varchar(3).
  • mail_reg – регистрационный e-mail, varchar(50).
  • mail – email, которые позже можно будет изменить в профиле пользователя, varchar(50).
  • last_act – время последней активности пользователя, int(11).
  • reg_date – дата регистрации, int(11).

Страница «Структура» в phpMyAdmin для данной таблицы должна выглядеть так (на неописанные выше поля внимание не обращать, они будут рассмотрены в следующих статьях):

(кликабельно)

Для удобства работы в будущем создадим в корне сайта папку lib, в которую поместим 2 файла: connect.php и function_global.php. В дальнейшем в этой папке будут храниться библиотеки различных функций.

Первый файл придётся подключать очень часто в будущем (он отвечает за подключение к БД), а во втором будут храниться часто используемые функции на сайте.

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

Реализация

Код файла connect.php:

1
2
3
4
5
<?
@mysql_connect("localhost", "login", "password")
	or die ("Ошибка подключения к базе данных");
@mysql_select_db("db_name");
?>

Файл отвечает за подключение и выбор БД, с которой будем работать. Вписываем в этот файл свои данные.

Файл index.php выглядит следующим образом:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?
ini_set ("session.use_trans_sid", true);
session_start();
include ('../lib/connect.php'); //подключаемся к БД
include ('../lib/function_global.php'); //подключаем библиотеку функций
 
//проверим, быть может пользователь уже авторизирован. Если это так, перенаправим его на главную страницу сайта
if (isset($_SESSION['id']) || (isset($_COOKIE['login']) && isset($_COOKIE['password']))) 
{
	header('Location: http://ваш_сайт/');
}
else 
{
	if (isset($_POST['GO'])) //если была нажата кнопка регистрации, проверим данные на корректность и, если данные введены и введены правильно, добавим запись с новым пользователем в БД
	{
		$correct = registrationCorrect(); //записываем в переменную результат работы функции registrationCorrect(), которая возвращает true, если введённые данные верны и false в противном случае
		if ($correct) //если данные верны, запишем их в базу данных
		{
			$login = htmlspecialchars($_POST['login']);
			$password = $_POST['password'];
			$mail = htmlspecialchars($_POST['mail']);
			$salt = mt_rand(100, 999);
			$tm = time();
			$password = md5(md5($password).$salt);
			if (mysql_query("INSERT INTO users (login,password,salt,mail_reg,mail,reg_date,last_act) VALUES ('".$login."','".$password."','".$salt."','".$mail."','".$mail."','".$tm."','".$tm."')")) //пишем данные в БД и авторизовываем пользователя
			{
				setcookie ("login", $login, time() + 50000, '/');
				setcookie ("password", md5($login.$password), time() + 50000, '/');
				$rez = mysql_query("SELECT * FROM users WHERE login=".$login);
				@$row = mysql_fetch_assoc($rez);
				$_SESSION['id'] = $row['id'];
				$regged = true;
				include ("template/registration.php"); //подключаем шаблон
			}
		}
		else
		{
			include_once ("template/registration.php"); //подключаем шаблон в случае некорректности данных
		}
	}
	else
	{
		include_once ("template/registration.php"); //подключаем шаблон в случае если кнопка регистрации нажата не была, то есть, пользователь только перешёл на страницу регистрации
	}
}
?>

Теперь разберём весь код чуть подробнее.

Вначале мы подключили два файла:

1
2
include ('../lib/connect.php');
include ('../lib/function_global.php');

О них говорилось выше. Во втором из них находится функция, которую мы рассмотрим чуть позже.

В следующих строках мы проверяем, не авторизирован ли пользователь:

1
2
3
4
if (isset($_SESSION['id']) || (isset($_COOKIE['login']) &#038;& isset($_COOKIE['password'])))
{
	header('Location: http://ваш_сайт/');
}

Дело в том, что в будущем, когда я буду рассказывать об авторизации на сайте, авторизация будет происходить следующим образом: в сессию будет писаться ваш id (уникальный для любого пользователя номер), а также будет создаваться два cookie, в одном будет храниться логин, во втором – пароль.

В данных строках, мы проверяем, есть ли у данного пользователя переменные $_SESSION[‘id’], $_COOKIE[‘login’] и $_COOKIE[‘password’]. Если такие переменные есть, то перенаправим пользователя на главную страницу сайта:

header('Location: http://ваш_сайт/');

Если же таких данных не оказалось, то идём дальше, где проверяем, была ли нажата кнопка с name=”GO”. Если это так, проверяем данные и, если всё хорошо, пишем их в БД. В противном случае, подключаем шаблон с формой:

1
2
3
4
else
	{
		include_once ("template/registration.php");
	}

Если же кнопка была нажата, в первую очередь необходимо проверить введённые пользователем данные на корректность. Делается это строкой:

$correct = registrationCorrect();

В файле function_global.php пишем следующую функцию:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function registrationCorrect() {
	if ($_POST['login'] == "") return false; //не пусто ли поле логина 	
	if ($_POST['password'] == "") return false; //не пусто ли поле пароля
	if ($_POST['password2'] == "") return false; //не пусто ли поле подтверждения пароля
	if ($_POST['mail'] == "") return false; //не пусто ли поле e-mail
	if ($_POST['lic'] != "ok") return false; //приняты ли правила
	if (!preg_match('/^([a-z0-9])(\w|[.]|-|_)+([a-z0-9])@([a-z0-9])([a-z0-9.-]*)([a-z0-9])([.]{1})([a-z]{2,4})$/is', $_POST['mail'])) return false; //соответствует ли поле e-mail регулярному выражению
	if (!preg_match('/^([a-zA-Z0-9])(\w|-|_)+([a-z0-9])$/is', $_POST['login'])) return false; // соответствует ли логин регулярному выражению
	if (strlen($_POST['password']) < 5) return false; //не меньше ли 5 символов длина пароля
 	if ($_POST['password'] != $_POST['password2']) return false; //равен ли пароль его подтверждению
	$login = $_POST['login'];
	$rez = mysql_query("SELECT * FROM users WHERE login=$login");
	if (@mysql_num_rows($rez) != 0) return false; // проверка на существование в БД такого же логина
	return true; //если выполнение функции дошло до этого места, возвращаем true }

При первом же несоответствии функция вернёт false и прекратит свою работу (данные некорректны), если же все данные качественны, выполнение функции дойдёт до конца и вернёт true (данные корректны).

Продолжим дальше разбирать файл index.php.

1
2
3
4
if ($correct)
{
//этот блок выполнится, если функция registrationCorrect вернула true. В противном случае будет опять подключён шаблон с формой
}

Внутри этого блока расположен следующий код, в нём данные непосредственно подготавливаются для записи в БД:

1
2
3
4
5
6
$login = htmlspecialchars($_POST['login']);
$password = $_POST['password'];
$mail = htmlspecialchars($_POST['mail']);
$salt = mt_rand(100, 999);
$tm = time();
$password = md5(md5($password).$salt);

В переменную $salt запишем случайное трёхзначное число. Оно нам пригодится для «примешивания» к паролю. Дело в том, что во избежание кражи паролей, они в открытом виде в БД не хранятся. Там хранятся лишь их хэши – 32-символьные строки, уникальные для определённой строки. В php существует функция md5(), которая преобразует любую переданную ей строку, в строку, состоящую из 32 символов. Например, если выполнить следующий код:

1
<? echo md5(“true-coder”); ?>

, то на экран выведется строка: cfcd208495d565ef66e7dff9f98764da

Понятия «расхэшировать» в природе не существует. Вы можете благополучно захэшировать огромную статью и на выходе получить те же 32 символа. Хэширование – не сжатие, а получение уникальной контрольной суммы для данной строки. Народные умельцы давно уже составили большие базы хэшей различных строк, но какими бы большими они ни были, если ваш пароль сколь либо сложен, узнать его будет всё также тяжело. Так вот, в БД не хранятся пароли в открытом виде, они хранятся в виде хэш-кода. А когда вы авторизируетесь на сайте, введённый вами пароль приводится скриптом к хэшу и сравнивается с хэшем в БД. Также поступим и мы, но ещё немного мудрее. Есть такое понятие как salt, так называемая «соль», которая «приклеивается» к паролю, а лишь потом применяется функция md5. Эту соль мы и генерируем ($salt = mt_rand(100, 999);). Её мы также запишем в БД.

В следующей строке мы создаём хэш пароля:

1
$password = md5(md5($password).$salt);

Поясню… Вначале мы применяем к паролю функцию md5, результат – 32-символьная строка. Потом «лепим» справа к этой строке ещё 3 случайных цифры (наша «соль») и применяем опять md5. Результат – опять 32-символьная строка. Смысл этих манипуляций состоит в том, что мы сильно усложняем жизнь злоумышленнику. И ему остаются лишь подбирать брутфорсом ваш пароль, что займёт у него, возможно, многие годы 😉

1
$tm = time(); //в эту переменную пишем время регистрации, необходимое для записи в БД. Также это время будет использовано для сохранения в БД времени последней активности пользователя.

Далее идёт собственно запись данных в БД. Если она будет удачной, то скрипт авторизирует пользователя, записав его id в сессию и создав нужные куки. Следует обратить внимание на то, как формируется пароль в куках. Это тот же md5, но применённый по другому принципу. Это нам пригодится при авторизации пользователя в будущем.

1
2
3
4
5
6
7
8
9
10
if (mysql_query("INSERT INTO users (login,password,salt,mail_reg,mail,reg_date,last_act) VALUES ('".$login."','".$password."','".$salt."','".$mail."','".$mail."','".$tm."','".$tm."')")) //пишем данные в БД и авторизовываем пользователя
			{
				setcookie ("login", $login, time() + 50000, '/');
				setcookie ("password", md5($login.$password), time() + 50000, '/');
				$rez = mysql_query("SELECT * FROM users WHERE login=".$login);
				@$row = mysql_fetch_assoc($rez);
				$_SESSION['id'] = $row['id'];
				$regged = true;
				include ("template/registration.php"); //подключаем шаблон
			}

После этого у пользователя у появится два cookie: $_COOKIE[‘login’] и $_COOKIE[‘password’], а также переменная сессии — $_SESSION[‘id’], равная id пользователя, который мы узнаём с помощью запроса к БД.

Переменная $regged нужна нам в качестве флага, говорящего, что регистрация прошла успешно. Дело в том, что внутри подключаемого файла include («template/registration.php»); присутствует условие, которое, при $regged == true выведет текст, что регистрация прошла успешно и не выведет форму регистрации.

На этом пока всё. Исходники здесь.

В следующей статье мы рассмотрим проверку введённых в форму регистрации данных на клиенте, посредством JavaScript.

До встречи! 😉

Пишем регистрацию на PHP и JavaScript – Часть 2: JavaScript/p