PHP & MySQL Login/Registration System Tutorial

by Alex Braham 47 views

Hey there, fellow coders and aspiring web developers! Today, we're diving deep into something super fundamental yet incredibly powerful: creating a registration and login system using PHP and MySQL. Whether you're just starting out or looking to solidify your skills, this guide is for you, guys. We'll break down the entire process, from setting up your database to handling user authentication securely. So, grab your favorite coding beverage, and let's get building!

Why is a Registration and Login System So Important?

First off, why bother with this? Think about any website or app you use that requires you to sign up or log in – social media, online stores, even your email. A registration and login system is the backbone of personalization and security. It allows users to create unique accounts, protecting their data and providing them with a tailored experience. For developers, it's a critical skill that opens doors to building dynamic and interactive web applications. Without it, your site would be an open book, accessible to anyone without any control over user data or personalized features. Imagine an e-commerce site where anyone could place orders without an account – chaos, right? Or a forum where users can't post or manage their profiles. This system is the gatekeeper, ensuring that only authorized users can access specific content or perform certain actions. It's the first step in building trust with your audience and offering them a sense of ownership over their digital presence on your platform. Plus, it's a fantastic way to gather user information (with their consent, of course!), which can be invaluable for marketing, analytics, and improving user experience over time. So, yeah, it's pretty darn important, and mastering it is a major win for any web developer.

Getting Started: The Tools You'll Need

Before we jump into the code, let's make sure you've got the right tools in your developer toolkit. You'll need a few things to get this project off the ground:

  1. A Local Development Environment: This is crucial for testing your code without affecting a live website. The most popular choice is XAMPP (which includes Apache, MySQL, PHP, and Perl) or WAMP (for Windows users). MAMP is another great option for Mac users. These packages bundle everything you need to run PHP and MySQL on your own computer.
  2. A Text Editor or IDE: You'll be writing a lot of code, so a good text editor or Integrated Development Environment (IDE) is essential. Visual Studio Code is a fantastic, free, and highly customizable option. Other popular choices include Sublime Text, Atom, or PHPStorm (a paid, professional IDE).
  3. A Web Browser: Pretty obvious, right? You'll need a browser like Chrome, Firefox, or Safari to test your website as you build it.

Once you have these set up, you're ready to roll! Make sure your Apache and MySQL servers are running within your chosen local environment.

Database Setup: Designing Your User Table

Alright, let's get our hands dirty with the database. We're going to use MySQL for this, as it's a robust and widely-used relational database system that plays nicely with PHP. We need a place to store our user information. The most straightforward way to do this is by creating a table specifically for users. Let's call it users.

Here's a basic structure for your users table. You can create this using a tool like phpMyAdmin (which comes with XAMPP/WAMP) or by running SQL commands directly.

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Let's break down what each part means, because understanding your database schema is key to building a solid application.

  • id: This is our primary key. It's an integer (INT) that will automatically increment (AUTO_INCREMENT) for each new user we add. Think of it as a unique identifier for every single user. It's also marked as PRIMARY KEY, meaning it must be unique and cannot be null. This is super important for referencing individual users.
  • username: This is the name users will use to log in. It's a string (VARCHAR) with a maximum length of 50 characters. NOT NULL means a username is required, and UNIQUE ensures that no two users can have the same username. This prevents duplicate accounts and makes authentication straightforward.
  • email: Similar to the username, this is for user identification and communication. It's a VARCHAR with a length of 100 characters. It's also NOT NULL and UNIQUE, as emails are typically used as a primary contact point and should be distinct for each user.
  • password: This is where the magic (and security!) happens. It's a VARCHAR with a length of 255 characters. We're making it NOT NULL because, duh, users need a password to log in. Crucially, this field will store the hashed password, not the plain text version. We'll get into hashing shortly, but the 255 length gives us ample space for strong, encrypted passwords.
  • created_at: This is a timestamp that automatically records when the user account was created. DEFAULT CURRENT_TIMESTAMP means if we don't specify a value, it will automatically be set to the current date and time when a new record is inserted. It's useful for tracking and auditing.

Setting up your database correctly from the start is like laying a strong foundation for a house. Get this right, and the rest of your development journey will be much smoother, guys. Remember to choose a descriptive database name and table names to keep your project organized!

Creating the Registration Form (HTML & PHP)

Now, let's build the user interface for registration. We'll create an HTML form that collects the necessary user details: username, email, and password. Then, we'll use PHP to process this form data and insert it into our MySQL database.

First, create a file named register.html (or register.php if you want to process it on the same page, which we'll do):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>User Registration</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .container { max-width: 400px; margin: auto; padding: 20px; border: 1px solid #ccc; border-radius: 5px; }
        input[type="text"], input[type="email"], input[type="password"] {
            width: calc(100% - 20px); /* Adjust for padding */
            padding: 10px;
            margin-bottom: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        button {
            background-color: #4CAF50;
            color: white;
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
        }
        button:hover { background-color: #45a049; }
        .message { margin-top: 15px; padding: 10px; border-radius: 4px; }
        .success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
        .error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
    </style>
</head>
<body>
    <div class="container">
        <h2>Register New Account</h2>
        <form action="register.php" method="post">
            <div>
                <label for="username">Username:</label>
                <input type="text" id="username" name="username" required>
            </div>
            <div>
                <label for="email">Email:</label>
                <input type="email" id="email" name="email" required>
            </div>
            <div>
                <label for="password">Password:</label>
                <input type="password" id="password" name="password" required>
            </div>
            <button type="submit">Register</button>
        </form>
        <p>Already have an account? <a href="login.php">Login here</a></p>
        
        <?php 
        // PHP logic will go here to display messages
        session_start();
        if (isset($_SESSION['message'])) {
            $message = $_SESSION['message'];
            $message_type = $_SESSION['message_type'];
            echo "<div class='message $message_type'>$message</div>";
            unset($_SESSION['message']);
            unset($_SESSION['message_type']);
        }
        ?>
    </div>
</body>
</html>

Notice the <form action="register.php" method="post">. This tells the browser to send the form data to register.php using the POST method when the submit button is clicked.

Now, let's create the register.php file to handle the submission. This is where the PHP and MySQL magic happens.

<?php
// Start session to store messages
session_start();

// Database connection details
$servername = "localhost"; // Usually localhost
$db_username = "root";    // Your MySQL username
$db_password = "";        // Your MySQL password (leave empty if none)
$dbname = "your_database_name"; // *** CHANGE THIS TO YOUR DATABASE NAME ***

// Create connection
$conn = new mysqli($servername, $db_username, $db_password, $dbname);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

// Check if the form was submitted
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // Get form data and sanitize it
    $username = filter_var($_POST['username'], FILTER_SANITIZE_STRING);
    $email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
    $password = $_POST['password']; // Password will be hashed next

    // *** IMPORTANT: Password Hashing ***
    // Use password_hash for secure password storage
    $hashed_password = password_hash($password, PASSWORD_DEFAULT);

    // Prepare SQL statement to prevent SQL injection
    $stmt = $conn->prepare("INSERT INTO users (username, email, password) VALUES (?, ?, ?)");
    // Bind parameters
    $stmt->bind_param("sss", $username, $email, $hashed_password);

    // Execute the statement
    if ($stmt->execute()) {
        // Registration successful
        $_SESSION['message'] = "Registration successful! You can now log in.";
        $_SESSION['message_type'] = "success";
        header("location: register.php"); // Redirect back to registration page to show message
        exit();
    } else {
        // Error handling
        // Check for duplicate entry errors
        if ($conn->errno == 1062) { // MySQL error code for duplicate entry
            $_SESSION['message'] = "Username or Email already taken. Please try another.";
            $_SESSION['message_type'] = "error";
        } else {
            $_SESSION['message'] = "Error during registration: " . $conn->error;
            $_SESSION['message_type'] = "error";
        }
        header("location: register.php"); // Redirect back to registration page to show error
        exit();
    }

    // Close statement
    $stmt->close();
}

// Close connection
$conn->close();
?>

Key points in register.php:

  • Database Connection: We establish a connection to your MySQL database using $servername, $db_username, $db_password, and $dbname. Remember to replace your_database_name with the actual name of your database!
  • Form Submission Check: if ($_SERVER["REQUEST_METHOD"] == "POST") ensures this code only runs when the form is submitted.
  • Data Sanitization: filter_var() is used to clean up the input data. FILTER_SANITIZE_STRING removes HTML tags from the username, and FILTER_SANITIZE_EMAIL validates and cleans the email format. This is a basic security measure.
  • Password Hashing: This is SUPER important, guys. Never store passwords in plain text! password_hash($password, PASSWORD_DEFAULT) creates a secure hash of the password. PASSWORD_DEFAULT uses the strongest currently available hashing algorithm.
  • Prepared Statements: $conn->prepare() and $stmt->bind_param() are used to create prepared statements. This is the best defense against SQL injection attacks. Instead of directly inserting user input into the SQL query, we use placeholders (?) and bind the variables separately. This ensures that malicious SQL code entered by users is treated as data, not commands.
  • Error Handling: We check if the $stmt->execute() was successful. If not, we try to identify if it's a duplicate entry error (username or email already exists) or some other database issue.
  • Session Messages & Redirection: We use PHP sessions (session_start()) to store a message (success or error) and then redirect the user back to the register.php page. The HTML part of register.php then checks for these session messages and displays them. This provides user feedback without losing form data on submission.

Creating the Login Form (HTML & PHP)

Now that users can register, they'll need a way to log in. We'll create a similar HTML form for login and then a login.php script to handle the authentication process.

Create a file named login.php:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>User Login</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .container { max-width: 400px; margin: auto; padding: 20px; border: 1px solid #ccc; border-radius: 5px; }
        input[type="text"], input[type="password"] {
            width: calc(100% - 20px); /* Adjust for padding */
            padding: 10px;
            margin-bottom: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        button {
            background-color: #007bff;
            color: white;
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
        }
        button:hover { background-color: #0056b3; }
        .message { margin-top: 15px; padding: 10px; border-radius: 4px; }
        .success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
        .error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
    </style>
</head>
<body>
    <div class="container">
        <h2>Login to Your Account</h2>
        <form action="login.php" method="post">
            <div>
                <label for="username">Username:</label>
                <input type="text" id="username" name="username" required>
            </div>
            <div>
                <label for="password">Password:</label>
                <input type="password" id="password" name="password" required>
            </div>
            <button type="submit">Login</button>
        </form>
        <p>Don't have an account? <a href="register.php">Register here</a></p>

        <?php 
        // PHP logic will go here to display messages
        session_start();
        if (isset($_SESSION['message'])) {
            $message = $_SESSION['message'];
            $message_type = $_SESSION['message_type'];
            echo "<div class='message $message_type'>$message</div>";
            unset($_SESSION['message']);
            unset($_SESSION['message_type']);
        }
        ?>
    </div>
</body>
</html>

And here's the login.php script to handle the login logic:

<?php
// Start session to store messages and user data
session_start();

// Database connection details
$servername = "localhost";
$db_username = "root";
$db_password = "";
$dbname = "your_database_name"; // *** CHANGE THIS TO YOUR DATABASE NAME ***

// Create connection
$conn = new mysqli($servername, $db_username, $db_password, $dbname);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

// Check if the form was submitted
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // Get form data and sanitize username
    $username = filter_var($_POST['username'], FILTER_SANITIZE_STRING);
    $password = $_POST['password'];

    // Prepare SQL statement to fetch user by username
    $stmt = $conn->prepare("SELECT id, username, password FROM users WHERE username = ?");
    $stmt->bind_param("s", $username);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($result->num_rows > 0) {
        // User found, get user data
        $user = $result->fetch_assoc();

        // *** Verify the password ***
        // password_verify() checks if the submitted password matches the hashed password
        if (password_verify($password, $user['password'])) {
            // Login successful!
            // Store user session data
            $_SESSION['loggedin'] = true;
            $_SESSION['user_id'] = $user['id'];
            $_SESSION['username'] = $user['username'];
            
            $_SESSION['message'] = "Login successful! Welcome, " . htmlspecialchars($user['username']) . "!";
            $_SESSION['message_type'] = "success";
            header("location: dashboard.php"); // Redirect to a dashboard page
            exit();
        } else {
            // Incorrect password
            $_SESSION['message'] = "Invalid username or password.";
            $_SESSION['message_type'] = "error";
        }
    } else {
        // User not found
        $_SESSION['message'] = "Invalid username or password.";
        $_SESSION['message_type'] = "error";
    }

    // Close statement
    $stmt->close();
    // Redirect back to login page to show message
    header("location: login.php");
    exit();
}

// Close connection
$conn->close();
?>

Explanation for login.php:

  • Session Start: We start the session again to handle messages and store login status.
  • Database Connection: Same as in register.php. Ensure your database details are correct.
  • Form Submission Check: Ensures the code runs only on POST requests.
  • Fetch User: We use a prepared statement to query the database for a user matching the entered username. Fetching only the necessary columns (id, username, password) is good practice.
  • password_verify(): This is the crucial function for checking passwords. It takes the plain text password submitted by the user and compares it against the stored hashed password. It returns true if they match and false otherwise. Never try to manually compare password hashes! Always use password_verify().
  • Session Data: If the password is correct, we set $_SESSION['loggedin'] = true; and store the user's ID and username in the session. This allows us to know if a user is logged in across different pages.
  • Redirection: On successful login, the user is redirected to dashboard.php. On failure, they are redirected back to login.php with an error message.

Creating a Protected Dashboard Page

To demonstrate that our login system works, let's create a simple dashboard.php page that only logged-in users can access.

Create a file named dashboard.php:

<?php
// Start session
session_start();

// Check if the user is logged in
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
    // If not logged in, redirect to the login page
    header("location: login.php");
    exit();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dashboard</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .container { max-width: 600px; margin: auto; padding: 20px; border: 1px solid #ccc; border-radius: 5px; }
        h2 { color: #333; }
        p { line-height: 1.6; }
        a { color: #007bff; text-decoration: none; }
        a:hover { text-decoration: underline; }
        .message { margin-top: 15px; padding: 10px; border-radius: 4px; }
        .success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
    </style>
</head>
<body>
    <div class="container">
        <h2>Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!</h2>
        <p>This is your protected dashboard.</p>
        <p>You have successfully logged in.</p>
        
        <?php 
        // Display any success message from login
        if (isset($_SESSION['message'])) {
            $message = $_SESSION['message'];
            $message_type = $_SESSION['message_type'];
            echo "<div class='message $message_type'>$message</div>";
            unset($_SESSION['message']);
            unset($_SESSION['message_type']);
        }
        ?>

        <p><a href="logout.php">Logout</a></p>
    </div>
</body>
</html>

How dashboard.php works:

  • Session Check: The very first thing it does is check if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true). If the loggedin session variable isn't set or isn't true, it means the user isn't authenticated. They are then immediately redirected to the login.php page using header("location: login.php");. This protects the page.
  • Display Username: If the user is logged in, their username (stored in $_SESSION['username']) is displayed. htmlspecialchars() is used here to prevent Cross-Site Scripting (XSS) attacks by converting special characters into their HTML entities.
  • Logout Link: A link to a logout.php page is provided. We'll create that next.

Creating the Logout Functionality

To log a user out, we simply need to destroy their session data. This effectively logs them out of the website.

Create a file named logout.php:

<?php
// Start session
session_start();

// Unset all of the session variables
$_SESSION = array();

// If it's desired to kill the session, also delete the session cookie.
// Note: This will destroy the session, and not just the session data!
if (ini_get("session.use_cookies")) {
    $params = session_get_cookie_params();
    setcookie(session_name(), '', time() - 42000,
        $params["path"], $params["domain"],
        $params["secure"],
        $params["httponly"]
    );
}

// Finally, destroy the session.
session_destroy();

// Redirect to the login page after logout
header("location: login.php");
exit();
?>

What logout.php does:

  • Session Start: Starts the session so we can access its variables.
  • Unset Variables: $_SESSION = array(); effectively clears all data stored in the session.
  • Destroy Cookie: The setcookie() part is a bit more advanced. It ensures that the session cookie itself is removed from the user's browser, making it harder for someone to hijack an old session.
  • session_destroy(): This function destroys all of the data associated with the current session.
  • Redirect: Redirects the user back to the login page.

Security Best Practices: What You MUST Know!

Guys, building a registration and login system isn't just about making it work; it's about making it secure. Security is paramount, and there are several vital practices you must follow:

  1. Always Use Prepared Statements: We've covered this, but it bears repeating. Prepared statements with bound parameters are your first line of defense against SQL injection. Never concatenate user input directly into your SQL queries.
  2. Hash Passwords Securely: Use password_hash() for storing passwords and password_verify() for checking them. Never store plain text passwords or use outdated hashing methods like MD5 or SHA1, which are easily crackable.
  3. Sanitize and Validate All Input: filter_var() is a good start, but you should also perform server-side validation. For example, check if passwords meet complexity requirements, if emails are valid, and if usernames contain only allowed characters. Remember to also sanitize output using htmlspecialchars() to prevent XSS.
  4. Use HTTPS: When deploying your application, always use HTTPS (SSL/TLS certificate). This encrypts the data exchanged between the user's browser and your server, protecting sensitive information like passwords and personal details from eavesdropping.
  5. Implement Rate Limiting: To prevent brute-force attacks (where attackers try many passwords quickly), consider implementing rate limiting on your login attempts. This could involve temporarily locking an account or IP address after a certain number of failed login attempts.
  6. Secure Session Management: Ensure your session cookies are set with appropriate flags like HttpOnly and Secure (if using HTTPS). Avoid storing sensitive information directly in the session if possible.
  7. Regularly Update Software: Keep your PHP version, web server, and database software up to date. Updates often include security patches for known vulnerabilities.

Building secure systems takes vigilance. Always think like an attacker and try to find weaknesses in your own code.

Conclusion: You've Built a Login System!

Congratulations, you've successfully built a functional and reasonably secure registration and login system using PHP and MySQL! We've covered database setup, form handling, secure password storage, authentication, session management, and essential security practices. This is a foundational skill that you'll use time and time again in web development.

Remember to practice, experiment, and always prioritize security. As you grow, you can add more features like password reset functionality, user roles, email verification, and much more. Keep coding, keep learning, and enjoy building awesome web applications!