Current Location: Home> Latest Articles> How to combine PDOStatement::fetchObject and Prepared Statements to prevent SQL injection

How to combine PDOStatement::fetchObject and Prepared Statements to prevent SQL injection

gitbox 2025-05-29

In modern PHP development, security is one of the issues we must prioritize, especially when handling database queries. SQL Injection is a common attack method where attackers can manipulate databases by injecting malicious SQL code. Therefore, the rational use of Prepared Statements and PDOStatement::fetchObject methods can effectively prevent such risks.

What are PDO and Prepared Statements?

PDO (PHP Data Objects) is a database access abstraction layer provided by PHP, which allows you to access multiple different databases in a consistent way.
Prepared Statements is a mechanism provided by PDO that allows you to define SQL structures first and then bind parameters, so that even if the user enters malicious code, it will not be executed as SQL, thereby preventing injection attacks.

Example:

 <?php
// create PDO Example
$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8mb4', 'username', 'password');

// Query with preprocessing statement
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute(['id' => $_GET['id']]);

// Get the result as an object
$user = $stmt->fetchObject();

if ($user) {
    echo "username: " . htmlspecialchars($user->username, ENT_QUOTES, 'UTF-8');
} else {
    echo "User not found。";
}
?>

In this example, the placeholder :id is used to bind the parameters, and PDO automatically handles the escape problem, avoiding SQL injection.

How to use fetchObject correctly?

The fetchObject method allows us to return the query result directly as an object. By default, it returns an anonymous stdClass object, but you can also specify a custom class to host the data.

Suppose we have a User class:

 <?php
class User {
    public $id;
    public $username;
    public $email;
}
?>

Specify the class name when using fetchObject :

 <?php
$stmt = $pdo->prepare('SELECT id, username, email FROM users WHERE id = :id');
$stmt->execute(['id' => $_GET['id']]);

$user = $stmt->fetchObject('User');

if ($user) {
    echo "welcome, " . htmlspecialchars($user->username, ENT_QUOTES, 'UTF-8');
}
?>

The advantage of this is that the structure is clearer and easier to expand later. For example, you can add methods to the User class for logical processing.

Safety Tips

Even if PDO and fetchObject are used, the following points should still be noted:

  • Never directly splice SQL statements entered by users.

  • Always use placeholders (named placeholders or question mark placeholders) and bind parameters.

  • Use htmlspecialchars to properly escape content output to web pages to prevent XSS attacks.

  • Set appropriate error handling modes, for example:

 <?php
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
?>
  • Differentiate between development and production environments. The production environment should prohibit the direct display of error messages to prevent the database structure from being leaked.

Additional content: processing of query failure

A robust system should be able to handle database errors gracefully. For example:

 <?php
try {
    $stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
    $stmt->execute(['id' => $_GET['id']]);
    $user = $stmt->fetchObject('User');

    if (!$user) {
        header('Location: https://gitbox.net/not-found');
        exit;
    }

    echo "Hello, " . htmlspecialchars($user->username, ENT_QUOTES, 'UTF-8');
} catch (PDOException $e) {
    error_log($e->getMessage());
    header('Location: https://gitbox.net/error');
    exit;
}
?>

In this example, we catch the query error through exception handling and jump to the friendly error page as the situation is.