SQL injection has been on the OWASP Top 10 for over two decades, and it’s still one of the most common vulnerabilities found in PHP applications — almost always because someone concatenated user input directly into a query string.
// VULNERABLE - never do this
$id = $_GET['id'];
$result = $pdo->query("SELECT * FROM users WHERE id = $id");
// SAFE - prepared statement with a bound parameter
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $_GET['id']]);
$user = $stmt->fetch();
Prepared statements work because the query structure is sent to the database separately from the data — the database never interprets the bound values as SQL syntax, no matter what characters they contain. This isn’t a best practice with exceptions; it should be the only way you ever build a query with user input.