PHP 8: Modern PHP Development · Databases with PDO

Prepared Statements and Safe Queries

Prevent SQL injection with bound parameters, including IN lists and dynamic sorting.

Always pass user data as bound parameters, never by concatenating it into SQL. Parameters can't be used for identifiers (table/column names) or keywords like ASC/DESC — for those, map user input to an allow-list. For IN (...) lists, generate one placeholder per value.

<?php
$stmt = $pdo->prepare('SELECT id, email FROM users WHERE email = :email AND active = :active');
$stmt->execute(['email' => $email, 'active' => 1]);
$user = $stmt->fetch();

// IN list
$ids = [3, 7, 9];
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$stmt = $pdo->prepare("SELECT * FROM orders WHERE id IN ($placeholders)");
$stmt->execute($ids);

// Dynamic sort with an allow-list
$sortable = ['created' => 'created_at', 'total' => 'total_cents'];
$column = $sortable[$_GET['sort'] ?? 'created'] ?? 'created_at';
$direction = ($_GET['dir'] ?? '') === 'asc' ? 'ASC' : 'DESC';
$rows = $pdo->query("SELECT * FROM orders ORDER BY {$column} {$direction} LIMIT 50")->fetchAll();

Common pitfalls

  • Building WHERE name = '$name' strings — classic SQL injection.
  • Trying to bind table or column names as parameters.
  • Using addslashes() as 'escaping'.

Key points

  • Bind every value
  • Allow-list identifiers and sort directions
  • Generate placeholders for IN lists

Further reading: PHP manual: prepared statements

Practice questions

How do you safely let users choose the ORDER BY column?

Answer

Map their input to an allow-list of known column names (['price' => 'price_cents']) and default to a safe column. Identifiers can't be parameterized.

Why are prepared statements effective against SQL injection?

Answer

The SQL structure is sent and parsed separately from the parameter values, so values are always treated as data, never as SQL code.

Open in the app to mark lessons complete, take notes and track your progress.