Skip to main content

Was ist PDO?

 

PDO steht für PHP Data Objects. Es ist eine konsistente Schnittstelle, um mit verschiedenen Datenbanken (wie MySQL, PostgreSQL, SQLite etc.) zu kommunizieren. Der große Vorteil: Du kannst die Datenbank wechseln, ohne deinen gesamten PHP-Code für die Datenbankinteraktion umschreiben zu müssen. PDO hilft außerdem dabei, SQL-Injection-Angriffe durch die Verwendung von Prepared Statements zu verhindern.


 

1. Die Verbindung herstellen (DSN)

 

Um eine Verbindung herzustellen, benötigst du einen DSN (Data Source Name). Dieser String enthält alle Informationen, die PHP benötigt, um die richtige Datenbank auf dem richtigen Server zu finden.

Der Aufbau des DSN variiert je nach Datenbank:

  • MySQL: mysql:host=dein_host;dbname=dein_db_name;charset=utf8mb4
  • PostgreSQL: pgsql:host=dein_host;dbname=dein_db_name
  • SQLite: sqlite:/pfad/zu/deiner/datenbank.db

Die Verbindung selbst wird innerhalb eines try-catch-Blocks erstellt. Das ist wichtig, um Verbindungsfehler professionell abzufangen.

Code-Beispiel (MySQL):

PHP

$host = 'localhost';
$db   = 'meine_datenbank';
$user = 'root';
$pass = 'mein_passwort';
$charset = 'utf8mb4';

$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // Wichtig, siehe unten
    PDO::ATTR_EMULATE_PREPARES   => false,
];

try {
     $pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
     throw new \PDOException($e->getMessage(), (int)$e->getCode());
}

Was bedeuten die $options?

  • PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION: Weist PDO an, bei Fehlern eine Exception (eine Art kritischer Fehler) auszulösen, die du mit catch abfangen kannst. Das ist der Standard für modernes PHP.
  • PDO::ATTR_DEFAULT_FETCH_MODE: Legt fest, wie du deine Daten standardmäßig aus der Datenbank zurückbekommst. Die zwei wichtigsten Modi sind:
    • PDO::FETCH_ASSOC: Gibt die Daten als assoziatives Array zurück (z.B. ['benutzername' => 'Max', 'email' => 'max@test.de']). Das ist der gebräuchlichste Modus.
    • PDO::FETCH_OBJ: Gibt die Daten als anonymes Objekt zurück (z.B. $user->benutzername, $user->email).

 

2. Daten abfragen: Der richtige Weg

 

Es gibt zwei grundlegende Wege, um Daten abzufragen: query() für einfache, statische Abfragen und prepare()/execute() für Abfragen mit variablen Daten.

 

A) Einfache Abfrage ohne variable Daten (query())

 

Wenn deine SQL-Abfrage keine variablen Eingaben (z.B. von einem Benutzer) enthält, kannst du query() verwenden. Es ist ein Einzeiler und sehr direkt.

PHP

// Führt die Abfrage direkt aus und holt alle Ergebnisse
$listings = $pdo->query("SELECT * FROM listings LIMIT 6")->fetchAll();

foreach ($listings as $listing) {
    echo $listing['title'] . '<br>'; // Wenn FETCH_ASSOC eingestellt ist
}

 

B) Sichere Abfrage mit variablen Daten (prepare() & execute())

 

Dies ist der Standardweg und die sicherste Methode, insbesondere wenn Benutzerdaten ins Spiel kommen. Sie schützt vor SQL-Injection.

Der Prozess:

  1. prepare(): Du bereitest die SQL-Anweisung mit Platzhaltern (? oder benannte wie :id) vor. Die Datenbank analysiert und kompiliert die Anweisung, ohne die tatsächlichen Werte zu kennen.
  2. execute(): Du sendest die Werte für die Platzhalter an die Datenbank. Die Datenbank fügt die Werte sicher in die vorbereitete Anweisung ein.

Beispiel mit Platzhaltern (?):

PHP

$status = 'active';
$limit = 10;

// 1. Vorbereiten (prepare)
$stmt = $pdo->prepare("SELECT * FROM listings WHERE status = ? LIMIT ?");

// 2. Ausführen (execute) mit den Werten in einem Array
// Die Reihenfolge im Array muss der Reihenfolge der '?' entsprechen
$stmt->execute([$status, $limit]);

// 3. Daten abholen
$listings = $stmt->fetchAll();

Beispiel mit benannten Platzhaltern (:):

PHP

$status = 'active';

// 1. Vorbereiten (prepare)
$stmt = $pdo->prepare("SELECT * FROM listings WHERE status = :status");

// 2. Ausführen (execute) mit einem assoziativen Array
$stmt->execute(['status' => $status]);

// 3. Daten abholen
$listing = $stmt->fetch(); // Holt nur die nächste Zeile

 

3. Daten abholen (Fetching)

 

Nachdem du eine Abfrage mit execute() ausgeführt hast, musst du die Ergebnisse noch „abholen“.

  • fetchAll(): Holt alle Ergebniszeilen auf einmal in ein großes Array. Praktisch für kleinere Ergebnismengen.
    PHP

    $users = $stmt->fetchAll();
    // $users ist jetzt ein Array von Arrays (oder Objekten)
    
  • fetch(): Holt nur die nächste Zeile aus dem Ergebnis. Wenn du es in einer while-Schleife verwendest, kannst du sehr große Ergebnismengen Zeile für Zeile verarbeiten, ohne den gesamten Speicher zu füllen.
    PHP

    // Holt eine einzelne Zeile
    $user = $stmt->fetch();
    
    // Verarbeitet viele Zeilen speicherschonend
    while ($row = $stmt->fetch()) {
        echo $row['username'] . '<br>';
    }
    
  • Iteration über das Statement-Objekt: Du kannst ein PDO-Statement-Objekt auch direkt in einer foreach-Schleife verwenden. Das ist oft die eleganteste und speicherschonendste Methode.
    PHP

    $stmt = $pdo->query("SELECT name FROM users");
    foreach ($stmt as $row) {
        echo $row['name'] . '<br>';
    }
    

 

Das Zusammenspiel mit extract()

 

Die Funktion extract() kann Variablen aus einem assoziativen Array erstellen. Wenn du PDO::FETCH_ASSOC verwendest, erhältst du pro Zeile ein solches Array.

Wie es funktioniert:

extract() nimmt ein Array wie [’name‘ => ‚Max‘, ‚alter‘ => 30] und erstellt daraus die Variablen $name = ‚Max‘; und $alter = 30;.

Anwendungsbeispiel in einer Schleife:

PHP

$stmt = $pdo->query("SELECT title, description FROM listings LIMIT 1");
$listing_data = $stmt->fetch(PDO::FETCH_ASSOC);

if ($listing_data) {
    extract($listing_data);

    // Jetzt kannst du direkt auf die Variablen zugreifen
    echo "<h1>$title</h1>";
    echo "<p>$description</p>";
}

Achtung: Sei vorsichtig mit extract() bei Daten, die du nicht kontrollierst (z.B. aus $_POST oder $_GET), da es bestehende Variablen überschreiben und zu Sicherheitslücken führen kann. Bei Datenbankergebnissen, deren Spaltennamen du kennst, ist es jedoch ein praktisches Werkzeug, um den Code in Templates (HTML-Ansichten) sauberer zu halten.


 

Reihenfolge und Zusammenfassung

 

  1. Verbindungsdaten definieren (Host, DB-Name, User, Passwort).
  2. DSN und Optionen erstellen.
  3. PDO-Objekt in einem try-catch-Block instanziieren.
  4. SQL-Query schreiben. Bei variablen Daten Platzhalter verwenden.
  5. prepare() aufrufen, um das Statement-Objekt zu erhalten.
  6. execute() mit den Daten als Array aufrufen.
  7. Daten abholen mit fetchAll(), fetch() oder durch Iteration.
  8. Daten verarbeiten und ausgeben.

 

Vorlage / Template für eine Datenbank-Klasse

 

Hier ist eine einfache, wiederverwendbare Klasse, die du als Grundlage für deine Projekte nutzen kannst.

Datei: Database.php

PHP

<?php

class Database {
    public ?PDO $conn = null;

    public function __construct(string $host, string $dbname, string $user, string $password) {
        $dsn = "mysql:host={$host};dbname={$dbname};charset=utf8mb4";
        $options = [
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES   => false,
        ];

        try {
            $this->conn = new PDO($dsn, $user, $password, $options);
        } catch (PDOException $e) {
            // Im echten Leben hier loggen statt ausgeben
            die("Verbindungsfehler: " . $e->getMessage());
        }
    }

    /**
     * Eine Abfrage ausführen und alle Ergebnisse zurückgeben.
     * @param string $query Die SQL-Anweisung mit Platzhaltern.
     * @param array $params Die Parameter für die Abfrage.
     * @return array
     */
    public function query(string $query, array $params = []): array {
        try {
            $stmt = $this->conn->prepare($query);
            $stmt->execute($params);
            return $stmt->fetchAll();
        } catch (PDOException $e) {
            // Fehlerbehandlung
            error_log("Query failed: " . $e->getMessage());
            return [];
        }
    }

     /**
     * Führt eine Abfrage aus und gibt das Statement-Objekt zurück.
     * Nützlich für große Datensätze oder wenn man Zeile für Zeile durchgehen will.
     * @param string $query Die SQL-Anweisung mit Platzhaltern.
     * @param array $params Die Parameter für die Abfrage.
     * @return PDOStatement|false
     */
    public function execute(string $query, array $params = []) {
        try {
            $stmt = $this->conn->prepare($query);
            $stmt->execute($params);
            return $stmt;
        } catch (PDOException $e) {
            error_log("Execute failed: " . $e->getMessage());
            return false;
        }
    }
}

Anwendungsbeispiel:

Datei: index.php

PHP

<?php
require 'Database.php';

// Konfiguration
$config = [
    'host' => 'localhost',
    'dbname' => 'meine_datenbank',
    'user' => 'root',
    'password' => 'mein_passwort'
];

// Datenbankobjekt erstellen
$db = new Database($config['host'], $config['dbname'], $config['user'], $config['password']);

// Alle aktiven Produkte abrufen
$activeProducts = $db->query("SELECT * FROM products WHERE status = ?", ['active']);

foreach ($activeProducts as $product) {
    echo 'Produktname: ' . htmlspecialchars($product['name']) . '<br>';
}

// Einen einzelnen Benutzer abrufen
$user_id = 123;
$userStmt = $db->execute("SELECT * FROM users WHERE id = :id", ['id' => $user_id]);
$user = $userStmt->fetch();

if ($user) {
    echo 'Willkommen, ' . htmlspecialchars($user['username']);
}

Leave a Reply