Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
rusldv
7 лет назад

PHP - Урок 14. Регистрация пользователей на сайте

PHP - Урок 14. Регистрация пользователей на сайте.




Кот программист из Интернета - Источник кота


Предыдущие уроки:



Программируем на PHP - Введение

PHP - Запросы от браузера к серверу

PHP - Как работает сервер

PHP - Урок 4. PHP - интерпретатор

PHP - Урок 5. Переменные сервера и глобальные переменные

PHP - Урок 6. Конструкции print и echo. Кавычки одинарные и двойные и конкатенация строк

PHP - Урок 7. Переменные, константы и условия

PHP - Урок 8. Точка входа в приложение. Настройка mod_rewrite и файл .htaccess

PHP - Урок 9. Массивы и switch. Кодим основной каркас

PHP - Урок 10. COOKIE

PHP - Урок 11. Функции. Добавляем ядро системы core.php

PHP - Урок 12. Обзор модели MVC. Добавляем шаблоны страниц в наше приложение

PHP - Урок 13. Введение в базы данных и SQL. СУБД MySQL. Подключаемся к БД из нашего приложения


Теория - Регистрация и авторизация пользователей


Практически во всех интерактивных (взаимодействующих с пользователями) проектах сети Интернет предусмотрена возможность создавать пользователями своих учетных записей. Которые обычно хранятся в базе данных такого сайта.
Регистрационная запись пользователя на сайте в Интернете можно сравнивать довольно со многими подобными в реальной жизни. Например: абонемент в фитнесс-клуб, запись в библиотеку, какой-нибудь ваш страховой полис, дисконтная карта и т.д.
То есть смысл регистрации в по большей части не в том, чтобы получить от вас какие-то ваши данные. Цель заключается в том, чтобы система (сайт) могла вас идентифицировать среди остальных.
Благодаря такой идентификации система определяет связанные с вами данные. Что какие у вас скидки (в случае магазина), какие книги вы взяли на чтение и любите читать (библиотека).
В случае соцсетей это - информация о вас, ваши подписки и подписчики, записи в блоге и т.д.

В обычной жизни, для идентификации в каких-либо учреждениях вы обычно предъявляете что-то что вам выдали. Например ваш паспорт, права или абонемент.
В Интернете такая форма идентификации не предусмотрена.
Сайт на котором вы хотите создать свою учетную запись предлагает вам форму регистрации.

Самым простым примером такой формы может быть форма из 2-полей и кнопки.

  1. Ваш логин
  2. Ваш пароль
  3. Кнопка регистрации

Однако такая форма довольно примитивна. Так как возникают следующие опасности относительно хранения регистрационных данных у пользователя.

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

Чтобы не допустить таких неприятностей принято вместо логина вводить e-mail (также может быть номер вашего телефона).
Когда в указываете e-mail или телефон в качестве логина, то у сайта появляется возможность восстановить ваш пароль, если вы его забудите, путем отправки нового пароля или ссылки на восстановление на указанный для связи адрес.
Если ваш пароль украдут, то вы таким же образом сможете его изменить.

Еще обычно в таких формах регистрации просят ввести пароль дважды - тут все просто. Это помогает избежать опечаток при наборе пароля. Допустить опечатку 2-раза подряд гораздо сложнее.

Практика - создаем учетные записи пользователей


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

Напомню что мы формально представляем БД так.

  1. База данных - это папка с файлами.
  2. Таблица - это файл.
  3. Запись в таблице - это запись в файле.

Такой формат например у MyISAM, у других движков может быть структура хранения данных другая - но логически ее всегда можно представить описанным выше образом.
Для формального понимания можно даже представить, что поля записи разделены каким-нибудь специальным символом. Например тильдой (12~rusldv~26~1).
На самом деле так было в 80-х в век майнфреймов. Сейчас в БД для получения строки берется описание структуры этой строки и по ней извлекаются строки (как бы накладывая по очереди трафарет на данные в файле и передвигая его дальше по файлу на расстояние увеличенное на размер (ширину) трафарета.
Так как в структуре описан размер каждого столбца таблицы, то без труда можно получить значение нужного поля в таблицы.

И так к чему это я.
Регистрационная (учетная) запись пользователя - это запись в таблице. 1 запись - 1 учетная запись.
Естественно в одной такой таблицы может храниться очень много учетных записей - на всех пользователей сайта.

Теперь давайте сделаем это. Запускаем денвер и переходим в нем к нашей базе данных thesite.
Как только мы выбрали базу данных мы можем с ней работать в phpMyAdmin в 2 режимах:

  1. Визуальном;
  2. Вводом запроса SQL.

Первый более простой для среднестатистического разработчика, поэтому начнем с него.

Для работы нам понадобится некоторое представление о сути дела. Попробую кратко поведать:

В SQL есть специальная команда для создания таблиц в базе данных - CREATE TABLE.
Ей передается имя таблицы, а также ее структура - список имен столбцов их типы и атрибуты.
Когда сервер базы данных получает такой запрос он:

  1. Создает файл в папке базы данных tablename.tbl
  2. В начало файла записывает структуру таблицы, описанную в команде CREATE TABLE.

(Затем при вставке записей в таблицу эта структура будет считываться программой-сервером БД и по ней (как по трафарету) будут добавляться (или модифицироваться) записи.

Простой пример команды для создания таблицы: CREATE TABLE test (name VARCHAR(36) NOT NULL, age TINYINT UNSIGNED NOT NULL)

Это очень простой пример и не очень правильный. Он создает таблицу в текущей (выбранной) БД с именем test и двумя столбцами - name (имя) и age (возраст).
name - здесь символьный тип переменной длины до 36 символов (по сути символьный массив), any имеет тип маленького целого числа которое не может быть отрицательным (атрибут UNSIGNED). Оба столбца не могут быть пустыми - в них обязательно должно что-то записываться при вставке новой записи.

Что ж теперь о нашей структуре таблице учетных записей.

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

Придумаем имя нашей таблицы. Я предпочитаю в начало добавлять префикс. Он позволяет хранить в одной базе данных несколько таблиц с одним именем но разными префиксами. Обычно это полезно при установки нескольких копий БД физически в одну бд. Например когда хостинг ограничивает кол-во баз данных на одном аккаунте.
Мне хот и ни чего не ограничивает, но я считаю так более изящно выглядит и легко ориентироваться в случае добавления специфических таблиц с другими префиксами.

Префикс будет состоять из 2 букв от слов в названии TheSite и подчеркивания: ts_.
А таблицу назовем users -таким образом полное имя таблицы у нас получается ts_users.

Для хранения учетной записи нам понадобятся следующие поля:

  1. id INT NOT NULL UNSIGNED AUTO_INCREMENT PRIMARY KEY
  2. email VARCHAR(255) NOT NULL UNIQUE
  3. password VARCHAR(255) NOT NULL
  4. hash VARCHAR(255)
  5. status TINYINT NOT NULL

Таким образом в визуальном режиме в phpMyAdmin мы кликаем на нашей базе данных thesite и нажимаем кнопку "Создать таблицу".
Вводим название таблицы - ts_users и количество столбцов - 5 и нажимаем ОК.

У нас появляется форма для определения нашей структуры таблицы.

Заполним ее в соответствии с описанной выше структурой.

Обратите внимание что поле id помимо остального содержит атрибуты AUTO_INCREMENT и PRIMARY KEY.
В phpMyAdmin это указали галочкой в A_I и значением PRIMARY в разделе Индекс.
AUTO_INCREMENT указывает серверу БД что при вставке записи в новую таблицу поле ID автоматически будет увеличено на 1.
Это удобно для автоматического назначения идентификаторов записям в таблице.

Столбец email содержит атрибут UNIQUE, который указывает что все значения в данном столбце должны быть уникальными (не могут повторяться). В связи с этим такое поле также включается в индекс (по нему можно производить поиск записей таблицы).

Единственные поля, в которых может быть пустое значение (NULL) - это hash. Он генерируется при входе пользователя в систему и в дальнейшем используется в куках вместо пароля. Это дополнительная безопасность от похищения пароля.

А теперь перейдем к коду на PHP.

Для того, чтобы мы могли авторизироваться на нашем сайте нам понадобится зарегистрироваться.

Для регистрации добавим функцию register в наше ядро /sys/core.php:

function register($pdo, $email, $password){
        $email = $pdo->quote($email);
        $password = md5($password);
        $password = $pdo->quote($password);
        //print $mail.' '.$password;
        // TODO: Проверить правильность мыла регулярным выражением
        $sql_check = "SELECT COUNT(id) FROM ts_users WHERE email=$email";
        $stmt = $pdo->query($sql_check);
        $row = $stmt->fetch(PDO::FETCH_NUM);
        if($row[0] > 0){
            print 'Учетная запись уже существует. Забыл пароль?';
        }else{
            // Добавляем учетную запись в таблицу ts_users
            $sql_insert = "INSERT INTO ts_users (email, password, status) VALUES ($email, $password, 1)";
            //print $sql_insert;
            
            if($pdo->exec($sql_insert)){
                /* это раскомментируем в следующем уроке
                $sql = "SELECT id FROM mc_user WHERE mail=$mail";
                $stmt = $pdo->query($sql);
                $row = $stmt->fetch(PDO::FETCH_ASSOC);
                $uid = $row['id'];
                $sql_insert = "INSERT INTO mc_profile (user_id) VALUES ('$uid')";
                $pdo->exec($sql_insert);
                mkdir('content/'.$uid.'/');
                */
                return true;
            }else{
                return false;
            }
        }
    }

Чтобы проверить работает ли наша функция - ее нужно вызвать. Так как мы ее будем вызывать в нашем switch-e. Сначала сделаем тестовый вызов:
в разделе case 'register': который в переключателе в файле index.php пишем:

if(register($pdo, 'test1@mail.loc', 'test1')){
    print 'ok';
}else{
    print 'Регистрация не получилась :(';
}

Убедившись что наша функция регистрации работает мы можем добавить полный код вызова в switch-e:

case 'register':
    $title = "Регистрация";
    $tpl = 'register';
    if($_SERVER['REQUEST_METHOD'] == 'POST'){
        $mail = clearStr($_POST['mail']);
        $password = clearStr($_POST['password']);
        $password_double = clearStr($_POST['password_double']);
                
        if(!empty($mail) && !empty($password) && !empty($password_double)){
            if($password != $password_double){
                print 'Пароли не совпадают';
            }else{
                if(register($pdo, $mail, $password)){
                    header('location: /login');
                }else{
                    print 'Регистрация не получилась :(';
                }
            }
        }
    }
break;

Если вы перейдете на страницу http://thesite.loc/register сразу после вставки этого кода, то встретитесь с ошибкой, которая говорит об отсутствии шаблона в папке templates.
Давайте его создадим - создаем файл register.tpl.php в папку sys/templates со следующем содержимым.

<h2>Регистрация</h2>
<form action="" method="post">
    <p><input name="mail" type="text" placeholder="e-mail" /></p>
    <p><input name="password" type="password" placeholder="Пароль" /></p>
    <p><input name="password_double" type="password" placeholder="Повторите пароль" /></p>
    <p><input name="register_submit" type="submit" value="Регестрироваться" /></p>
</form>

Теперь перейдя на страницу thesite.loc/register мы встретим нашу форму регистрации. Введя в нее данные и нажав кнопку, данная учетная запись будет сохранена в нашу базу данных в таблицу ts_users. В этом мы сможем убедиться перейдя в нее через phpMyAdmin:

Кстати если вы встретите такое предупреждение:

Warning: Cannot modify header information - headers already sent by (output started at Z:\home\thesite.loc\www\index.php:2) in Z:\home\thesite.loc\www\index.php

Знайте - это связанно с отправкой заголовков после вывода контента в коде (например после echo). Заголовки умеют отправлять несколько функций. Собственно header(), setcookie() и location().
В нашем случае это location, которая вызывается после успешной регистрации и пытается переадресовать нас на страницу авторизации (которую мы пока не создали). Но дело не в этом. Дело в том, что location отправляет заголовки.
А у нас в index.php в прошлом уроке был вывод списка баз данных из массива:

$pdo = init();
$stmt = $pdo->query("SHOW DATABASES");
$dbs = $stmt->fetchAll(PDO::FETCH_ASSOC);
print_r($dbs);

Здесь нужно либо закомментировать последнюю функцию, либо удалить вообще все строи кроме первой. (Список баз данных нам вряд-ли понадобится). Оставим только $pdo = init(); - мы его будем использовать в switch-e.
Теперь при вводе данных регистрации нас благополучно перекидывает на страницу http://thesite.loc/login предварительно сохранив учетную запись в БД.

Полный код сегодняшнего урока


index.php

<?php 
    define(ROOT, $_SERVER['DOCUMENT_ROOT']);
    require(ROOT.'/sys/core.php');

    $page = route(1);
    $ext = route(2);
    
    //echo 'Запрашиваемая страница: '.$page;
    //echo '<br />Дополнительные данные: '.$ext;

    $pdo = init();
    
    switch($page){
        case 'login':
            echo '<h2>Страница авторизации</h2>';
            break;
        case 'logout':
            echo '<h2>Тут мы разлогиваемся</h2>';
            break;
        case 'register':
            $title = "Регистрация";
            $tpl = 'register';
            if($_SERVER['REQUEST_METHOD'] == 'POST'){
                $mail = clearStr($_POST['mail']);
                $password = clearStr($_POST['password']);
                $password_double = clearStr($_POST['password_double']);
                
                if(!empty($mail) && !empty($password) && !empty($password_double)){
                    if($password != $password_double){
                        print 'Пароли не совпадают';
                    }else{
                        if(register($pdo, $mail, $password)){
                            header('location: /login');
                        }else{
                            print 'Регистрация не получилась :(';
                        }
                    }
                }
            }
            break;
        case 'user':
            echo '<h2>Профиль пользователя</h2>';
            break;
        case 'post':
            echo '<h2>Конкретный пост</h2>';
            break;
        case 'news':
            echo '<h2>Страница новостей</h2>';
            break;
        default:
            $title = "Страница по умолчанию";
            $tpl = "default";
    }

    include_once(ROOT.'/sys/templates/index.tpl.php');

/sys/core.php

<?php
/* 
* This core functions for application
*/

// Обработка текста
function clearInt($num){
    return abs((int)$num);
}

function clearStr($str){
    return trim(strip_tags($str));
}

function clearHTML($html){
    return trim(htmlspecialchars($html));
}

/* Route functions */
function route($item = 1) {
    $request = explode("/", $_SERVER["REQUEST_URI"]);
    return $request[$item];
}

// Соединение с БД
function init(){
    $config = parse_ini_file(ROOT.'/sys/config.ini');
    //print_r($config);
    $dsn = "{$config['driver']}:host={$config['host']};dbname={$config['schema']}";
    return new PDO($dsn, $config['user'], $config['password']);
}

// Регистрация
    function register($pdo, $email, $password){
        $email = $pdo->quote($email);
        $password = md5($password);
        $password = $pdo->quote($password);
        //print $mail.' '.$password;
        // TODO: Проверить правильность мыла регулярным выражением
        $sql_check = "SELECT COUNT(id) FROM ts_users WHERE email=$email";
        $stmt = $pdo->query($sql_check);
        $row = $stmt->fetch(PDO::FETCH_NUM);
        if($row[0] > 0){
            print 'Учетная запись уже существует. Забыл пароль?';
        }else{
            // Добавляем учетную запись в таблицу ts_users
            $sql_insert = "INSERT INTO ts_users (email, password, status) VALUES ($email, $password, 1)";
            //print $sql_insert;
            
            if($pdo->exec($sql_insert)){
                /* это расскомментируем в следующем уроке
                $sql = "SELECT id FROM mc_user WHERE mail=$mail";
                $stmt = $pdo->query($sql);
                $row = $stmt->fetch(PDO::FETCH_ASSOC);
                $uid = $row['id'];
                $sql_insert = "INSERT INTO mc_profile (user_id) VALUES ('$uid')";
                $pdo->exec($sql_insert);
                mkdir('content/'.$uid.'/');
                */
                return true;
            }else{
                return false;
            }
        }
    }



/sys/templates/register.tpl

<h2>Регистрация</h2>
<form action="" method="post">
    <p><input name="mail" type="text" placeholder="e-mail" /></p>
    <p><input name="password" type="password" placeholder="Пароль" /></p>
    <p><input name="password_double" type="password" placeholder="Повторите пароль" /></p>
    <p><input name="register_submit" type="submit" value="Регистритроваться" /></p>
</form>

Ну на сегодня закончим. В следующем уроке напишем функции и шаблон страницы входа пользователя в систему.
897
304.112 GOLOS
На Golos с November 2016
Комментарии (4)
Сортировать по:
Сначала старые