how to build a simple PHP Search Engine with Sitemap Indexing, Pagination, and Auto-Suggestions β perfect for beginner to intermediate developers.
π Build Your Own Search Engine in PHP with Sitemap Parsing, Pagination, and Auto-Suggestions
If you've ever wanted to build a simple search engine for your own content or a client project, this post will show you exactly how to do it β using PHP, MySQL, Tailwind CSS, and basic JavaScript for real-time auto-suggestions.
We'll cover:
- β User authentication
- β Submitting and parsing sitemaps
- β Indexing pages in MySQL
- β Search input with live suggestions
- β Search result pagination
Letβs dive in!
π§± 1. Database Structure
Create a MySQL database and run the following SQL to set up required tables:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(100) UNIQUE,
password VARCHAR(255)
);
CREATE TABLE sitemaps (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
url TEXT,
submitted_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE search_index (
id INT AUTO_INCREMENT PRIMARY KEY,
url TEXT,
title TEXT,
description TEXT,
content LONGTEXT,
indexed_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
π 2. User Registration and Login (Tailwind Styled)
register.php
β Tailwind-Styled Registration Form
<?php
include 'db.php';
$message = "";
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username']);
$password = $_POST['password'];
if ($username && $password) {
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $conn->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
$stmt->bind_param("ss", $username, $hash);
if ($stmt->execute()) {
$message = "Registration successful. <a href='login.php' class='underline text-blue-500'>Login here</a>.";
} else {
$message = "Error: Username might already exist.";
}
} else {
$message = "All fields are required.";
}
}
?>
<!-- HTML form as shown in previous answer (Tailwind-styled) -->
login.php
β Secure Login
<?php
session_start();
include 'db.php';
$message = "";
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username']);
$password = $_POST['password'];
$stmt = $conn->prepare("SELECT id, password FROM users WHERE username = ?");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($userId, $hash);
if ($stmt->fetch() && password_verify($password, $hash)) {
$_SESSION['user_id'] = $userId;
header("Location: dashboard.php");
exit;
} else {
$message = "Invalid credentials.";
}
}
?>
<!-- Tailwind styled login form similar to register.php -->
πΊοΈ 3. Submit Sitemap and Crawl URLs
submit_sitemap.php
<?php
session_start();
include 'db.php';
if (!isset($_SESSION['user_id'])) {
die("Login required.");
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$sitemap = $_POST['sitemap'];
$userId = $_SESSION['user_id'];
$stmt = $conn->prepare("INSERT INTO sitemaps (user_id, url) VALUES (?, ?)");
$stmt->bind_param("is", $userId, $sitemap);
$stmt->execute();
// Start background crawl (e.g. queue for cron job)
file_get_contents("http://localhost/crawler.php?sitemap=" . urlencode($sitemap));
echo "Sitemap submitted. Crawling started!";
}
?>
π€ 4. Sitemap Crawler and Indexer (Basic)
crawler.php
<?php
include 'db.php';
function fetchSitemapUrls($sitemapUrl) {
$xml = @simplexml_load_file($sitemapUrl);
$urls = [];
if ($xml && $xml->url) {
foreach ($xml->url as $url) {
$urls[] = (string) $url->loc;
}
}
return $urls;
}
function indexUrl($url) {
$html = @file_get_contents($url);
if (!$html) return;
preg_match("/<title>(.*?)<\/title>/i", $html, $titleMatch);
preg_match("/<meta name=\"description\" content=\"(.*?)\"/i", $html, $descMatch);
$title = $titleMatch[1] ?? 'No Title';
$desc = $descMatch[1] ?? '';
$cleanText = strip_tags($html);
global $conn;
$stmt = $conn->prepare("INSERT INTO search_index (url, title, description, content) VALUES (?, ?, ?, ?)");
$stmt->bind_param("ssss", $url, $title, $desc, $cleanText);
$stmt->execute();
}
$sitemap = $_GET['sitemap'] ?? '';
if ($sitemap) {
$urls = fetchSitemapUrls($sitemap);
foreach ($urls as $url) {
indexUrl($url);
}
echo "Indexed " . count($urls) . " pages.";
}
Use a cron job to trigger this script in production or queue it after submission.
π 5. Search Engine Interface (with Autocomplete + Pagination)
index.php
<?php
include 'db.php';
$q = $_GET['q'] ?? '';
$page = max(1, intval($_GET['page'] ?? 1));
$perPage = 10;
$offset = ($page - 1) * $perPage;
$total = 0;
$results = [];
if ($q) {
$stmt = $conn->prepare("SELECT COUNT(*) FROM search_index WHERE title LIKE CONCAT('%', ?, '%') OR content LIKE CONCAT('%', ?, '%')");
$stmt->bind_param("ss", $q, $q);
$stmt->execute(); $stmt->bind_result($total); $stmt->fetch(); $stmt->close();
$stmt = $conn->prepare("SELECT * FROM search_index WHERE title LIKE CONCAT('%', ?, '%') OR content LIKE CONCAT('%', ?, '%') LIMIT ? OFFSET ?");
$stmt->bind_param("ssii", $q, $q, $perPage, $offset);
$stmt->execute(); $results = $stmt->get_result();
}
?>
<!-- Tailwind search form, result loop, and pagination from previous answer -->
suggest.php
for AJAX Autocomplete
<?php
include 'db.php';
$q = $_GET['q'] ?? '';
$suggestions = [];
if ($q) {
$stmt = $conn->prepare("SELECT DISTINCT title FROM search_index WHERE title LIKE CONCAT(?, '%') LIMIT 10");
$stmt->bind_param("s", $q);
$stmt->execute();
$res = $stmt->get_result();
while ($row = $res->fetch_assoc()) {
$suggestions[] = $row['title'];
}
}
header('Content-Type: application/json');
echo json_encode($suggestions);
π Final Thoughts
You now have a working PHP search engine that:
- Lets users submit and index sitemaps
- Crawls and stores searchable content
- Provides a nice UI with Tailwind
- Includes live search suggestions and pagination
π§ Bonus Ideas:
- Add full-text indexing with MySQL
FULLTEXT
- Use Redis to cache popular queries
- Add tags or categories to indexed pages
Top comments (0)