1. Develop PHP Contact Form on LEMP Stack

๐ŸŽฏ Goal

Learn to manually install and configure a LEMP stack (Linux, Nginx, MySQL, PHP) on Ubuntu and create a simple PHP contact form application with database storage.

๐Ÿ“‹ Prerequisites

Before beginning this tutorial, you should:

๐Ÿ“š Learning Objectives

By the end of this tutorial, you will:

๐Ÿ” Why This Matters

In real-world applications, LEMP stack knowledge is crucial because:

๐Ÿ“ Step-by-Step Instructions

Step 1: Connect to Your Ubuntu VM

  1. Use SSH to connect to your Ubuntu VM:

    ssh azureuser@<VM_Public_IP>
    

    Replace <VM_Public_IP> with your VM’s actual public IP address or FQDN.

๐Ÿ’ก Information

  • SSH (Secure Shell): A secure protocol for remotely accessing and managing servers
  • azureuser: The default username created during Azure VM setup
  • Make sure port 22 (SSH) is open in your Network Security Group

Step 2: Update System and Install Nginx

  1. Update the package list to ensure you have the latest information:

    sudo apt update
    
  2. Install Nginx web server:

    sudo apt install nginx -y
    
  3. Verify Nginx is running:

    sudo systemctl status nginx
    
  4. Test the web server by visiting your VM’s public IP in a browser. You should see the default Nginx welcome page.

๐Ÿ’ก Information

  • Nginx: High-performance web server and reverse proxy
  • systemctl: Command to manage system services on Ubuntu
  • The -y flag automatically answers “yes” to installation prompts
  • Nginx automatically starts and enables itself after installation

โš ๏ธ Common Mistakes

  • Forgetting to update package lists may result in installing outdated packages
  • Not checking service status might leave you unaware if Nginx failed to start

Step 3: Install MySQL Database Server

  1. Install MySQL server:

    sudo apt install mysql-server -y
    
  2. Check that MySQL is running:

    sudo systemctl status mysql
    
  3. Set up MySQL root password (for our simple educational setup):

    sudo mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'SecurePass123!';"
    
  4. Create a dedicated user for PHP applications:

    sudo mysql -u root -pSecurePass123! -e "CREATE USER IF NOT EXISTS 'php_user'@'localhost' IDENTIFIED BY 'php123';"
    sudo mysql -u root -pSecurePass123! -e "GRANT ALL PRIVILEGES ON *.* TO 'php_user'@'localhost';"
    sudo mysql -u root -pSecurePass123! -e "FLUSH PRIVILEGES;"
    

๐Ÿ’ก Information

  • MySQL: Popular open-source relational database management system
  • mysql_native_password: Authentication method compatible with PHP
  • php_user: Dedicated database user for PHP applications
  • The password setup here is for educational purposes - production systems need stronger security

Step 4: Install PHP and Configure for Nginx

  1. Install PHP and necessary extensions:

    sudo apt install php-fpm php-mysql -y
    
  2. Check that PHP-FPM is running:

    sudo systemctl status php8.1-fpm
    
  3. Configure Nginx to process PHP files by editing the default site configuration:

    sudo nano /etc/nginx/sites-available/default
    
  4. Replace the existing content with this configuration:

    /etc/nginx/sites-available/default

    server {
        listen 80;
        root /var/www/html;
        index index.php index.html index.nginx-debian.html;
    
        location / {
            try_files $uri $uri/ =404;
        }
    
        location ~ \.php$ {
            include snippets/fastcgi-php.conf;
            fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        }
    }
    
  5. Test the Nginx configuration:

    sudo nginx -t
    
  6. Reload Nginx to apply the changes:

    sudo systemctl reload nginx
    

๐Ÿ’ก Information

  • PHP-FPM: FastCGI Process Manager that handles PHP requests efficiently
  • php-mysql: PHP extension for MySQL database connectivity
  • fastcgi_pass: Tells Nginx where to send PHP requests (to PHP-FPM socket)
  • try_files: Nginx directive that checks for file existence before processing

Step 5: Create Database Setup Script

  1. Navigate to the web directory:

    cd /var/www/html
    
  2. Create a database setup script:

    sudo nano database_setup.php
    
  3. Add the following PHP code:

    /var/www/html/database_setup.php

    <?php
    // Database configuration
    $host = 'localhost';
    $username = 'php_user';
    $password = 'php123';
    $database = 'contact_db';
    
    try {
        // Connect to MySQL without specifying database first
        $pdo = new PDO("mysql:host=$host", $username, $password);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
        // Create database if it doesn't exist
        $pdo->exec("CREATE DATABASE IF NOT EXISTS $database");
    
        // Connect to the specific database
        $pdo = new PDO("mysql:host=$host;dbname=$database", $username, $password);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
        // Create contacts table if it doesn't exist
        $sql = "CREATE TABLE IF NOT EXISTS contacts (
            id INT AUTO_INCREMENT PRIMARY KEY,
            name VARCHAR(100) NOT NULL,
            email VARCHAR(100) NOT NULL,
            message TEXT NOT NULL,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )";
    
        $pdo->exec($sql);
    
    } catch(PDOException $e) {
        die("Database setup failed: " . $e->getMessage());
    }
    ?>
    

๐Ÿ’ก Information

  • PDO: PHP Data Objects - a database access layer providing uniform interface
  • ATTR_ERRMODE: Configures how PDO handles errors
  • AUTO_INCREMENT: MySQL feature that automatically increments ID values
  • TIMESTAMP: MySQL data type that stores date and time
  • This script creates both database and table automatically on first run

Step 6: Create Landing Page

  1. Create the main landing page:

    sudo nano index.html
    
  2. Add the following HTML content:

    /var/www/html/index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Simple Contact App</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <div class="container">
            <h1>Welcome to Our Contact Application!</h1>
            <p>This is a simple LEMP stack demonstration with a PHP contact form.</p>
            <p>You can submit messages and view all submitted messages.</p>
    
            <div class="navigation">
                <a href="contact_form.html" class="button">Submit a Message</a>
                <a href="on_get_messages.php" class="button">View All Messages</a>
            </div>
        </div>
    </body>
    </html>
    

Step 7: Create CSS Styling

  1. Create a CSS file for styling:

    sudo nano style.css
    
  2. Add the following CSS content:

    /var/www/html/style.css

    body {
        font-family: Arial, sans-serif;
        max-width: 800px;
        margin: 50px auto;
        padding: 20px;
        background-color: #f5f5f5;
    }
    
    .container {
        background-color: white;
        padding: 40px;
        border-radius: 8px;
        box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        text-align: center;
    }
    
    h1 {
        color: #333;
        margin-bottom: 20px;
    }
    
    p {
        color: #666;
        font-size: 18px;
        line-height: 1.6;
        margin-bottom: 30px;
    }
    
    .navigation {
        margin-top: 30px;
    }
    
    .button {
        display: inline-block;
        background-color: #007bff;
        color: white;
        padding: 15px 30px;
        text-decoration: none;
        border-radius: 5px;
        margin: 10px;
        font-size: 16px;
        font-weight: bold;
    }
    
    .button:hover {
        background-color: #0056b3;
    }
    
    /* Form styling */
    .form-container {
        max-width: 600px;
        margin: 0 auto;
        text-align: left;
    }
    
    label {
        display: block;
        margin-bottom: 5px;
        font-weight: bold;
        color: #555;
    }
    
    input[type="text"], input[type="email"], textarea {
        width: 100%;
        padding: 10px;
        margin-bottom: 15px;
        border: 1px solid #ddd;
        border-radius: 4px;
        box-sizing: border-box;
    }
    
    .submit-button {
        background-color: #28a745;
        color: white;
        padding: 12px 24px;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 16px;
    }
    
    .submit-button:hover {
        background-color: #218838;
    }
    

Step 8: Create Contact Form

  1. Create the contact form HTML:

    sudo nano contact_form.html
    
  2. Add the following content:

    /var/www/html/contact_form.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Contact Form</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <div class="container">
            <h1>Contact Us</h1>
            <div class="form-container">
                <form action="on_post_contact.php" method="POST">
                    <label for="name">Full Name:</label>
                    <input type="text" id="name" name="name" required>
    
                    <label for="email">Email Address:</label>
                    <input type="email" id="email" name="email" required>
    
                    <label for="message">Message:</label>
                    <textarea id="message" name="message" rows="5" required></textarea>
    
                    <button type="submit" class="submit-button">Send Message</button>
                </form>
            </div>
    
            <div class="navigation">
                <a href="index.html" class="button">โ† Back to Home</a>
                <a href="on_get_messages.php" class="button">View Messages</a>
            </div>
        </div>
    </body>
    </html>
    

๐Ÿ’ก Information

  • action=“on_post_contact.php”: Specifies which PHP file handles form submission
  • method=“POST”: HTTP method for sending form data securely
  • required: HTML5 attribute ensuring fields are filled before submission
  • External CSS: Linked CSS file maintains consistent styling across pages

Step 9: Create Form Processing Script

  1. Create the PHP script to handle form submissions:

    sudo nano on_post_contact.php
    
  2. Add the following PHP code:

    /var/www/html/on_post_contact.php

    <?php
    // Include database setup
    require_once 'database_setup.php';
    
    // Check if form was submitted
    if ($_SERVER["REQUEST_METHOD"] == "POST") {
    
        // Sanitize input data
        $name = htmlspecialchars(trim($_POST['name']));
        $email = htmlspecialchars(trim($_POST['email']));
        $message = htmlspecialchars(trim($_POST['message']));
    
        try {
            // Insert data into database
            $sql = "INSERT INTO contacts (name, email, message) VALUES (:name, :email, :message)";
            $stmt = $pdo->prepare($sql);
            $stmt->bindParam(':name', $name);
            $stmt->bindParam(':email', $email);
            $stmt->bindParam(':message', $message);
            $stmt->execute();
    
            $success = true;
    
        } catch(PDOException $e) {
            $error = "Error saving message: " . $e->getMessage();
        }
    
    } else {
        // Redirect if accessed directly
        header("Location: contact_form.html");
        exit();
    }
    ?>
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Message Sent</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <div class="container">
            <?php if (isset($success)): ?>
                <h1>Thank You!</h1>
                <p>Your message has been sent successfully.</p>
                <div class="form-container">
                    <p><strong>Name:</strong> <?php echo $name; ?></p>
                    <p><strong>Email:</strong> <?php echo $email; ?></p>
                    <p><strong>Message:</strong> <?php echo nl2br($message); ?></p>
                </div>
            <?php else: ?>
                <h1>Error</h1>
                <p><?php echo $error; ?></p>
            <?php endif; ?>
    
            <div class="navigation">
                <a href="contact_form.html" class="button">โ† Send Another Message</a>
                <a href="on_get_messages.php" class="button">View All Messages</a>
                <a href="index.html" class="button">Home</a>
            </div>
        </div>
    </body>
    </html>
    

Step 10: Create Messages Display Script

  1. Create the script to display all messages:

    sudo nano on_get_messages.php
    
  2. Add the following PHP code:

    /var/www/html/on_get_messages.php

    <?php
    // Include database setup
    require_once 'database_setup.php';
    
    try {
        // Fetch all messages from database
        $sql = "SELECT * FROM contacts ORDER BY created_at DESC";
        $stmt = $pdo->query($sql);
        $messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    } catch(PDOException $e) {
        $error = "Error fetching messages: " . $e->getMessage();
    }
    ?>
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>All Messages</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <div class="container">
            <h1>All Contact Messages</h1>
    
            <?php if (isset($error)): ?>
                <p>Error: <?php echo $error; ?></p>
            <?php elseif (empty($messages)): ?>
                <p>No messages found. <a href="contact_form.html">Submit the first message!</a></p>
            <?php else: ?>
                <?php foreach ($messages as $msg): ?>
                    <div class="form-container" style="margin-bottom: 20px; border-left: 4px solid #007bff;">
                        <h3><?php echo htmlspecialchars($msg['name']); ?></h3>
                        <p><strong>Email:</strong> <?php echo htmlspecialchars($msg['email']); ?></p>
                        <p><strong>Message:</strong> <?php echo nl2br(htmlspecialchars($msg['message'])); ?></p>
                        <p><strong>Submitted:</strong> <?php echo $msg['created_at']; ?></p>
                    </div>
                <?php endforeach; ?>
            <?php endif; ?>
    
            <div class="navigation">
                <a href="contact_form.html" class="button">Submit New Message</a>
                <a href="index.html" class="button">โ† Back to Home</a>
            </div>
        </div>
    </body>
    </html>
    

Step 11: Set Proper File Permissions

  1. Set proper ownership for all web files:

    sudo chown -R www-data:www-data /var/www/html/
    
  2. Set appropriate file permissions:

    sudo chmod 644 /var/www/html/*.php
    sudo chmod 644 /var/www/html/*.html
    sudo chmod 644 /var/www/html/*.css
    

๐Ÿ’ก Information

  • www-data: Default web server user in Ubuntu
  • 644: Read/write for owner, read-only for group and others
  • Proper permissions ensure web server can read files but maintain security

๐Ÿงช Final Tests

Test 1: Verify LEMP Stack Installation

  1. Open your browser and visit http://<VM_Public_IP>/
  2. You should see the welcome page with proper styling
  3. Check that all navigation links work correctly

Test 2: Test Database Connectivity

  1. Visit http://<VM_Public_IP>/on_get_messages.php
  2. You should see “No messages found” initially
  3. This confirms the database connection and table creation worked

Test 3: Test Complete Form Workflow

  1. Navigate to http://<VM_Public_IP>/contact_form.html
  2. Fill out the form with test data:
    • Name: “John Doe”
    • Email: “john@example.com
    • Message: “This is a test message”
  3. Submit the form
  4. You should see a confirmation page with your submitted data
  5. Navigate to “View All Messages” to see your submission in the list

โœ… Expected Results

๐Ÿ”ง Troubleshooting

If Nginx is not serving pages:

If PHP is not processing:

If database connection fails:

๐Ÿ“š Further Reading

Done! ๐ŸŽ‰

Great job! You’ve successfully implemented a complete LEMP stack and learned how to create dynamic PHP applications with database integration! This knowledge will help you understand automated deployments and more complex web applications. ๐Ÿš€