Простая регистрация и авторизация на PHP с нотификациями

Создание учетных записей и личных кабинетов является одной из ключевых функциональностей ряда сайтов определенного типа. Этот функционал позволяет пользователю иметь персонализированный раздел на сайте, с помощью которого он может совершать какие-либо действия от себя: писать сообщения, делать покупки, хранить личную информацию и т. д. Чтобы создать такой раздел и позволить иметь туда доступ пользователю, используются такие процессы, как регистрация и авторизация на PHP. В статье мы напишем простые скрипты для регистрации нового пользователя и его авторизации. Также затронем процессы выхода пользователя из системы («разлогинивание») и нотификации.

Регистрация и авторизация на PHP: описание задачи

Наш пример будет работать следующим образом. У нас будет HTML-страница с двумя кнопками («Регистрация» и «Авторизация»). При нажатии на какую-либо из них произойдет перенаправление на страницу с соответствующей формой. После успешной регистрации система перенаправит нас на страницу авторизации. Авторизуясь, мы снова попадаем на главную страницу, где вместо предыдущих двух кнопок будет отображена кнопка «Выйти», которая будет свидетельствовать о том, что мы вошли в систему. После нажатия на эту кнопку произойдет «разлогинивание» и на странице мы снова увидим кнопки для регистрации и авторизации.

Список файлов примера следующий:

Структура файлов регистрации и авторизации на PHP

Главная HTML-страница с двумя кнопками находится в index.php. Файл registration.php содержит форму и скрипт регистрации пользователя, а login.php — форму и скрипт авторизации. Конфигурация подключения к базе данных вынесена в config.php. Логика нотификаций находится в notification.php, а скрипт для выхода из системы расположен в logout.php.

Для работы с базой данных мы будем использовать MySQLi. Версия PHP должна быть не ниже 8.2 — мы используем метод execute_query для совершения запросов в базу, который был введен именно в этой версии PHP. Для минимальной CSS-стилизации возьмем популярную библиотеку Bootstrap.

Главная HTML-страница

Создадим файл index.php и сверстаем элементарную HTML-страницу, добавив туда только заголовок (пусть будет Homepage) и кнопки «Регистрация» и «Авторизация». В этом файле пропишем условную логику: если пользователь авторизован, показываем кнопку «Выйти», иначе отображаем кнопки «Регистрация» и «Авторизация». Для этого создадим функцию isLoggedIn, которая проверит, авторизован ли пользователь, основываясь на наличии ключа, созданного в сессии при авторизации (пусть это будет user_id). Чтобы иметь доступ к данным сохраненным в сессии на этапе авторизации, начнем код страницы с вызова функции session_start().

<?php
// Инициализируем сессию
session_start();

// Функция проверки, авторизован ли пользователь
function isLoggedIn() {
    return isset($_SESSION['user_id']) && $_SESSION['user_id'];
}
?>

<!doctype html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.1.3/dist/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
    <title>Homepage</title>
</head>
<body>
    <article class="container">
        <h1>Homepage</h1>

        <!-- Отображаем различные кнопки, в зависимости от того, авторизован ли пользователь -->
        <?php if (isLoggedIn()) { ?>
            <a href="logout.php" class="btn btn-secondary">Выйти</a>
        <?php } else { ?>
            <a href="registration.php" class="btn btn-primary">Регистрация</a>
            <a href="login.php" class="btn btn-primary">Авторизация</a>
        <?php }  ?>
    </article>
</body>
</html>

Результат будет таким:

Домашняя страница

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

Файлы конфигурации и нотификации

Для подключения к базе данных необходимы данные доступа к ней. В реальных проектах эти данные хранятся в отдельном файле и не сохраняются в системе контроля версии (например, Git), так как являются конфиденциальными. Следуя этой практике, создадим отдельный файл для подобного рода информации — config.php. Код этого файла будет просто возвращать данные для подключения к базе:

<?php
return [
    'dbHost' => 'localhost',
    'dbUsername' => 'your_username',
    'dbPassword' => 'your_password',
    'dbName' => 'example'
];

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

<?php
function notify(?string $message = null)
{
    // Если передаем сообщение, то устанавливаем его в сессию
    if ($message) {
        $_SESSION['notification'] = $message;
    } else {
        // При вызове функции без параметра отобразим сообщение на странице, если в сессии есть это сообщение
        if (!empty($_SESSION['notification'])) { ?>
            <div class="alert alert-danger mt-3">
                <?php echo $_SESSION['notification']; ?>
            </div>
        <?php }
        // Затем просто удалим нотификацию из сессии
        unset($_SESSION['notification']);
    }
}

Далее перейдем к процессу регистрации на PHP.

Регистрация

Создадим файл registration.php, где будет форма и скрипт регистрации. Код файла будет таким:

<?php
// Сохраняем данные конфигурации в переменную
$config = require_once 'config.php';
// Подключаем нотификации
require_once 'notification.php';
// Инициализируем сессию
session_start();

// Если это POST-запрос, то есть мы нажали на кнопку "Регистрация", выполняем процесс регистрации
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // В этот массив будем собирать возможные ошибки
    $errors = [];

    // Валидируем email
    $email = isset($_POST['email']) ? trim($_POST['email']) : null;
    if (empty($email)) {
        $errors[] = 'Введите email';
    }
    if (!filter_var($email, FILTER_SANITIZE_EMAIL)) {
        $errors[] = 'Неверный email';
    }

    // Валидируем пароль
    $password = isset($_POST['password']) ? trim($_POST['password']) : null;
    if (empty($password)) {
        $errors[] = 'Введите пароль';
    }
    if (strlen(trim($password)) < 6 || strlen(trim($password)) > 50) {
        $errors[] = 'Пароль должен содержать не менее 6 и не более 50 символов';
    }

    // Проверяем, правильно ли пользователь подтвердил пароль
    $passwordRepeat = isset($_POST['password_repeat']) ? trim($_POST['password_repeat']) : null;
    if ($password !== $passwordRepeat) {
        $errors[] = 'Пароль подвержден неверно';
    }

    // Если ошибок нет, продолжаем
    if (empty($errors)) {
        try {
            // Подключаемся к базе данных
            $connection = new mysqli($config['dbHost'], $config['dbUsername'], $config['dbPassword'], $config['dbName']);

            // Делаем запрос в базу, проверяя, существует ли уже зарегистрированный пользователь с таким email
            $sql = "SELECT id FROM users WHERE email = ?";
            $result = $connection->execute_query($sql, [$email]);
            // Если такой пользователь есть, выводим сообщение
            if ($result->fetch_assoc()) {
                notify('Пользователь с таким email уже существует');
            } else { // Иначе создаем запись в базе данных с новым пользователем
                $sql = "INSERT INTO users (email, password) VALUES (?, ?)";
                $passwordHash = password_hash($password, PASSWORD_DEFAULT);
                $connection->execute_query($sql, [$email, $passwordHash]);

                // После создания нового пользователя редиректим на страницу авторизации
                header('location: login.php');
                exit;
            }
        } catch (mysqli_sql_exception $e) {
            // Если произошла какая-либо ошибка при регистрации, выводим ее
            notify('Произошла ошибка при регистрации');
            // Можно сделать запись в лог об ошибке
            error_log($e->__toString());
        } finally { // При любом исходе процесса регистрации закрываем подключение к базе данных
            if (isset($connection)) {
                $connection->close();
            }
        }
    } else { // В случае наличия ошибок выводим их на страницу
        notify(implode('<br>', $errors));
    }
}
?>

<!doctype html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.1.3/dist/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
    <title>Homepage</title>
</head>
<body>

<section class="container w-25">
    <h2>Регистрация</h2>
    <form method="post">
        <div class="form-group">
            <label for="email">Email</label>
            <input type="email" name="email" class="form-control" id="email" placeholder="Enter email">
        </div>
        <div class="form-group">
            <label for="password">Пароль</label>
            <input type="password" name="password" class="form-control" id="password" placeholder="Password">
        </div>
        <div class="form-group">
            <label for="password-repeat">Повторите пароль</label>
            <input type="password" name="password_repeat" class="form-control" id="password-repeat"
                   placeholder="Repeat password">
        </div>
        <button type="submit" class="btn btn-primary">Регистрация</button>
    </form>

    <?php notify(); ?>
</section>

</body>
</html>

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

Форма регистрации

После создания нового пользователя у нас появляется возможность авторизоваться, чем мы и займемся в следующем разделе.

Авторизация

Для авторизации на PHP создадим файл login.php и поместим в его следующий код:

<?php
$config = require_once 'config.php';
require_once 'notification.php';
session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $errors = [];

    $email = isset($_POST['email']) ? trim($_POST['email']) : null;
    if (empty($email)) {
        $errors[] = 'Введите email';
    }

    $password = isset($_POST['password']) ? trim($_POST['password']) : null;
    if (empty($password)) {
        $errors[] = 'Введите пароль';
    }

    if (empty($errors)) {
        try {
            $connection = new mysqli($config['dbHost'], $config['dbUsername'], $config['dbPassword'], $config['dbName']);
            // Пробуем извлечь пользователя из базы с предоставленным email
            $sql = "SELECT id, email, password FROM users WHERE email = ?";
            $result = $connection->execute_query($sql, [$email]);

            // Если пользователь найден, сверяем пароли
            if ($user = $result->fetch_assoc()) {
                // Если пароли совпадают, сохраняем данные пользователя в сессию и редиректим на главную страницу
                if (password_verify($password, $user['password'])) {
                    $_SESSION['user_id'] = $user['id'];
                    $_SESSION['email'] = $user['email'];

                    header('location: /');
                    exit;
                }
                // В случае несовпадения паролей выводим сообщение, что нет пользователя с такой комбинацией
                // Не стоит выводить сообщение о том, что только пароль неверный - это усиливает уязвимость сайта к взлому
                notify('Пользователь с такой комбинацией email и пароля не существует');
            } else {
                // Такое же сообщение выведем, если email неверный
                notify('Пользователь с такой комбинацией email и пароля не существует');
            }
        } catch (mysqli_sql_exception $e) {
            notify('Произошла ошибка при авторизации');
            error_log($e->__toString());
        } finally {
            if (isset($connection)) {
                $connection->close();
            }
        }
    } else {
        notify(implode('<br>', $errors));
    }
}
?>

<!doctype html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.1.3/dist/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
    <title>Login</title>
</head>
<body>

<section class="container w-25">
    <h2>Авторизация</h2>
    <form method="post">
        <div class="form-group">
            <label for="email">Email</label>
            <input type="email" name="email" class="form-control" id="email" placeholder="Enter email">
        </div>
        <div class="form-group">
            <label for="password">Пароль</label>
            <input type="password" name="password" class="form-control" id="password" placeholder="Password">
        </div>
        <button type="submit" class="btn btn-primary">Войти</button>
    </form>

    <?php notify(); ?>
</section>

</body>
</html>

Внешний вид формы авторизации такой:

Форма авторизации

После успешной авторизации пользователь будет перенаправлен на главную страницу сайта, где вместо двух кнопок увидит кнопку «Выйти», которая будет свидетельствовать о том, что он авторизован:

Домашняя страница авторизованного пользователя

При нажатии на эту кнопку мы запустим процесс выхода из системы — разберем его далее.

Выход из системы

Для «разлогинивания» создадим отдельный файл logout.php. Туда поместим скрипт, который завершит сессию и перенаправит пользователя на главную страницу:

<?php
session_start();

// Удалим все данные сессии
$_SESSION = [];

// Удалим сессию
session_destroy();

// После выхода из системы перейдем на главную страницу
header('Location: index.php');
exit;

Главная страница после «разлогинивания» приобретет изначальный вид, указывающий нам на то, что выход из системы произведен успешно.

Заключение

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

Оцените статью
DevReflex
Добавить комментарий

  1. Геннадий

    Скажите, а вы пишете скрипты на ПХП на заказ?
    Я хотел бы заказать. обращался на профи и фриланс ру, различные биржи. Многие умеют работать с КМС или платформами, но мало кто умеет работать с чистым кодом ПХП.
    Если вы занимаетесь кодированием, может поработаем?

    Ответить
    1. Андрей Толпеко автор

      Здравствуйте! К сожалению, сейчас таким заказам не готов уделять время. Но благодарю за предложение!

      Ответить
  2. Борис

    Исправил одну ошибку, работает.

    Ответить
    1. Mas

      как исавить

      Ответить
  3. Mas

    Parse error: syntax error, unexpected identifier «dbName», expecting «]» in /config.php on line 6

    Ответить
    1. Андрей Толпеко автор

      Верно! Была ошибка в файле config.php. Пропущеная закрывающая кавычка после ‘your_password’. Уже исправил:
      return [
      ‘dbHost’ => ‘localhost’,
      ‘dbUsername’ => ‘your_username’,
      ‘dbPassword’ => ‘your_password’,
      ‘dbName’ => ‘example’
      ];

      Ответить
  4. Олег

    Здравствуйте. Подскажите пож-та как вывести логин или емейл зарегистрировавшегося посетителя в шапке сайта перед кнопкой ,,Выйти,, что бы посетитель видел что он залогинился на сайте?

    Ответить
    1. Андрей Толпеко автор

      Здравствуйте!
      Мы знаем ID пользователя и можем по нему извлечь остальные данные из базы.
      Для этого простого примера можем написать функцию для получения email:

      function getUserEmail() {
      $userId = $_SESSION[‘user_id’] ?? null;
      if (!$userId) {
      return null;
      }

      $config = require_once ‘config.php’;
      $connection = new mysqli($config[‘dbHost’], $config[‘dbUsername’], $config[‘dbPassword’], $config[‘dbName’]);
      $sql = ‘SELECT email FROM users WHERE id = ?’;
      $result = $connection->execute_query($sql, [$userId]);
      $userData = $result->fetch_assoc();

      return $userData[’email’] ?? null;
      }

      Потом вызвать ее перед кнопкой «Выйти»:

      echo ‘User: ‘ . getUserEmail();

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

      Ответить
  5. Елена

    Здравствуйте,
    у меня код не работает, выдается сообщение «Произошла ошибка при регистрации».
    Может ли это быть связано с версиями моей системы?
    Веб-сервер
    Apache/2.4.33 (Win64) OpenSSL/1.0.2u mod_fcgid/2.3.9 PHP/8.3.1
    Версия клиента базы данных: libmysql — mysqlnd 8.3.1
    PHP расширение: mysqli Документация curl Документация mbstring Документация
    Версия PHP: 8.3.1
    phpMyAdmin
    Информация о версии: 5.1.2

    Ответить
    1. Андрей Толпеко автор

      Здравствуйте!
      В нашем примере такая ошибка возникает, когда приложение выбрасывает исключение при подключении к базе и совершении запроса к ней. Мы отлавливаем исключение и записываем в лог в блоке catch:

      error_log($e->__toString());

      Чтобы узнать, какая конкретно ошибка произошла, можно либо посмотреть логи ошибок на Вашем сервере, либо отобразить ошибку прямо на странице после нажатия кнопки «Регистрация», добавив в блок catch распечатку ошибки:

      var_dump($e->__toString());

      Скорее всего, возникли проблемы при подключении к Вашей базе данных. Проверьте, пожалуйста, файл config.php. Правильно ли заданы данные подключения базе данных: dbHost, dbUsername, dbPassword, dbName.

      Ответить