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
- Senha
- ConfirmaÇão Da Senha
- Caixa de seleção do contrato
- Botão de registro
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
users
tabela do banco de dados, defina uma mensagem flash , redirecione o usuário para alogin.php
pá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
└── libs
Linguagem 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.php
na public
pasta. Para acessar a register.php
página, você precisa usar o seguinte URL:
http://localhost/auth/public/register.php
Linguagem de código: texto simples ( texto simples )
Para remover o public
URL acima, você pode usar o módulo URL Rewrite do Apache Web Server. Para fazer isso, você precisa usar um .htaccess
arquivo.
Segundo, crie .htaccess
o 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 public
da URL. Se você abrir o URL http://localhost/auth/register.php
, verá um erro.
Para corrigir o erro, você precisará de outro .htaccess
arquivo na public
pasta.
Terceiro, crie outro .htaccess
na public
pasta 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.php
página sem usar o /public/
URL assim:
http://localhost/auth/register.php
Linguagem de código: texto simples ( texto simples )
Crie o formulário de inscrição
Primeiro, crie um formulário de registro no register.php
arquivo:
<!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.php
arquivo na src/inc
pasta e copie a seção de cabeçalho do register.php
arquivo para o header.php
arquivo:
<!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.php
arquivo na src/inc
pasta e copie a seção de rodapé do register.php
arquivo para o footer.php
arquivo:
</main>
</body>
</html>
Linguagem de código: HTML, XML ( xml )
Terceiro, inclua os arquivos header.php
e footer.php
da inc
pasta no register.php
arquivo. Normalmente, você usa a construção require
ou include
.
No entanto, você pode querer incluir os arquivos header.php
e footer.php
em 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.php
arquivo na src/libs
pasta e definir a view()
função que carrega o código de um arquivo PHP e passa dados para ele:
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 .php
extensã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.php
arquivo e torna o title
como uma variável no header.php
arquivo:
<?php view('header', ['title' => 'Register']) ?>
Linguagem de código: PHP ( php )
Como o header.php
arquivo aceita the title
como 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.php
arquivo, a title
variável será padronizada Home
se não estiver definida.
Para usar a view()
função no register.php
, é register.php
necessário incluir o helpers.php
.
Para incluir os arquivos centralmente, você pode criar um bootstrap.php
arquivo na src
pasta. O bootstrap.php
incluirá todos os arquivos necessários. E você inclui o bootstrap.php
arquivo no register.php
arquivo.
O boostrap.php
arquivo ficará assim:
<?php
require_once __DIR__ . '/libs/helpers.php';
Linguagem de código: PHP ( php )
E o register.php
arquivo 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.php
usando 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.php
arquivo 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.php
arquivo 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á true
se 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.php
arquivo 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.php
efilter.php
nasrc/libs
pasta. - Segundo, adicione o código a esses arquivos (veja o código no final deste tutorial).
- Terceiro, inclua esses arquivos no
bootstrap.php
arquivo.
O bootstrap.php
arquivo 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
$_POST
matriz armazena as entradas do usuário. - A
$fields
matriz associativa armazena as regras de todos os campos. - É
$messages
uma matriz multidimensional que especifica as mensagens personalizadas para as mesmas regras obrigatórias dos campospassword2
eagree
. Este argumento é opcional. Se você pular, afilter()
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.php
página usando a técnica post-redirect-get (PRG). Além disso, você precisa adicionar os arrays $inputs
e $errors
à $_SESSION
variável para que possa acessá-los na GET
solicitaçã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.php
pá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.php
nasrc/libs
pasta. - Segundo, adicione o código ao
flash.php
arquivo. - Terceiro, inclua o
flash.php
arquivo no formatobootstrap.php
.
Para criar uma mensagem flash no register.php
arquivo, 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.php
arquivo:
<!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 error
classe CSS.
O seguinte define a error_class()
função no src/libs/helpers.php
arquivo, que retorna a 'error'
classe se o $errors
array 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 exit
construção:
header ('Location: login.php');
exit;
Linguagem de código: PHP ( php )
O seguinte define uma função chamada redirect_to()
function no helpers.php
arquivo 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.php
página. Antes de fazer isso, você precisa adicionar as variáveis $inputs
e $errors
à $_SESSION
variável para que possa acessá-las na solicitação subsequente.
O seguinte define a redirect_with()
função que adiciona os elementos do $items
array à $_SESSION
variá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 $inputs
and $errors
e redireciona para a register.php
pá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 $_SESSION
e 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 $_SESSION
variá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 $inputs
e $errors
da $_SESSION
variável e desconfigurá-los usando a session_flash()
função:
[$errors, $inputs] = session_flash('errors', 'inputs');
Linguagem de código: PHP ( php )
A boostrap.php
função é atualizada para incluir a chamada à session_start()
função e o flash.php
arquivo:
<?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.php
arquivo 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.php
na src
pasta e adicionar a primeira parte do public/register.php
arquivo 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.php
arquivo:
<?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 users
tabela.
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 auth
no 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 users
para 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 users
tabela 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çõesNOT NULL
eUNIQUE
. Isso significa que não haverá duas linhas com o mesmo nome de usuário.email
coluna é como ausername
coluna, que éNOT NULL
eUNIQUE
.password
coluna uma coluna variável e não nula.is_admin
é uma pequena coluna inteira. Seu valor padrão é zero. Seis_admin
for zero, o usuário não é o administrador. Seis_admin
for 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.php
na config
pasta 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.php
arquivo na src/libs
pasta.
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.php
arquivo.
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 $pdo
variá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.php
na src
pasta.
Segundo, defina a register_user()
função que insere um novo usuário na users
tabela:
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..mqDfwq
Linguagem de código: texto simples ( texto simples )
O PASSWORD_BCRYPT
argumento 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.php
Linguagem 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.php
ficará em branco. E você aprenderá como criar o formulário de login no próximo tutorial .