Lección 18: Registro

Registrar un usuario en nuestra plataforma es sencillo, solo hay que guardar los datos en la base de datos. Pero el asunto se complica cuanto tenemos que comprobar si es un ser humano o un androide. Lo idóneo estaría en una entrevista con una caja que emitiera pitiditos y una lupa apuntando al ojo de nuestro sospechoso mientras les hacemos preguntas sobre tortugas (estilo Blade Runner), pero en la actualidad nos vamos a conformar con saber si la cuenta de email es real. Y la mejor estrategia es enviar un correo con un texto secreto (token) que nosotros conozcamos previamente.

Los pasos para hace un buen registro y que no se nos escape ningún Replicante serían:

  1. Pedir los datos para crear la cuenta.
  2. Validar que los datos son compatibles con la base de datos (el email no existe, tiene un formato válido…).
  3. Guardarlos en la base de datos, y marcamos que de momento no son hábiles.
  4. Enviamos un token a la dirección de email que nos han dado para comprobar que es un usuario real.
  5. Activar cuenta si pulsan sobre el enlace y el token es correcto.
  6. Llevar a la página de identificación y notificamos que su cuenta ha sido activada.

Ejemplo completo

La base de datos tiene una tabla llamada usuarios con la estructura:

CampoTipoClave primaria
emailStringSi
passwordStringNo
activoIntNo
tokenStringNo

Puedes descarga el SQLite preparado compatible con el ejemplo.

registro.php

Tendremos nuestro formulario, la validación de que el email no existe en la base de datos, la capacidad para guardar los datos y enviar el correo con el enlace para validar nuestra cuenta.

<?php
//======================================================================
// PROCESAR FORMULARIO 
//======================================================================

//-----------------------------------------------------
// Funciones Para Validar
//-----------------------------------------------------

/**
 * Método que valida si un texto no esta vacío
 * @param {string} - Texto a validar
 * @return {boolean}
 */
function validar_requerido(string $texto): bool
{
    return !(trim($texto) == '');
}

/**
 * Método que valida si el texto tiene un formato válido de E-Mail
 * @param {string} - Email
 * @return {bool}
 */
function validar_email(string $texto): bool
{
    return filter_var($texto, FILTER_VALIDATE_EMAIL);
}

//-----------------------------------------------------
// Variables
//-----------------------------------------------------
$errores = [];
$email = isset($_REQUEST['email']) ? $_REQUEST['email'] : '';
$password = isset($_REQUEST['password']) ? $_REQUEST['password'] : '';

// Comprobamos si nos llega los datos por POST
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    
    //-----------------------------------------------------
    // Validaciones
    //-----------------------------------------------------
    // Email
    if (!validar_requerido($email)) {
        $errores[] = 'Campo Email obligatorio.';
    }

    if (!validar_email($email)) {
        $errores[] = 'Campo Email no tiene un formato válido';
    }

    // Contraseña
    if (!validar_requerido($password)) {
        $errores[] = 'Campo Contraseña obligatorio.';
    }

    /* Verificar que no existe en la base de datos el mismo email */
    // Conecta con base de datos
    $miPDO = new PDO('sqlite:base-de-datos.sqlite');
    // Cuenta cuantos emails existen
    $miConsulta = $miPDO->prepare('SELECT COUNT(*) as length FROM usuarios WHERE email = :email;');
    // Ejecuta la busqueda
    $miConsulta->execute([
        'email' => $email
    ]);
    // Recoge los resultados
    $resultado = $miConsulta->fetch();
    // Comprueba si existe
    if ((int) $resultado['length'] > 0) {
        $errores[] = 'La dirección de email ya esta registrada.';
    }

    //-----------------------------------------------------
    // Crear cuenta
    //-----------------------------------------------------
    if (count($errores) === 0) {
        /* Registro En La Base De Datos */

        // Prepara INSERT
        $token = bin2hex(openssl_random_pseudo_bytes(16));
        $miNuevoRegistro = $miPDO->prepare('INSERT INTO usuarios (email, password, activo, token) VALUES (:email, :password, :activo, :token);');
        // Ejecuta el nuevo registro en la base de datos
        $miNuevoRegistro->execute([
            'email' => $email,
            'password' => password_hash($password, PASSWORD_BCRYPT),
            'activo' => 0,
            'token' => $token
        ]);

        /* Envío De Email Con Token */

        // Cabecera
        $headers = [
            'From' => 'curso@php.com',
            'Content-type' => 'text/plain; charset=utf-8'
        ];
        // Variables para el email
        $emailEncode = urlencode($email);
        $tokenEncode = urlencode($token);
        // Texto del email
        $textoEmail = "
           Hola!\n 
           Gracias por registrate en la mejor plataforma de internet, demuestras inteligencia.\n
           Para activar entra en el siguiente enlace:\n
           http://midomino.com/verificar-cuenta.php?email=$emailEncode&token=$tokenEncode
            ";
        // Envio del email
        mail($email, 'Activa tu cuenta', 'Gracias por suscribirte', $headers);

        /* Redirección a login.php con GET para informar del envio del email */

        header('Location: identificarse.php?registrado=1');
        die();
    }
}
?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>Registro</h1>
    <!-- Mostramos errores por HTML -->
    <?php if (isset($errores)): ?>
    <ul class="errores">
        <?php 
            foreach ($errores as $error) {
                echo '<li>' . $error . '</li>';
            } 
        ?> 
    </ul>
    <?php endif; ?>
    <!-- Formulario -->
    <form action="" method="post">
        <div>
            <!-- Campo de Email -->
            <label>
                E-mail
                <input type="text" name="email">
            </label>
        </div>
        <div>
            <!-- Campo de Contraseña -->
            <label>
                Contraseña
                <input type="text" name="password">
            </label>
        </div>
        <div>
            <!-- Botón submit -->
            <input type="submit" value="Registrarse">
        </div>
    </form>
</body>
</html>

verificar-cuenta.php

Se llegará a través del enlace enviado después del registro. Si el email y token coinciden con lo guardado en la base de datos, cambiaremos el campo activo a 1 (marcamos como habilitado o verificado).

<?php

//-----------------------------------------------------
// Variables
//-----------------------------------------------------
$email = isset($_REQUEST['email']) ? urldecode($_REQUEST['email']) : '';
$token = isset($_REQUEST['token']) ? urldecode($_REQUEST['token']) : '';

//-----------------------------------------------------
// COMPROBAR SI SON CORRECTOS LOS DATOS
//-----------------------------------------------------
// Conecta con base de datos
$miPDO = new PDO('sqlite:base-de-datos.sqlite');
// Prepara SELECT para obtener la contraseña almacenada del usuario
$miConsulta = $miPDO->prepare('SELECT COUNT(*) as length FROM usuarios WHERE email = :email AND token = :token AND activo = 0;');
// Ejecuta consulta
$miConsulta->execute([
    'email' => $email,
    'token' => $token
]);
$resultado = $miConsulta->fetch();
// Existe el usuario con el token
if ((bool) $resultado['length']) {
    //-----------------------------------------------------
    // ACTIVAR CUENTA
    //-----------------------------------------------------
    // Prepara la actualización
    $miActualiacion = $miPDO->prepare('UPDATE usuarios SET activo = 1 WHERE email = :email;');
    // Ejecuta actualización
    $miActualiacion->execute([
        'email' => $email
    ]);
    //-----------------------------------------------------
    // REDIRECCIONAR A IDENTIFICACION
    //-----------------------------------------------------
    header('Location: identificarse.php?activada=1');
    die();
}

// No es un usuario válido, le enviamos al formulario de identificación
header('Location: identificarse.php');
die();

identificarse.php

Muestra mensajes de información como cuenta activada o registrado, pero también es la encargada de comprobar si los datos son correctos. Y en ese caso, crear una sesión y llevar a la página privada.

<?php 
    //======================================================================
    // PROCESAR FORMULARIO 
    //======================================================================
    
    //-----------------------------------------------------
    // Variables
    //-----------------------------------------------------
    $email = isset($_REQUEST['email']) ? $_REQUEST['email'] : null;
    $password = isset($_REQUEST['contrasenya']) ? $_REQUEST['contrasenya'] : null;
    $errores = [];

    // Comprobamos que nos llega los datos del formulario
    if ($_SERVER['REQUEST_METHOD'] == 'POST') {

        //-----------------------------------------------------
        // COMPROBAR SI LA CUENTA ESTA ACTIVA
        //-----------------------------------------------------
        // Conecta con base de datos
        $miPDO = new PDO('sqlite:base-de-datos.sqlite');
        // Prepara SELECT para obtener la contraseña almacenada del usuario
        $miConsulta = $miPDO->prepare('SELECT activo, password FROM usuarios WHERE email = :email;');
        // Ejecuta consulta
        $miConsulta->execute([
            'email' => $email
        ]);
        // Guardo el resultado
        $resultado = $miConsulta->fetch();
        if ((int) $resultado['activo'] !== 1) {
            $errores[] = 'Tu cuenta aún no esta activa. ¿Has comprobado tu bandeja de correo?';
        } else {
            //-----------------------------------------------------
            // COMPROBAR LA CONTRASEÑA
            //-----------------------------------------------------
            // Comprobamos si es válida
            if (password_verify($password, $resultado['password'])) {
                // Si son correctos, creamos la sesión
                session_start();
                $_SESSION['email'] = $email;
                // Redireccionamos a la página segura
                header('Location: privado.php');
                die();
            } else {
                $errores[] = 'El email o la contraseña es incorrecta.';
            }
        }
    }
?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Entra</title>
</head>
<body>
    <h1>Entrar</h1>
    <!-- Mostramos errores por HTML -->
    <?php if (count($errores) > 0): ?>
    <ul class="errores">
        <?php 
            foreach ($errores as $error) {
                echo '<li>' . $error . '</li>';
            } 
        ?> 
    </ul>
    <?php endif; ?>
    <!-- Mensaje de aviso al registrarte -->
    <?php if(isset($_REQUEST['registrado'])): ?> 
    <p>¡Gracias por registrarte! Revista tu bandeja de correo para activar la cuenta.</p>
    <?php endif; ?> 
    <!-- Mensaje de cuenta activa -->
    <?php if(isset($_REQUEST['activada'])): ?> 
    <p>¡Cuenta activada!</p>
    <?php endif; ?> 
    <!-- Formulario de identificación -->
    <form method="post">
        <p>
            <input type="text" name="email" placeholder="Email"> 
        </p> 
        <p>
            <input type="password" name="contrasenya" placeholder="Contraseña"> 
        </p>
        <p>
            <input type="submit" value="Entrar"> 
        </p>
    </form>
</body>
</html>

privado.php

Al igual que la lección de Login, la zona privada a continuación de la identificación.

<?php
// Activa las sesiones
session_start();
// Comprueba si existe la sesión 'email', en caso contrario vuelve a la página de identificación
if (!isset($_SESSION['email'])) {
    header('Location: identificarse.php');
    die();
}
?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Panel secreto</title>
</head>
<body>
   <h1>Tu panel secreto</h1>
</body>
</html>