About Modules Projects Documents Contact
← Back to Home PHP Backend

M295 - Implementing the Backend of an Application

Grade: 5.0
40+ Hours
6 Competencies

In this module I built the server side of a web application in plain PHP, without a framework. The focus was on doing it securely: accessing the database through PDO, protecting every query with prepared statements, storing passwords only as hashes, managing logins with sessions and validating input on the server instead of trusting the browser.

Related project

Bücher Antiquariat, my full-stack PHP web app for an antiquarian bookshop, applies these backend skills in practice: plain PHP without a framework, MySQL, a login-protected admin area and server-side form handling.

View the Bücher Antiquariat project →

What I Learned

Core competencies and skills developed throughout this module

1

Structuring a Backend Without a Framework

Organised a PHP application so that every request follows a clear path, from the incoming URL through the logic to the rendered page.

  • Using page controllers: one file per URL, logic at the top
  • Sharing configuration and helpers through require
  • Separating processing (POST) from displaying (GET)
  • Comparing page controllers with the MVC pattern
2

Database Access with PDO

Connected PHP to MySQL using PDO and configured it so that errors become visible instead of silently failing.

  • Creating one shared PDO connection per request
  • Setting the error mode to exceptions
  • Fetching results as associative arrays
  • Keeping credentials out of the public web root
3

Preventing SQL Injection

Learned why user input must never be concatenated into SQL and how prepared statements solve the problem for good.

  • Passing values as parameters to prepare() and execute()
  • Building dynamic search queries safely from optional filters
  • Using a whitelist for column names, for example in ORDER BY
  • Deleting and changing data only via POST requests
4

Passwords, Sessions & Access Control

Implemented a login that stores no plain-text passwords and protects every admin page against unauthorised access.

  • Hashing passwords with password_hash()
  • Checking logins with password_verify()
  • Remembering the logged-in user with $_SESSION
  • Guarding protected pages with a requireLogin() check
5

Validating Input & Escaping Output

Treated all external data as untrusted: checked it on the server before saving and escaped it before printing it into HTML.

  • Validating required fields, lengths and number ranges on the server
  • Showing all validation errors at once
  • Escaping output with htmlspecialchars() against XSS
  • Using the Post/Redirect/Get pattern after form submissions
6

Transactions & Data Consistency

Grouped related write operations into transactions so the database never ends up in a half-finished state.

  • Starting transactions with beginTransaction()
  • Confirming with commit() or undoing with rollBack()
  • Handling PDOException without leaking internals
  • Implementing search, filtering, sorting and pagination

Code Examples

Practical examples from the module

login.php
<?php
require __DIR__ . '/db.php';   // creates $pdo
session_start();

$error = null;

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $email    = trim($_POST['email'] ?? '');
    $password = $_POST['password'] ?? '';

    // Prepared statement: user input never ends up inside the SQL string
    $stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email = ?');
    $stmt->execute([$email]);
    $user = $stmt->fetch(PDO::FETCH_ASSOC);

    if ($user && password_verify($password, $user['password_hash'])) {
        session_regenerate_id(true);   // prevents session fixation
        $_SESSION['user_id'] = $user['id'];
        header('Location: admin/index.php');
        exit;
    }

    $error = 'E-mail or password is wrong.';
}
search.php
<?php
// Builds one query from optional filters, always with parameters
$where  = [];
$params = [];

if (!empty($_GET['title'])) {
    $where[]  = 'title LIKE ?';
    $params[] = '%' . $_GET['title'] . '%';
}
if (!empty($_GET['max_price'])) {
    $where[]  = 'price <= ?';
    $params[] = (float) $_GET['max_price'];
}

// Column names cannot be bound as parameters: use a whitelist
$allowedSort = ['title', 'price', 'year'];
$sort = in_array($_GET['sort'] ?? '', $allowedSort, true) ? $_GET['sort'] : 'title';

$sql = 'SELECT id, title, price FROM books';
if ($where) {
    $sql .= ' WHERE ' . implode(' AND ', $where);
}
$sql .= " ORDER BY $sort";

$stmt = $pdo->prepare($sql);
$stmt->execute($params);

foreach ($stmt as $book) {
    echo '<li>' . htmlspecialchars($book['title'], ENT_QUOTES) . '</li>';
}

Skills Acquired

Technologies and concepts mastered in this module

PHP 8 PDO MySQL Prepared Statements SQL Injection Prevention Password Hashing Sessions Authentication Input Validation XSS Protection Transactions HTTP GET & POST

Key Takeaways

  • Never trust input: Everything coming from the browser is validated on the server.
  • Parameters, not strings: Prepared statements make SQL injection impossible by design.
  • Hash, never store: Passwords belong in the database only as password_hash() values.
  • Escape on output: Every value printed into HTML goes through htmlspecialchars().
  • All or nothing: Transactions keep related changes consistent even if something fails.