Formulário de registro PHP

Resumo : neste tutorial você aprenderá como criar um formulário de registro em PHP do zero.

Introdução ao formulário de registro PHP

Neste tutorial, você criará um formulário de registro de usuário que consiste nos seguintes campos de entrada:

  • Nome de usuário
  • E-mail
  • Senha
  • ConfirmaÇão Da Senha
  • Caixa de seleção do contrato
  • Botão de registro
Formulário de registro PHP

Quando um usuário preenche o formulário e clica no botão Registrar, você precisa:

  • Limpe e valide as entradas do usuário.
  • Se os dados do formulário não forem válidos, mostre o formulário com as entradas do usuário e mensagens de erro.
  • Se os dados do formulário forem válidos, insira o novo usuário na userstabela do banco de dados, defina uma mensagem flash , redirecione o usuário para a login.phppágina e exiba a mensagem flash. Observe que você aprenderá como criar um formulário de login no próximo tutorial.

Configurar estrutura do projeto

Primeiro, crie uma pasta raiz do projeto, por exemplo, auth.

Segundo, crie as seguintes pastas na pasta raiz do projeto, por exemplo

├── config
├── public
└── src
   ├── inc
   └── libsLinguagem de código:  texto simples  ( texto simples )

A seguir descreve-se a finalidade de cada pasta:

Pasta Propósito
configuração Armazene o arquivo de configuração, como configuração do banco de dados
público Armazene os arquivos públicos acessados ​​diretamente pelos usuários
fonte Armazene os arquivos de origem que não devem ser expostos ao público
fonte/inc Armazene os arquivos comumente incluídos, como cabeçalho e rodapé de uma página
src/libs Armazene os arquivos da biblioteca, por exemplo, validação, sanitização, etc.

Remova o público do URL

Primeiro, crie register.phpna publicpasta. Para acessar a register.phppágina, você precisa usar o seguinte URL:

http://localhost/auth/public/register.phpLinguagem de código:  texto simples  ( texto simples )

Para remover o publicURL acima, você pode usar o módulo URL Rewrite do Apache Web Server. Para fazer isso, você precisa usar um .htaccessarquivo.

Segundo, crie .htaccesso arquivo na pasta raiz do projeto ( auth) e use o seguinte código:

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteRule ^$ public/ [L]
    RewriteRule (.*) public/$1 [L]
</IfModule>Linguagem de código:  texto simples  ( texto simples )

As diretivas acima instruem o Apache a remover o publicda URL. Se você abrir o URL http://localhost/auth/register.php, verá um erro.

Para corrigir o erro, você precisará de outro .htaccessarquivo na publicpasta.

Terceiro, crie outro .htaccessna publicpasta e use as seguintes diretivas:

<IfModule mod_rewrite.c>
    Options -Multiviews
    RewriteEngine On
    RewriteBase /auth/public
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
</IfModule>Linguagem de código:  texto simples  ( texto simples )

Agora, você pode acessar a register.phppágina sem usar o /public/URL assim:

http://localhost/auth/register.phpLinguagem de código:  texto simples  ( texto simples )

Crie o formulário de inscrição

Primeiro, crie um formulário de registro no register.phparquivo:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://tutorials.acervolima.com/app/css/style.css">
    <title>Register</title>
</head>
<body>
<main>
    <form action="register.php" method="post">
        <h1>Sign Up</h1>
        <div>
            <label for="username">Username:</label>
            <input type="text" name="username" id="username">
        </div>
        <div>
            <label for="email">Email:</label>
            <input type="email" name="email" id="email">
        </div>
        <div>
            <label for="password">Password:</label>
            <input type="password" name="password" id="password">
        </div>
        <div>
            <label for="password2">Password Again:</label>
            <input type="password" name="password2" id="password2">
        </div>
        <div>
            <label for="agree">
                <input type="checkbox" name="agree" id="agree" value="yes"/> I agree
                with the
                <a href="#" title="term of services">term of services</a>
            </label>
        </div>
        <button type="submit">Register</button>
        <footer>Already a member? <a href="login.php">Login here</a></footer>
    </form>
</main>
</body>
</html>Linguagem de código:  HTML, XML  ( xml )

Se você acessar a URL http://localhost/auth/register.php, verá um formulário de registro de usuário.

Deixe o formulário de inscrição mais organizado

Primeiro, crie o header.phparquivo na src/incpasta e copie a seção de cabeçalho do register.phparquivo para o header.phparquivo:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://tutorials.acervolima.com/app/css/style.css">
    <title>Register</title>
</head>
<body>
<main>Linguagem de código:  HTML, XML  ( xml )

Segundo, crie o footer.phparquivo na src/incpasta e copie a seção de rodapé do register.phparquivo para o footer.phparquivo:

</main>
</body>
</html>Linguagem de código:  HTML, XML  ( xml )

Terceiro, inclua os arquivos header.phpe footer.phpda incpasta no register.phparquivo. Normalmente, você usa a construção requireou include.

No entanto, você pode querer incluir os arquivos header.phpe footer.phpem outros arquivos, por exemplo, login.php. Portanto, o título da página não deve ser fixado assim:

<title>Register</title>Linguagem de código:  HTML, XML  ( xml )

Para tornar a  <title> tag dinâmica, você pode criar um helpers.phparquivo na src/libspasta e definir a  view()função que carrega o código de um arquivo PHP e passa dados para ele:</code> more dynamic, you can define a new function called <code>view()</code> that loads the code from a file and passes data to it:</p>

function view(string $filename, array $data = []): void
{
    // create variables from the associative array
    foreach ($data as $key => $value) {
        $$key = $value;
    }
    require_once __DIR__ . '/../inc/' . $filename . '.php';
}
Linguagem de código:  PHP  ( php )

Esta view()função carrega o código de um arquivo sem a necessidade de especificar a .phpextensão do arquivo.

A view()função permite passar dados para o arquivo incluído como um array associativo . No arquivo incluído, você pode usar as chaves dos elementos como nomes de variáveis ​​e os valores como valores de variáveis. Confira as variáveis ​​variáveis ​​para mais detalhes.

Por exemplo, o seguinte usa a view()função para carregar o código do header.phparquivo e torna o titlecomo uma variável no header.phparquivo:

<?php view('header', ['title' => 'Register']) ?>Linguagem de código:  PHP  ( php )

Como o header.phparquivo aceita the titlecomo uma variável, você precisa atualizá-lo da seguinte forma:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://tutorials.acervolima.com/app/css/style.css">
    <title><?= $title ?? 'Home' ?></title>
</head>
<body>
<main>Linguagem de código:  PHP  ( php )

Neste novo header.phparquivo, a titlevariável será padronizada Homese não estiver definida.

Para usar a view()função no register.php, é register.phpnecessário incluir o helpers.php.

Para incluir os arquivos centralmente, você pode criar um bootstrap.phparquivo na srcpasta. O bootstrap.phpincluirá todos os arquivos necessários. E você inclui o bootstrap.phparquivo no register.phparquivo.

O boostrap.phparquivo ficará assim:

<?php

require_once __DIR__ . '/libs/helpers.php';Linguagem de código:  PHP  ( php )

E o register.phparquivo ficará parecido com o seguinte:

<?php

require __DIR__ . '/../src/bootstrap.php';
?>

<?php view('header', ['title' => 'Register']) ?>

<form action="register.php" method="post">
...
</form>

<?php view('footer') ?>Linguagem de código:  PHP  ( php )

Processar o envio do formulário de inscrição

O formulário de registro é enviado register.phpusando o método HTTP POST. Para processar os dados do formulário, você pode verificar se a solicitação HTTP está POST no início do register.phparquivo assim:

if(strtoupper($_SERVER['REQUEST_METHOD']) === 'POST') {
    // process the form
}Linguagem de código:  PHP  ( php )

Como o código acima pode ser usado em outras páginas, você pode definir a is_post_request()função no helpers.phparquivo para encapsulá-lo:

function is_post_request(): bool
{
    return strtoupper($_SERVER['REQUEST_METHOD']) === 'POST';
}Linguagem de código:  PHP  ( php )

Da mesma forma, você também pode definir a is_get_request()função que retornará truese a solicitação HTTP atual for GET:

function is_get_request(): bool
{
    return strtoupper($_SERVER['REQUEST_METHOD']) === 'GET';
}Linguagem de código:  PHP  ( php )

E no início do register.phparquivo você pode usar a is_post_request()função da seguinte forma:

<?php

require __DIR__ . '/../src/bootstrap.php';


if (is_post_request()) {
    //...
}Linguagem de código:  PHP  ( php )

Limpe e valide as entradas do usuário

Para limpar e validar entradas do usuário, você pode usar as funções sanitize()e valdiate()desenvolvidas no tutorial de limpeza e validação de entrada ou pode usar a função.filter()

Para usar essas funções, você precisa:

  • Primeiro, crie os arquivos sanitization.php, validation.phpe filter.phpna src/libspasta.
  • Segundo, adicione o código a esses arquivos (veja o código no final deste tutorial).
  • Terceiro, inclua esses arquivos no bootstrap.phparquivo.

O bootstrap.phparquivo ficará parecido com o seguinte:

<?php

session_start();
require_once __DIR__ . '/libs/helpers.php';
require_once __DIR__ . '/libs/sanitization.php';
require_once __DIR__ . '/libs/validation.php';
require_once __DIR__ . '/libs/filter.php';Linguagem de código:  PHP  ( php )

Veja a seguir como usar a filter()função para limpar e validar as entradas do usuário:

$fields = [
    'username' => 'string | required | alphanumeric | between: 3, 25 | unique: users, username',
    'email' => 'email | required | email | unique: users, email',
    'password' => 'string | required | secure',
    'password2' => 'string | required | same: password',
    'agree' => 'string | required'
];

// custom messages
$messages = [
    'password2' => [
        'required' => 'Please enter the password again',
        'same' => 'The password does not match'
    ],
    'agree' => [
        'required' => 'You need to agree to the term of services to register'
    ]
];

[$inputs, $errors] = filter($_POST, $fields, $messages);Linguagem de código:  PHP  ( php )

Na filter()função, você passa três argumentos:

  • A $_POSTmatriz armazena as entradas do usuário.
  • A $fieldsmatriz associativa armazena as regras de todos os campos.
  • É $messagesuma matriz multidimensional que especifica as mensagens personalizadas para as mesmas regras obrigatórias dos campos password2e agree. Este argumento é opcional. Se você pular, a filter()função usará mensagens de validação padrão.

Se o formulário for inválido, você precisará redirecionar os usuários para a register.phppágina usando a técnica post-redirect-get (PRG). Além disso, você precisa adicionar os arrays $inputse $errorsà $_SESSIONvariável para que possa acessá-los na GETsolicitação após o redirecionamento.

Se o formulário for válido, você precisa criar uma conta de usuário e redirecionar os usuários para a login.phppágina usando a técnica PRG. Para mostrar uma mensagem uma vez nas páginas, você pode usar mensagens flash baseadas em sessão .

Gerenciar mensagens flash

Para gerenciar mensagens flash, você usa a flash()função definida no tutorial de mensagens flash :

  • Primeiro, crie um novo arquivo flash.phpna src/libspasta.
  • Segundo, adicione o código ao flash.phparquivo.
  • Terceiro, inclua o flash.phparquivo no formato bootstrap.php.

Para criar uma mensagem flash no register.phparquivo, você chama a flash()função:

flash(
    'user_register_success',
    'Your account has been created successfully. Please login here.',
    'success'
);Linguagem de código:  PHP  ( php )

Para mostrar todas as mensagens flash, você chama a flash()função sem passar nenhum argumento:

flash()Linguagem de código:  PHP  ( php )

Por exemplo, você pode mostrar as mensagens flash no header.phparquivo:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://tutorials.acervolima.com/app/css/style.css">
    <title><?= $title ?? 'Home' ?></title>
</head>
<body>
<main>
<?php flash() ?>
Linguagem de código:  PHP  ( php )

Defina a classe CSS do erro

Quando um campo de formulário contém dados inválidos, por exemplo, formato de endereço de e-mail incorreto, você precisa destacá-lo adicionando uma errorclasse CSS.

O seguinte define a error_class()função no src/libs/helpers.phparquivo, que retorna a 'error'classe se o $errorsarray tiver um erro associado a um campo:

function error_class(array $errors, string $field): string
{
    return isset($errors[$field]) ? 'error' : '';
}Linguagem de código:  PHP  ( php )

Redirecionar

Depois que os usuários se registrarem nas contas com sucesso, você precisará redirecioná -los para a página de login. Para fazer isso, você pode usar a header()função com a exitconstrução:

header ('Location: login.php');
exit;Linguagem de código:  PHP  ( php )

O seguinte define uma função chamada redirect_to()function no helpers.phparquivo para encapsular o código acima:

function redirect_to(string $url): void
{
    header('Location:' . $url);
    exit;
}Linguagem de código:  PHP  ( php )

Se os dados do formulário forem inválidos, você poderá redirecionar os usuários de volta à register.phppágina. Antes de fazer isso, você precisa adicionar as variáveis $inputs​​​​e $errorsà $_SESSIONvariável para que possa acessá-las na solicitação subsequente.

O seguinte define a redirect_with()função que adiciona os elementos do $itemsarray à $_SESSIONvariável e redireciona para uma URL:

function redirect_with(string $url, array $items): void
{
    foreach ($items as $key => $value) {
        $_SESSION[$key] = $value;
    }

    redirect_to($url);
}Linguagem de código:  PHP  ( php )

Observe que a redirect_with()função chama a redirect_to()função para redirecionar os usuários para uma URL.

A seguir mostramos como usar a redirect_with()função que adiciona o array $inputsand $errorse redireciona para a register.phppágina:

redirect_with('register.php', [
   'inputs' => $inputs,
   'errors' => $errors
]);Linguagem de código:  PHP  ( php )

Se quiser definir uma mensagem flash e redirecionar para outra página, você pode definir uma nova função auxiliar redirect_with_message()como esta:

function redirect_with_message(string $url, string $message, string $type=FLASH_SUCCESS)
{
    flash('flash_' . uniqid(), $message, $type);
    redirect_to($url);

}Linguagem de código:  PHP  ( php )

Observe que o nome da mensagem flash começa com flash_e é seguido por um id exclusivo retornado pela uniqid()função. Será assim:

flash_615481fce49e8

Usamos um nome gerado para a mensagem flash porque não é importante neste caso. E exibiremos todas as mensagens flash de qualquer maneira.

Por exemplo, você pode redirecionar os usuários para a página de login com uma mensagem de sucesso como esta:

redirect_with_message(
    'login.php',
    'Your account has been created successfully. Please login here.'
);Linguagem de código:  JavaScript  ( javascript )

Dados da sessão Flash

Para obter dados $_SESSIONe removê-los imediatamente, você pode definir uma função auxiliar session_flash():

function session_flash(...$keys): array
{
    $data = [];
    foreach ($keys as $key) {
        if (isset($_SESSION[$key])) {
            $data[] = $_SESSION[$key];
            unset($_SESSION[$key]);
        } else {
            $data[] = [];
        }
    }
    return $data;
}Linguagem de código:  PHP  ( php )

A session_flash()função aceita um número variável de chaves. Também é conhecida como função variadica .

Se existir uma chave na $_SESSIONvariável, a função adiciona o valor dessa chave à matriz de retorno e desativa o valor. Caso contrário, a função retornará um array vazio para essa chave.

O seguinte usa a desestruturação do array para obter $inputse $errorsda $_SESSIONvariável e desconfigurá-los usando a session_flash()função:

[$errors, $inputs] = session_flash('errors', 'inputs');Linguagem de código:  PHP  ( php )

A boostrap.phpfunção é atualizada para incluir a chamada à session_start()função e o flash.phparquivo:

<?php

session_start();
require_once __DIR__ . '/libs/helpers.php';
require_once __DIR__ . '/libs/flash.php';
require_once __DIR__ . '/libs/sanitization.php';
require_once __DIR__ . '/libs/validation.php';
require_once __DIR__ . '/libs/filter.php';Linguagem de código:  HTML, XML  ( xml )

Observe que você precisa chamar a session_start()função para iniciar uma nova sessão ou retomar uma existente para gerenciar os dados da sessão.

O arquivo Register.php completo

A seguir mostra o register.phparquivo completo:

<?php

require __DIR__ . '/../src/bootstrap.php';

$errors = [];
$inputs = [];

if (is_post_request()) {

    $fields = [
        'username' => 'string | required | alphanumeric | between: 3, 25 | unique: users, username',
        'email' => 'email | required | email | unique: users, email',
        'password' => 'string | required | secure',
        'password2' => 'string | required | same: password',
        'agree' => 'string | required'
    ];

    // custom messages
    $messages = [
        'password2' => [
            'required' => 'Please enter the password again',
            'same' => 'The password does not match'
        ],
        'agree' => [
            'required' => 'You need to agree to the term of services to register'
        ]
    ];

    [$inputs, $errors] = filter($_POST, $fields, $messages);

    if ($errors) {
        redirect_with('register.php', [
            'inputs' => $inputs,
            'errors' => $errors
        ]);
    }

    if (register_user($inputs['email'], $inputs['username'], $inputs['password'])) {
        redirect_with_message(
            'login.php',
            'Your account has been created successfully. Please login here.'
        );

    }

} else if (is_get_request()) {
    [$inputs, $errors] = session_flash('inputs', 'errors');
}

?>

<?php view('header', ['title' => 'Register']) ?>

<form action="register.php" method="post">
    <h1>Sign Up</h1>
    <div>
        <label for="username">Username:</label>
        <input type="text" name="username" id="username" value="<?= $inputs['username'] ?? '' ?>"
               class="<?= error_class($errors, 'username') ?>">
        <small><?= $errors['username'] ?? '' ?></small>
    </div>

    <div>
        <label for="email">Email:</label>
        <input type="email" name="email" id="email" value="<?= $inputs['email'] ?? '' ?>"
               class="<?= error_class($errors, 'email') ?>">
        <small><?= $errors['email'] ?? '' ?></small>
    </div>

    <div>
        <label for="password">Password:</label>
        <input type="password" name="password" id="password" value="<?= $inputs['password'] ?? '' ?>"
               class="<?= error_class($errors, 'password') ?>">
        <small><?= $errors['password'] ?? '' ?></small>
    </div>

    <div>
        <label for="password2">Password Again:</label>
        <input type="password" name="password2" id="password2" value="<?= $inputs['password2'] ?? '' ?>"
               class="<?= error_class($errors, 'password2') ?>">
        <small><?= $errors['password2'] ?? '' ?></small>
    </div>

    <div>
        <label for="agree">
            <input type="checkbox" name="agree" id="agree" value="checked" <?= $inputs['agree'] ?? '' ?> /> I
            agree
            with the
            <a href="#" title="term of services">term of services</a>
        </label>
        <small><?= $errors['agree'] ?? '' ?></small>
    </div>

    <button type="submit">Register</button>

    <footer>Already a member? <a href="login.php">Login here</a></footer>

</form>

<?php view('footer') ?>
Linguagem de código:  PHP  ( php )

Para separar as partes lógicas e de visualização, você pode criar um novo arquivo register.phpna srcpasta e adicionar a primeira parte do public/register.phparquivo a ele:

<?php

$errors = [];
$inputs = [];

if (is_post_request()) {

    $fields = [
        'username' => 'string | required | alphanumeric | between: 3, 25 | unique: users, username',
        'email' => 'email | required | email | unique: users, email',
        'password' => 'string | required | secure',
        'password2' => 'string | required | same: password',
        'agree' => 'string | required'
    ];

    // custom messages
    $messages = [
        'password2' => [
            'required' => 'Please enter the password again',
            'same' => 'The password does not match'
        ],
        'agree' => [
            'required' => 'You need to agree to the term of services to register'
        ]
    ];

    [$inputs, $errors] = filter($_POST, $fields, $messages);

    if ($errors) {
        redirect_with('register.php', [
            'inputs' => $inputs,
            'errors' => $errors
        ]);
    }

    if (register_user($inputs['email'], $inputs['username'], $inputs['password'])) {
        redirect_with_message(
            'login.php',
            'Your account has been created successfully. Please login here.'
        );

    }

} else if (is_get_request()) {
    [$inputs, $errors] = session_flash('inputs', 'errors');
}Linguagem de código:  HTML, XML  ( xml )

E o seguinte mostra o public/register.phparquivo:

<?php

require __DIR__ . '/../src/bootstrap.php';
require __DIR__ . '/../src/register.php';
?>

<?php view('header', ['title' => 'Register']) ?>

<form action="register.php" method="post">
    <h1>Sign Up</h1>

    <div>
        <label for="username">Username:</label>
        <input type="text" name="username" id="username" value="<?= $inputs['username'] ?? '' ?>"
               class="<?= error_class($errors, 'username') ?>">
        <small><?= $errors['username'] ?? '' ?></small>
    </div>

    <div>
        <label for="email">Email:</label>
        <input type="email" name="email" id="email" value="<?= $inputs['email'] ?? '' ?>"
               class="<?= error_class($errors, 'email') ?>">
        <small><?= $errors['email'] ?? '' ?></small>
    </div>

    <div>
        <label for="password">Password:</label>
        <input type="password" name="password" id="password" value="<?= $inputs['password'] ?? '' ?>"
               class="<?= error_class($errors, 'password') ?>">
        <small><?= $errors['password'] ?? '' ?></small>
    </div>

    <div>
        <label for="password2">Password Again:</label>
        <input type="password" name="password2" id="password2" value="<?= $inputs['password2'] ?? '' ?>"
               class="<?= error_class($errors, 'password2') ?>">
        <small><?= $errors['password2'] ?? '' ?></small>
    </div>

    <div>
        <label for="agree">
            <input type="checkbox" name="agree" id="agree" value="checked" <?= $inputs['agree'] ?? '' ?> /> I
            agree
            with the
            <a href="#" title="term of services">term of services</a>
        </label>
        <small><?= $errors['agree'] ?? '' ?></small>
    </div>

    <button type="submit">Register</button>

    <footer>Already a member? <a href="login.php">Login here</a></footer>

</form>

<?php view('footer') ?>
Linguagem de código:  JavaScript  ( javascript )

Como a register_user()função não existe, você precisa defini-la. Antes de fazer isso, você precisa criar um novo banco de dados e a userstabela.

Crie um novo banco de dados e a tabela de usuários

Usaremos MySQL para armazenar as informações do usuário. Para interagir com o MySQL, você pode usar qualquer ferramenta cliente MySQL, como phpmyadmin ou mysql.

Primeiro, crie um novo banco de dados chamado authno servidor de banco de dados MySQL:

CREATE DATABASE auth;Linguagem de código:  SQL (linguagem de consulta estruturada)  ( sql )

Segundo, crie uma nova tabela chamada userspara armazenar as informações do usuário:

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(25) NOT NULL UNIQUE,
    email VARCHAR(320) NOT NULL UNIQUE,
    password VARCHAR(256) NOT NULL,
    is_admin TINYINT(1) not null default 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);Linguagem de código:  SQL (linguagem de consulta estruturada)  ( sql )

A userstabela possui as seguintes colunas:

  • idé a chave primária . Como o id é uma coluna de incremento automático, o MySQL aumentará seu valor em um para cada nova linha. Em outras palavras, você não precisa fornecer o id ao inserir uma nova linha na tabela.
  • usernameé uma coluna de caracteres variável com as restrições NOT NULLe UNIQUE. Isso significa que não haverá duas linhas com o mesmo nome de usuário.
  • emailcoluna é como a usernamecoluna, que é NOT NULLe UNIQUE.
  • passwordcoluna uma coluna variável e não nula.
  • is_adminé uma pequena coluna inteira. Seu valor padrão é zero. Se is_adminfor zero, o usuário não é o administrador. Se is_adminfor 1, o usuário é um administrador que tem mais privilégios do que usuários normais.
  • created_até uma coluna de carimbo de data/hora que o MySQL irá atualizar para o carimbo de data/hora atual quando você inserir uma nova linha na tabela.
  • updated_até uma coluna de data e hora em que o MySQL irá atualizá-la para o carimbo de data/hora atual automaticamente quando você atualizar uma linha existente.

Conecte-se ao banco de dados MySQL

Para se conectar ao banco de dados MySQL , você usará a biblioteca de objetos de dados PHP (ou PDO) .

Primeiro, crie um novo arquivo database.phpna configpasta e adicione os seguintes parâmetros de banco de dados ao arquivo:

<?php

const DB_HOST = 'localhost';
const DB_NAME = 'auth';
const DB_USER = 'root';
const DB_PASSWORD = '';Linguagem de código:  PHP  ( php )

Segundo, crie o connection.phparquivo na src/libspasta.

Terceiro, defina a db()função que conecta o banco de dados uma vez e retorna um novo objeto PDO. A db()função usa a configuração do banco de dados definida no config/database.phparquivo.

function db(): PDO
{
    static $pdo;

    if (!$pdo) {
        $pdo = new PDO(
            sprintf("mysql:host=%s;dbname=%s;charset=UTF8", DB_HOST, DB_NAME),
            DB_USER,
            DB_PASSWORD,
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
        );
    }
    return $pdo;
}
Linguagem de código:  PHP  ( php )

Na db()função, the $pdoé uma variável estática. Quando você chama a db()função pela primeira vez, a $pdovariável não é inicializada. Portanto, ifé executado o bloco de código dentro da instrução que se conecta ao banco de dados e retorna um novo objeto PDO.

Como the $pdoé uma variável estática, ela ainda estará ativa após a db()conclusão da função function. Portanto, quando você chama a db()função novamente, ela retorna o objeto PDO. Em outras palavras, a função não se conecta novamente ao banco de dados.

Como criar uma conexão com o banco de dados é caro em termos de tempo e recursos, você deve conectar-se ao banco de dados uma vez por solicitação.

Defina a função register_user()

Primeiro, crie um novo arquivo chamado auth.phpna srcpasta.

Segundo, defina a register_user()função que insere um novo usuário na userstabela:

function register_user(string $email, string $username, string $password, bool $is_admin = false): bool
{
    $sql = 'INSERT INTO users(username, email, password, is_admin)
            VALUES(:username, :email, :password, :is_admin)';

    $statement = db()->prepare($sql);

    $statement->bindValue(':username', $username, PDO::PARAM_STR);
    $statement->bindValue(':email', $email, PDO::PARAM_STR);
    $statement->bindValue(':password', password_hash($password, PASSWORD_BCRYPT), PDO::PARAM_STR);
    $statement->bindValue(':is_admin', (int)$is_admin, PDO::PARAM_INT);

    return $statement->execute();
}Linguagem de código:  PHP  ( php )

Ao armazenar uma senha no banco de dados, você nunca a armazena em texto simples por motivos de segurança. Em vez disso, você deve sempre fazer o hash da senha antes de armazená-la.

Para fazer o hash de uma senha, você usa a função integrada password_hash():

password_hash($password, PASSWORD_BCRYPT)Linguagem de código:  PHP  ( php )

Por exemplo, se a senha for Password1, a password_hash()função retornará o seguinte hash:

<?php

echo password_hash('Password1', PASSWORD_BCRYPT);Linguagem de código:  PHP  ( php )

Saída:

$2y$10$QlUdCEXY68bswdVsKlE.5OjHa7X8fvtCmlYLnIkfvbcGd..mqDfwqLinguagem de código:  texto simples  ( texto simples )

O PASSWORD_BCRYPTargumento instrui a password_hash()função a usar o CRYPT_BLOWFISH algoritmo que é muito seguro.

Como password_hash()é uma função unidirecional, os hackers não podem “descriptografá-la” para o texto simples original ( Password1). Isso fornece uma defesa contra o comprometimento de senhas quando um banco de dados vaza.

Junte tudo

A pasta do projeto e a estrutura do arquivo serão semelhantes a esta:

├── config
|  └── database.php
├── public
|  └── register.php
└── src
   ├── auth.php
   ├── bootstrap.php
   ├── inc
   |  ├── footer.php
   |  └── header.php
   ├── libs
   |  ├── connection.php
   |  ├── flash.php
   |  ├── helpers.php
   |  ├── sanitization.php
   |  ├── validation.php
   |  └── filter.php
   └── register.phpLinguagem de código:  PHP  ( php )

arquivo config/database.php

<?php

const DB_HOST = 'localhost';
const DB_NAME = 'auth';
const DB_USER = 'root';
const DB_PASSWORD = '';Linguagem de código:  PHP  ( php )

arquivo inc/auth.php

/**
* Register a user
*
* @param string $email
* @param string $username
* @param string $password
* @param bool $is_admin
* @return bool
*/
function register_user(string $email, string $username, string $password, bool $is_admin = false): bool
{
    $sql = 'INSERT INTO users(username, email, password, is_admin)
            VALUES(:username, :email, :password, :is_admin)';

    $statement = db()->prepare($sql);

    $statement->bindValue(':username', $username, PDO::PARAM_STR);
    $statement->bindValue(':email', $email, PDO::PARAM_STR);
    $statement->bindValue(':password', password_hash($password, PASSWORD_BCRYPT), PDO::PARAM_STR);
    $statement->bindValue(':is_admin', (int)$is_admin, PDO::PARAM_INT);


    return $statement->execute();
}Linguagem de código:  PHP  ( php )

src/libs/connection.php

<?php

/**
 * Connect to the database and returns an instance of PDO class
 * or false if the connection fails
 *
 * @return PDO
 */
function db(): PDO
{
    static $pdo;

    if (!$pdo) {
        $pdo = new PDO(
            sprintf("mysql:host=%s;dbname=%s;charset=UTF8", DB_HOST, DB_NAME),
            DB_USER,
            DB_PASSWORD,
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
        );
    }
    
    return $pdo;
}
Linguagem de código:  PHP  ( php )

Arquivo src/libs/flash.php

<?php

const FLASH = 'FLASH_MESSAGES';

const FLASH_ERROR = 'error';
const FLASH_WARNING = 'warning';
const FLASH_INFO = 'info';
const FLASH_SUCCESS = 'success';

/**
* Create a flash message
*
* @param string $name
* @param string $message
* @param string $type
* @return void
*/
function create_flash_message(string $name, string $message, string $type): void
{
    // remove existing message with the name
    if (isset($_SESSION[FLASH][$name])) {
        unset($_SESSION[FLASH][$name]);
    }
    // add the message to the session
    $_SESSION[FLASH][$name] = ['message' => $message, 'type' => $type];
}


/**
* Format a flash message
*
* @param array $flash_message
* @return string
*/
function format_flash_message(array $flash_message): string
{
    return sprintf('<div class="alert alert-%s">%s</div>',
        $flash_message['type'],
        $flash_message['message']
    );
}

/**
* Display a flash message
*
* @param string $name
* @return void
*/
function display_flash_message(string $name): void
{
    if (!isset($_SESSION[FLASH][$name])) {
        return;
    }

    // get message from the session
    $flash_message = $_SESSION[FLASH][$name];

    // delete the flash message
    unset($_SESSION[FLASH][$name]);

    // display the flash message
    echo format_flash_message($flash_message);
}

/**
* Display all flash messages
*
* @return void
*/
function display_all_flash_messages(): void
{
    if (!isset($_SESSION[FLASH])) {
        return;
    }

    // get flash messages
    $flash_messages = $_SESSION[FLASH];

    // remove all the flash messages
    unset($_SESSION[FLASH]);

    // show all flash messages
    foreach ($flash_messages as $flash_message) {
        echo format_flash_message($flash_message);
    }
}

/**
* Flash a message
*
* @param string $name
* @param string $message
* @param string $type (error, warning, info, success)
* @return void
*/
function flash(string $name = '', string $message = '', string $type = ''): void
{
    if ($name !== '' && $message !== '' && $type !== '') {
        // create a flash message
        create_flash_message($name, $message, $type);
    } elseif ($name !== '' && $message === '' && $type === '') {
        // display a flash message
        display_flash_message($name);
    } elseif ($name === '' && $message === '' && $type === '') {
        // display all flash message
        display_all_flash_messages();
    }
}Linguagem de código:  PHP  ( php )

src/inc/header.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://tutorials.acervolima.com/app/css/style.css">
    <title><?= $title ?? 'Home' ?></title>
</head>
<body>
<main>
<?php flash() ?>
Linguagem de código:  PHP  ( php )

Arquivo src/inc/footer.php

</main>
</body>
</html>Linguagem de código:  PHP  ( php )

src/libs/helpers.php

<?php

/**
 * Display a view
 *
 * @param string $filename
 * @param array $data
 * @return void
 */
function view(string $filename, array $data = []): void
{
    // create variables from the associative array
    foreach ($data as $key => $value) {
        $$key = $value;
    }
    require_once __DIR__ . '/../inc/' . $filename . '.php';
}


/**
 * Return the error class if error is found in the array $errors
 *
 * @param array $errors
 * @param string $field
 * @return string
 */
function error_class(array $errors, string $field): string
{
    return isset($errors[$field]) ? 'error' : '';
}

/**
 * Return true if the request method is POST
 *
 * @return boolean
 */
function is_post_request(): bool
{
    return strtoupper($_SERVER['REQUEST_METHOD']) === 'POST';
}

/**
 * Return true if the request method is GET
 *
 * @return boolean
 */
function is_get_request(): bool
{
    return strtoupper($_SERVER['REQUEST_METHOD']) === 'GET';
}

/**
 * Redirect to another URL
 *
 * @param string $url
 * @return void
 */
function redirect_to(string $url): void
{
    header('Location:' . $url);
    exit;
}

/**
 * Redirect to a URL with data stored in the items array
 * @param string $url
 * @param array $items
 */
function redirect_with(string $url, array $items): void
{
    foreach ($items as $key => $value) {
        $_SESSION[$key] = $value;
    }

    redirect_to($url);
}

/**
 * Redirect to a URL with a flash message
 * @param string $url
 * @param string $message
 * @param string $type
 */
function redirect_with_message(string $url, string $message, string $type = FLASH_SUCCESS)
{
    flash('flash_' . uniqid(), $message, $type);
    redirect_to($url);
}

/**
 * Flash data specified by $keys from the $_SESSION
 * @param ...$keys
 * @return array
 */
function session_flash(...$keys): array
{
    $data = [];
    foreach ($keys as $key) {
        if (isset($_SESSION[$key])) {
            $data[] = $_SESSION[$key];
            unset($_SESSION[$key]);
        } else {
            $data[] = [];
        }
    }
    return $data;
}
Linguagem de código:  PHP  ( php )

inc/sanitização.php

<?php
const FILTERS = [
    'string' => FILTER_SANITIZE_STRING,
    'string[]' => [
        'filter' => FILTER_SANITIZE_STRING,
        'flags' => FILTER_REQUIRE_ARRAY
    ],
    'email' => FILTER_SANITIZE_EMAIL,
    'int' => [
        'filter' => FILTER_SANITIZE_NUMBER_INT,
        'flags' => FILTER_REQUIRE_SCALAR
    ],
    'int[]' => [
        'filter' => FILTER_SANITIZE_NUMBER_INT,
        'flags' => FILTER_REQUIRE_ARRAY
    ],
    'float' => [
        'filter' => FILTER_SANITIZE_NUMBER_FLOAT,
        'flags' => FILTER_FLAG_ALLOW_FRACTION
    ],
    'float[]' => [
        'filter' => FILTER_SANITIZE_NUMBER_FLOAT,
        'flags' => FILTER_REQUIRE_ARRAY
    ],
    'url' => FILTER_SANITIZE_URL,
];

/**
* Recursively trim strings in an array
* @param array $items
* @return array
*/
function array_trim(array $items): array
{
    return array_map(function ($item) {
        if (is_string($item)) {
            return trim($item);
        } elseif (is_array($item)) {
            return array_trim($item);
        } else
            return $item;
    }, $items);
}

/**
* Sanitize the inputs based on the rules an optionally trim the string
* @param array $inputs
* @param array $fields
* @param int $default_filter FILTER_SANITIZE_STRING
* @param array $filters FILTERS
* @param bool $trim
* @return array
*/
function sanitize(array $inputs, array $fields = [], int $default_filter = FILTER_SANITIZE_STRING, array $filters = FILTERS, bool $trim = true): array
{
    if ($fields) {
        $options = array_map(fn($field) => $filters[$field], $fields);
        $data = filter_var_array($inputs, $options);
    } else {
        $data = filter_var_array($inputs, $default_filter);
    }

    return $trim ? array_trim($data) : $data;
}Linguagem de código:  PHP  ( php )

src/libs/validação.php

<?php


const DEFAULT_VALIDATION_ERRORS = [
    'required' => 'The %s is required',
    'email' => 'The %s is not a valid email address',
    'min' => 'The %s must have at least %s characters',
    'max' => 'The %s must have at most %s characters',
    'between' => 'The %s must have between %d and %d characters',
    'same' => 'The %s must match with %s',
    'alphanumeric' => 'The %s should have only letters and numbers',
    'secure' => 'The %s must have between 8 and 64 characters and contain at least one number, one upper case letter, one lower case letter and one special character',
    'unique' => 'The %s already exists',
];


/**
* Validate
* @param array $data
* @param array $fields
* @param array $messages
* @return array
*/
function validate(array $data, array $fields, array $messages = []): array
{
    // Split the array by a separator, trim each element
    // and return the array
    $split = fn($str, $separator) => array_map('trim', explode($separator, $str));

    // get the message rules
    $rule_messages = array_filter($messages, fn($message) => is_string($message));
    // overwrite the default message
    $validation_errors = array_merge(DEFAULT_VALIDATION_ERRORS, $rule_messages);

    $errors = [];

    foreach ($fields as $field => $option) {

        $rules = $split($option, '|');

        foreach ($rules as $rule) {
            // get rule name params
            $params = [];
            // if the rule has parameters e.g., min: 1
            if (strpos($rule, ':')) {
                [$rule_name, $param_str] = $split($rule, ':');
                $params = $split($param_str, ',');
            } else {
                $rule_name = trim($rule);
            }
            // by convention, the callback should be is_<rule> e.g.,is_required
            $fn = 'is_' . $rule_name;

            if (is_callable($fn)) {
                $pass = $fn($data, $field, ...$params);
                if (!$pass) {
                    // get the error message for a specific field and rule if exists
                    // otherwise get the error message from the $validation_errors
                    $errors[$field] = sprintf(
                        $messages[$field][$rule_name] ?? $validation_errors[$rule_name],
                        $field,
                        ...$params
                    );
                }
            }
        }
    }

    return $errors;
}

/**
* Return true if a string is not empty
* @param array $data
* @param string $field
* @return bool
*/
function is_required(array $data, string $field): bool
{
    return isset($data[$field]) && trim($data[$field]) !== '';
}

/**
* Return true if the value is a valid email
* @param array $data
* @param string $field
* @return bool
*/
function is_email(array $data, string $field): bool
{
    if (empty($data[$field])) {
        return true;
    }

    return filter_var($data[$field], FILTER_VALIDATE_EMAIL);
}

/**
* Return true if a string has at least min length
* @param array $data
* @param string $field
* @param int $min
* @return bool
*/
function is_min(array $data, string $field, int $min): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    return mb_strlen($data[$field]) >= $min;
}

/**
* Return true if a string cannot exceed max length
* @param array $data
* @param string $field
* @param int $max
* @return bool
*/
function is_max(array $data, string $field, int $max): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    return mb_strlen($data[$field]) <= $max;
}

/**
* @param array $data
* @param string $field
* @param int $min
* @param int $max
* @return bool
*/
function is_between(array $data, string $field, int $min, int $max): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    $len = mb_strlen($data[$field]);
    return $len >= $min && $len <= $max;
}

/**
* Return true if a string equals the other
* @param array $data
* @param string $field
* @param string $other
* @return bool
*/
function is_same(array $data, string $field, string $other): bool
{
    if (isset($data[$field], $data[$other])) {
        return $data[$field] === $data[$other];
    }

    if (!isset($data[$field]) && !isset($data[$other])) {
        return true;
    }

    return false;
}

/**
* Return true if a string is alphanumeric
* @param array $data
* @param string $field
* @return bool
*/
function is_alphanumeric(array $data, string $field): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    return ctype_alnum($data[$field]);
}

/**
* Return true if a password is secure
* @param array $data
* @param string $field
* @return bool
*/
function is_secure(array $data, string $field): bool
{
    if (!isset($data[$field])) {
        return false;
    }

    $pattern = "#.*^(?=.{8,64})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*\W).*$#";
    return preg_match($pattern, $data[$field]);
}

/**
* Return true if the $value is unique in the column of a table
* @param array $data
* @param string $field
* @param string $table
* @param string $column
* @return bool
*/
function is_unique(array $data, string $field, string $table, string $column): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    $sql = "SELECT $column FROM $table WHERE $column = :value";

    $stmt = db()->prepare($sql);
    $stmt->bindValue(":value", $data[$field]);

    $stmt->execute();

    return $stmt->fetchColumn() === false;
}Linguagem de código:  PHP  ( php )

src/libs/filter.php

<?php

/**
 * Sanitize and validate data
 * @param array $data
 * @param array $fields
 * @param array $messages
 * @return array
 */
function filter(array $data, array $fields, array $messages = []): array
{
    $sanitization = [];
    $validation = [];

    // extract sanitization & validation rules
    foreach ($fields as $field => $rules) {
        if (strpos($rules, '|')) {
            [$sanitization[$field], $validation[$field]] = explode('|', $rules, 2);
        } else {
            $sanitization[$field] = $rules;
        }
    }

    $inputs = sanitize($data, $sanitization);
    $errors = validate($inputs, $validation, $messages);

    return [$inputs, $errors];
}Linguagem de código:  HTML, XML  ( xml )

src/bootstrap.php

<?php

<?php

session_start();
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/libs/helpers.php';
require_once __DIR__ . '/libs/flash.php';
require_once __DIR__ . '/libs/sanitization.php';
require_once __DIR__ . '/libs/validation.php';
require_once __DIR__ . '/libs/filter.php';
require_once __DIR__ . '/libs/connection.php';
require_once __DIR__ . '/auth.php';Linguagem de código:  PHP  ( php )

público/registro.php

<?php
require __DIR__ . '/../src/bootstrap.php';
require __DIR__ . '/../src/register.php';
?>

<?php view('header', ['title' => 'Register']) ?>

<form action="register.php" method="post">
    <h1>Sign Up</h1>

    <div>
        <label for="username">Username:</label>
        <input type="text" name="username" id="username" value="<?= $inputs['username'] ?? '' ?>"
               class="<?= error_class($errors, 'username') ?>">
        <small><?= $errors['username'] ?? '' ?></small>
    </div>

    <div>
        <label for="email">Email:</label>
        <input type="email" name="email" id="email" value="<?= $inputs['email'] ?? '' ?>"
               class="<?= error_class($errors, 'email') ?>">
        <small><?= $errors['email'] ?? '' ?></small>
    </div>

    <div>
        <label for="password">Password:</label>
        <input type="password" name="password" id="password" value="<?= $inputs['password'] ?? '' ?>"
               class="<?= error_class($errors, 'password') ?>">
        <small><?= $errors['password'] ?? '' ?></small>
    </div>

    <div>
        <label for="password2">Password Again:</label>
        <input type="password" name="password2" id="password2" value="<?= $inputs['password2'] ?? '' ?>"
               class="<?= error_class($errors, 'password2') ?>">
        <small><?= $errors['password2'] ?? '' ?></small>
    </div>

    <div>
        <label for="agree">
            <input type="checkbox" name="agree" id="agree" value="checked" <?= $inputs['agree'] ?? '' ?> /> I
            agree
            with the
            <a href="#" title="term of services">term of services</a>
        </label>
        <small><?= $errors['agree'] ?? '' ?></small>
    </div>

    <button type="submit">Register</button>

    <footer>Already a member? <a href="login.php">Login here</a></footer>

</form>

<?php view('footer') ?>
Linguagem de código:  PHP  ( php )

src/register.php

<?php

$errors = [];
$inputs = [];

if (is_post_request()) {

    $fields = [
        'username' => 'string | required | alphanumeric | between: 3, 25 | unique: users, username',
        'email' => 'email | required | email | unique: users, email',
        'password' => 'string | required | secure',
        'password2' => 'string | required | same: password',
        'agree' => 'string | required'
    ];

    // custom messages
    $messages = [
        'password2' => [
            'required' => 'Please enter the password again',
            'same' => 'The password does not match'
        ],
        'agree' => [
            'required' => 'You need to agree to the term of services to register'
        ]
    ];

    [$inputs, $errors] = filter($_POST, $fields, $messages);

    if ($errors) {
        redirect_with('register.php', [
            'inputs' => $inputs,
            'errors' => $errors
        ]);
    }

    if (register_user($inputs['email'], $inputs['username'], $inputs['password'])) {
        redirect_with_message(
            'login.php',
            'Your account has been created successfully. Please login here.'
        );

    }

} else if (is_get_request()) {
    [$inputs, $errors] = session_flash('inputs', 'errors');
}Linguagem de código:  PHP  ( php )

público/login.php

<?php

require __DIR__ . '/../src/bootstrap.php';Linguagem de código:  HTML, XML  ( xml )

O login.phpficará em branco. E você aprenderá como criar o formulário de login no próximo tutorial .

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *