Creating a Secure PHP Login System with Bootstrap 5, Database, and CSS
Creating a Secure PHP Login System with Bootstrap 5, Database, and CSS

Creating a secure login system is a crucial part of any web application. This tutorial will guide you through the process of building a secure PHP login system using Bootstrap 5 for styling, a MySQL database for user data storage, and CSS animations for a smooth user experience. Prerequisites Before we start, ensure you have the following: A web server with PHP and MySQL installed (e.g., XAMPP, WAMP, LAMP). Basic knowledge of PHP, MySQL, HTML, and CSS. Bootstrap 5 library. Step 1: Setting Up the Project Create the project directory structure: secure-login/ ├── css/ │ └── styles.css ├── js/ ├── index.php ├── login.php ├── register.php ├── welcome.php └── config.php   Download and include Bootstrap 5: Add the following Bootstrap CDN links in your index.php, login.php, register.php, and welcome.php files: <link href="https://stackpath.bootstrapcdn.com/bootstrap/5.1.3/css/bootstrap.min.css" rel="stylesheet"> <script src="https://stackpath.bootstrapcdn.com/bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script> Create a database and user table: CREATE DATABASE secure_login; USE secure_login; CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );   Step 2: Configuration File Create config.php to handle database connections: <?php $servername = "localhost"; $username = "root"; // DB username $password = "root"; // DB password $dbname = "secure_login"; $conn = new mysqli($servername, $username, $password, $dbname); if ($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } ?> Step 3: Registration Form Create register.php for new users to sign up: <?php require_once "config.php"; $username = $password = ""; $username_err = $password_err = ""; if ($_SERVER["REQUEST_METHOD"] == "POST") { if (empty(trim($_POST["username"]))) { $username_err = "Please enter a username."; } else { $sql = "SELECT id FROM users WHERE username = ?"; if ($stmt = $conn->prepare($sql)) { $stmt->bind_param("s", $param_username); $param_username = trim($_POST["username"]); if ($stmt->execute()) { $stmt->store_result(); if ($stmt->num_rows == 1) { $username_err = "This username is already taken."; } else { $username = trim($_POST["username"]); } } else { echo "Oops! Something went wrong. Please try again later."; } $stmt->close(); } } if (empty(trim($_POST["password"]))) { $password_err = "Please enter a password."; } else { $password = trim($_POST["password"]); } if (empty($username_err) && empty($password_err)) { $sql = "INSERT INTO users (username, password) VALUES (?, ?)"; if ($stmt = $conn->prepare($sql)) { $stmt->bind_param("ss", $param_username, $param_password); $param_username = $username; $param_password = password_hash($password, PASSWORD_DEFAULT); if ($stmt->execute()) { header("location: login.php"); } else { echo "Something went wrong. Please try again later."; } $stmt->close(); } } $conn->close(); } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Register</title> <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> <link rel="stylesheet" href="css/styles.css"> </head> <body> <div class="wrapper"> <h2>Register</h2> <p>Please fill this form to create an account.</p> <form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" method="post"> <div class="form-group <?php echo (!empty($username_err)) ? 'has-error' : ''; ?>"> <label>Username</label> <input type="text" name="username" class="form-control" value="<?php echo $username; ?>"> <span class="help-block"><?php echo $username_err; ?></span> </div> <div class="form-group <?php echo (!empty($password_err)) ? 'has-error' : ''; ?>"> <label>Password</label> <input type="password" name="password" class="form-control"> <span class="help-block"><?php echo $password_err; ?></span> </div> <div class="form-group"> <input type="submit" class="btn btn-primary" value="Submit"> </div> <p>Already have an account? <a href="login.php">Login here</a>.</p> </form> </div> <script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous"></script> </body> </html>   Step 4: Login Form Create login.php for user authentication: <?php session_start(); require_once "config.php"; $username = $password = ""; $username_err = $password_err = $login_err = ""; if ($_SERVER["REQUEST_METHOD"] == "POST") { if (empty(trim($_POST["username"]))) { $username_err = "Please enter username."; } else { $username = trim($_POST["username"]); } if (empty(trim($_POST["password"]))) { $password_err = "Please enter your password."; } else { $password = trim($_POST["password"]); } if (empty($username_err) && empty($password_err)) { $sql = "SELECT id, username, password FROM users WHERE username = ?"; if ($stmt = $conn->prepare($sql)) { $stmt->bind_param("s", $param_username); $param_username = $username; if ($stmt->execute()) { $stmt->store_result(); if ($stmt->num_rows == 1) { $stmt->bind_result($id, $username, $hashed_password); if ($stmt->fetch()) { if (password_verify($password, $hashed_password)) { session_start(); $_SESSION["loggedin"] = true; $_SESSION["id"] = $id; $_SESSION["username"] = $username; header("location: welcome.php"); } else { $login_err = "Invalid username or password."; } } } else { $login_err = "Invalid username or password."; } } else { echo "Oops! Something went wrong. Please try again later."; } $stmt->close(); } } $conn->close(); } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login</title> <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> <link rel="stylesheet" href="css/styles.css"> </head> <body> <div class="wrapper"> <h2>Login</h2> <p>Please fill in your credentials to login.</p> <?php if(!empty($login_err)){ echo '<div class="alert alert-danger">' . $login_err . '</div>'; } ?> <form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" method="post"> <div class="form-group <?php echo (!empty($username_err)) ? 'has-error' : ''; ?>"> <label>Username</label> <input type="text" name="username" class="form-control" value="<?php echo $username; ?>"> <span class="help-block"><?php echo $username_err; ?></span> </div> <div class="form-group <?php echo (!empty($password_err)) ? 'has-error' : ''; ?>"> <label>Password</label> <input type="password" name="password" class="form-control"> <span class="help-block"><?php echo $password_err; ?></span> </div> <div class="form-group"> <input type="submit" class="btn btn-primary" value="Login"> </div> <p>Don't have an account? <a href="register.php">Sign up now</a>.</p> </form> </div> <script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous"></script> </body> </html>   Step 5: Welcome Page Create welcome.php to display after successful login: <?php session_start(); if(!isset($_SESSION["loggedin"]) || $_SESSION["loggedin"] !== true){ header("location: login.php"); exit; } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Welcome</title> <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"></head> <body> <div class="page-header"> <h1>Hi, <b><?php echo htmlspecialchars($_SESSION["username"]); ?></b>. Welcome to our site.</h1> </div> <p> <a href="logout.php" class="btn btn-danger">Sign Out of Your Account</a> </p> <script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous"></script> </body> </html> Step 6: CSS Animations Add CSS animations in styles.css: body { font: 14px sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; background: #f8f9fa; } .wrapper { width: 360px; padding: 20px; background: #fff; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); animation: fadeIn 1.5s ease-in-out; } @keyframes fadeIn { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } Step 7: Logout Create logout.php to handle user logout: <?php session_start(); $_SESSION = array(); session_destroy(); header("location: login.php"); exit; ?>   You've now created a secure PHP login system with Bootstrap 5, MySQL, and CSS animations. This setup provides a basic structure that you can expand upon by adding more features such as email verification, password recovery, and user profile management. Remember to always prioritize security and follow best practices to protect user data.   < > GitHub

Converting Hex Colors to Dark, Light Versions, and RGB Using PHP
Converting Hex Colors to Dark, Light Versions, and RGB Using PHP

When developing web applications, manipulating colors is often necessary to create dynamic and visually appealing designs. PHP can be a handy tool for converting hex colors to their darker or lighter versions and RGB format. This article will guide you through the process of achieving these tasks using PHP. Understanding Hex and RGB Colors Colors in web development are typically represented using hex (hexadecimal) or RGB (Red, Green, Blue) formats. Hex Color: Represented as a six-digit combination of numbers and letters preceded by a hash (#), for example, #FF5733. RGB Color: Represented as a function rgb() with three parameters, each ranging from 0 to 255, for example, rgb(255, 87, 51).   Converting Hex to RGB Before manipulating the color, it's essential to convert the hex color to RGB format. This process involves extracting the red, green, and blue components from the hex string. Here’s a function to convert a hex color to RGB: function hexToRgb($hex) { $hex = str_replace("#", "", $hex); if(strlen($hex) == 3) { $r = hexdec(str_repeat(substr($hex, 0, 1), 2)); $g = hexdec(str_repeat(substr($hex, 1, 1), 2)); $b = hexdec(str_repeat(substr($hex, 2, 1), 2)); } else { $r = hexdec(substr($hex, 0, 2)); $g = hexdec(substr($hex, 2, 2)); $b = hexdec(substr($hex, 4, 2)); } return array($r, $g, $b); } Example Usage: $hex = "#FF5733"; $rgb = hexToRgb($hex); print_r($rgb); // Output: Array ( [0] => 255 [1] => 87 [2] => 51 )   Converting Hex to a Darker Version To darken a color, you reduce its RGB values. Here's a function to darken a hex color by a specific percentage: function darkenHex($hex, $percent) { $rgb = hexToRgb($hex); $r = max(0, min(255, $rgb[0] - ($rgb[0] * $percent / 100))); $g = max(0, min(255, $rgb[1] - ($rgb[1] * $percent / 100))); $b = max(0, min(255, $rgb[2] - ($rgb[2] * $percent / 100))); return sprintf("#%02x%02x%02x", $r, $g, $b); } Example Usage: $hex = "#FF5733"; $darkHex = darkenHex($hex, 20); echo $darkHex; // Output: #cc4629 Converting Hex to a Lighter Version To lighten a color, you increase its RGB values. Here’s a function to lighten a hex color by a specific percentage: function lightenHex($hex, $percent) { $rgb = hexToRgb($hex); $r = max(0, min(255, $rgb[0] + ($rgb[0] * $percent / 100))); $g = max(0, min(255, $rgb[1] + ($rgb[1] * $percent / 100))); $b = max(0, min(255, $rgb[2] + ($rgb[2] * $percent / 100))); return sprintf("#%02x%02x%02x", $r, $g, $b); } Example Usage: $hex = "#FF5733"; $lightHex = lightenHex($hex, 20); echo $lightHex; // Output: #ff9177   Combining the Functions Combining all the functions, you can create a utility class to manage color conversions and adjustments. class ColorConverter { public static function hexToRgb($hex) { $hex = str_replace("#", "", $hex); if(strlen($hex) == 3) { $r = hexdec(str_repeat(substr($hex, 0, 1), 2)); $g = hexdec(str_repeat(substr($hex, 1, 1), 2)); $b = hexdec(str_repeat(substr($hex, 2, 1), 2)); } else { $r = hexdec(substr($hex, 0, 2)); $g = hexdec(substr($hex, 2, 2)); $b = hexdec(substr($hex, 4, 2)); } return array($r, $g, $b); } public static function darkenHex($hex, $percent) { $rgb = self::hexToRgb($hex); $r = max(0, min(255, $rgb[0] - ($rgb[0] * $percent / 100))); $g = max(0, min(255, $rgb[1] - ($rgb[1] * $percent / 100))); $b = max(0, min(255, $rgb[2] - ($rgb[2] * $percent / 100))); return sprintf("#%02x%02x%02x", $r, $g, $b); } public static function lightenHex($hex, $percent) { $rgb = self::hexToRgb($hex); $r = max(0, min(255, $rgb[0] + ($rgb[0] * $percent / 100))); $g = max(0, min(255, $rgb[1] + ($rgb[1] * $percent / 100))); $b = max(0, min(255, $rgb[2] + ($rgb[2] * $percent / 100))); return sprintf("#%02x%02x%02x", $r, $g, $b); } } $hex = "#FF5733"; $rgb = ColorConverter::hexToRgb($hex); $darkHex = ColorConverter::darkenHex($hex, 20); $lightHex = ColorConverter::lightenHex($hex, 20); echo "Original Hex: $hex\n"; echo "RGB: " . implode(", ", $rgb) . "\n"; echo "Darker Hex: $darkHex\n"; echo "Lighter Hex: $lightHex\n";   < > GitHub

Building a User Logs Table with Navigation and Search Using Bootstrap 5 and MySQL
Building a User Logs Table with Navigation and Search Using Bootstrap 5 and MySQL

Incorporating a database into our user logs table allows for more dynamic and scalable data management. In this tutorial, we will extend the previous example by fetching user logs from a MySQL database. Prerequisites Before we start, ensure you have the following: Basic understanding of HTML, CSS, JavaScript, and PHP. Bootstrap 5 library (can be included via CDN). MySQL installed and running. PHP installed and running.   Setting Up the MySQL Database First, let's set up a MySQL database and create a table for user logs. Creating the Database and Table Open your MySQL command line or a MySQL client like phpMyAdmin. Create a new database and a table for user logs: CREATE DATABASE user_logs_db; USE user_logs_db; CREATE TABLE user_logs ( id INT AUTO_INCREMENT PRIMARY KEY, user_id VARCHAR(255) NOT NULL, action VARCHAR(255) NOT NULL, timestamp DATETIME NOT NULL ); Insert some sample data into the table: INSERT INTO user_logs (user_id, action, timestamp) VALUES ('user123', 'Login', '2024-06-14 10:00:00'), ('user456', 'Logout', '2024-06-14 10:05:00'), ('user789', 'Login', '2024-06-14 10:15:00'); Setting Up the Server-Side Code We'll use PHP to fetch data from the MySQL database and serve it to our front-end. Create a new file named fetch_logs.php to handle data fetching. PHP Script (fetch_logs.php) <?php $servername = "localhost"; $username = "root"; $password = "root"; // your MySQL password $dbname = "user_logs_db"; $conn = new mysqli($servername, $username, $password, $dbname); if ($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } $search = $_GET['search'] ?? ''; $page = $_GET['page'] ?? 1; $logsPerPage = 10; $offset = ($page - 1) * $logsPerPage; $sqlCount = "SELECT COUNT(*) as count FROM user_logs WHERE user_id LIKE ? OR action LIKE ? OR timestamp LIKE ?"; $stmtCount = $conn->prepare($sqlCount); $searchParam = "%$search%"; $stmtCount->bind_param("sss", $searchParam, $searchParam, $searchParam); $stmtCount->execute(); $resultCount = $stmtCount->get_result(); $totalLogs = $resultCount->fetch_assoc()['count']; $sql = "SELECT * FROM user_logs WHERE user_id LIKE ? OR action LIKE ? OR timestamp LIKE ? LIMIT ? OFFSET ?"; $stmt = $conn->prepare($sql); $stmt->bind_param("sssii", $searchParam, $searchParam, $searchParam, $logsPerPage, $offset); $stmt->execute(); $result = $stmt->get_result(); $logs = []; while($row = $result->fetch_assoc()) { $logs[] = $row; } $response = [ 'logs' => $logs, 'totalLogs' => $totalLogs ]; header('Content-Type: application/json'); echo json_encode($response); $conn->close(); ?> Setting Up the Front-End Next, we need to modify our front-end to fetch data from the server-side script and display it dynamically. HTML Structure <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>User Logs Table</title> <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> </head> <body> <div class="container mt-5"> <h1 class="mb-4">User Logs</h1> <div class="row mb-3"> <div class="col-md-6"> <input type="text" id="searchInput" class="form-control" placeholder="Search logs..."> </div> </div> <table class="table table-striped"> <thead> <tr> <th scope="col">#</th> <th scope="col">User ID</th> <th scope="col">Action</th> <th scope="col">Timestamp</th> </tr> </thead> <tbody id="logsTableBody"> <!-- Log entries will be inserted here dynamically --> </tbody> </table> <nav> <ul class="pagination justify-content-center" id="pagination"> <!-- Pagination buttons will be inserted here dynamically --> </ul> </nav> </div> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> <script src="script.js"></script> </body> </html> JavaScript for Fetching Data and Rendering Update the JavaScript file (script.js) to fetch data from the PHP script and render it dynamically. // script.js let currentPage = 1; const logsPerPage = 10; $(document).ready(function () { fetchLogs(); $('#searchInput').on('input', fetchLogs); }); function fetchLogs() { const searchQuery = $('#searchInput').val(); $.get('fetch_logs.php', { search: searchQuery, page: currentPage }, function (data) { renderTable(data.logs); renderPagination(data.totalLogs); }, 'json'); } function renderTable(logs) { $('#logsTableBody').empty(); logs.forEach(log => { $('#logsTableBody').append(` <tr> <th scope="row">${log.id}</th> <td>${log.user_id}</td> <td>${log.action}</td> <td>${log.timestamp}</td> </tr> `); }); } function renderPagination(totalLogs) { const totalPages = Math.ceil(totalLogs / logsPerPage); $('#pagination').empty(); for (let i = 1; i <= totalPages; i++) { $('#pagination').append(` <li class="page-item ${i === currentPage ? 'active' : ''}"> <a class="page-link" href="#" data-page="${i}">${i}</a> </li> `); } $('.page-link').on('click', function (e) { e.preventDefault(); currentPage = $(this).data('page'); fetchLogs(); }); } Explanation MySQL Database: Created a database user_logs_db and a table user_logs to store the logs. Inserted sample data into the table. PHP Script (fetch_logs.php): Connects to the MySQL database. Fetches logs based on search query and pagination parameters. Returns the logs and total count as a JSON response. HTML Structure: The table and search input remain the same as before. Uses Bootstrap for styling and structure. JavaScript (script.js): Fetches data from the PHP script based on search query and pagination. Renders the table and pagination dynamically.   < > GitHub

Creating an AMP Blog Page with PHP and MySQL
Creating an AMP Blog Page with PHP and MySQL

In today's fast-paced digital world, page speed and mobile friendliness are crucial for user engagement and SEO. Accelerated Mobile Pages (AMP) is an open-source initiative that aims to provide web pages that load quickly on mobile devices. This article will guide you through creating a simple AMP-powered blog page using PHP and MySQL. Prerequisites Before we begin, ensure you have the following installed on your development environment: PHP (version 7.0 or higher) MySQL (version 5.7 or higher) A web server like Apache or Nginx Composer (for managing PHP dependencies)   Step 1: Setting Up the MySQL Database First, we need to set up a MySQL database to store our blog posts. Create a Database: Open your MySQL command line or a tool like phpMyAdmin and run the following SQL command to create a database: CREATE DATABASE amp_blog; Create a Table: Next, create a table to store the blog posts. USE amp_blog; CREATE TABLE posts ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); Step 2: Setting Up the PHP Project Project Structure: Create a project directory and structure it as follows: amp-blog/ ├── index.php ├── post.php ├── db.php └── vendor/ Database Connection (db.php): Create a file named db.php to handle the database connection. <?php $host = '127.0.0.1'; $db = 'amp_blog'; $user = 'root'; $pass = ''; $charset = 'utf8mb4'; $dsn = "mysql:host=$host;dbname=$db;charset=$charset"; $options = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]; try { $pdo = new PDO($dsn, $user, $pass, $options); } catch (\PDOException $e) { throw new \PDOException($e->getMessage(), (int)$e->getCode()); } ?> Step 3: Creating the Blog Pages Homepage (index.php): This page will display a list of blog posts. <?php require 'db.php'; $stmt = $pdo->query('SELECT id, title, created_at FROM posts ORDER BY created_at DESC'); $posts = $stmt->fetchAll(); ?> <!doctype html> <html amp> <head> <meta charset="utf-8"> <title>AMP Blog</title> <link rel="canonical" href="index.php"> <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <style amp-custom> body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f9f9f9; } .container { max-width: 800px; margin: 0 auto; padding: 20px; } h1 { text-align: center; color: #333; } .post { background: #fff; border: 1px solid #ddd; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .post h2 { margin-top: 0; font-size: 1.5em; } .post time { display: block; font-size: 0.9em; color: #999; margin-bottom: 10px; } .post a { text-decoration: none; color: #333; } .post a:hover { text-decoration: underline; } </style> <script async src="https://cdn.ampproject.org/v0.js"></script> </head> <body> <div class="container"> <h1>My AMP Blog</h1> <?php foreach ($posts as $post): ?> <div class="post"> <h2><a href="post.php?id=<?= $post['id'] ?>"><?= htmlspecialchars($post['title']) ?></a></h2> <time datetime="<?= $post['created_at'] ?>"><?= $post['created_at'] ?></time> </div> <?php endforeach; ?> </div> </body> </html> Single Post Page (post.php): This page will display a single blog post. <?php require 'db.php'; $id = $_GET['id']; $stmt = $pdo->prepare('SELECT title, content, created_at FROM posts WHERE id = ?'); $stmt->execute([$id]); $post = $stmt->fetch(); if (!$post) { die('Post not found!'); } ?> <!doctype html> <html amp> <head> <meta charset="utf-8"> <title><?= htmlspecialchars($post['title']) ?></title> <link rel="canonical" href="post.php?id=<?= $id ?>"> <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <style amp-custom> body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f9f9f9; } .container { max-width: 800px; margin: 0 auto; padding: 20px; } h1 { text-align: center; color: #333; } time { display: block; font-size: 0.9em; color: #999; margin-bottom: 20px; text-align: center; } .content { background: #fff; border: 1px solid #ddd; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } </style> <script async src="https://cdn.ampproject.org/v0.js"></script> </head> <body> <div class="container"> <h1><?= htmlspecialchars($post['title']) ?></h1> <time datetime="<?= $post['created_at'] ?>"><?= $post['created_at'] ?></time> <div class="content"> <?= nl2br(htmlspecialchars($post['content'])) ?> </div> </div> </body> </html> Step 4: Testing Your AMP Blog Run Your Server: Start your web server (Apache or Nginx) and navigate to http://localhost/amp-blog/index.php to view your blog. Validate AMP: Use the AMP Validator to ensure your pages comply with AMP standards. Simply paste the URL of your blog pages into the validator.   < > GitHub


© vladoivankovic.com

VladoIvankovic