Ce face PDO
PDO (PHP Data Objects) este alternativa la mysqli, folosită de majoritatea framework-urilor. Te conectezi cu un DSN, un text care spune tipul bazei de date și unde se află: "mysql:host=localhost;dbname=magazin;charset=utf8mb4" pentru MySQL sau "sqlite:magazin.db" pentru un fișier SQLite.
Metodele principale: prepare() pregătește o interogare cu marcaje ? sau cu nume (:nume), execute([...]) o rulează cu valorile date, fetch() citește un rând, fetchAll() le citește pe toate, lastInsertId() dă id-ul generat, iar rowCount() numărul de rânduri modificate. Pentru interogări fără valori externe există query() și exec().
Din PHP 8, PDO aruncă implicit excepții PDOException la erori. Setează și PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ca rândurile să vină ca tablouri asociative. Exemplele de mai jos folosesc o bază SQLite în memorie, ca să poată rula oriunde; cu MySQL se schimbă doar DSN-ul.
Parametri
| Parametru | Descriere |
|---|---|
$dsn |
Tipul bazei de date și datele de conectare, de exemplu "mysql:host=localhost;dbname=magazin;charset=utf8mb4". |
$username |
Opțional. Utilizatorul (nu se folosește la SQLite). |
$password |
Opțional. Parola. |
$options |
Opțional. Setări, de exemplu [PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]. |
Ce returnează
Un obiect PDO. La eroare de conectare aruncă PDOException.
Exemple
Exemplul 1: Conectare, tabel și inserare pregătită
<?php
$db = new PDO("sqlite::memory:", null, null, [
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
// Pentru MySQL: new PDO("mysql:host=localhost;dbname=magazin;charset=utf8mb4", "ana", "parola_ta", [...]);
$db->exec("CREATE TABLE produse (id INTEGER PRIMARY KEY AUTOINCREMENT, nume TEXT, pret INTEGER, stoc INTEGER)");
$stmt = $db->prepare("INSERT INTO produse (nume, pret, stoc) VALUES (:nume, :pret, :stoc)");
foreach ([["Laptop", 3200, 5], ["Mouse", 80, 40], ["Monitor", 900, 0]] as [$nume, $pret, $stoc]) {
$stmt->execute(["nume" => $nume, "pret" => $pret, "stoc" => $stoc]);
}
echo "Ultimul id: " . $db->lastInsertId();
Rezultat
Ultimul id: 3
Exemplul 2: SELECT cu valori de la utilizator
<?php
$db = new PDO("sqlite::memory:", null, null, [PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]);
$db->exec("CREATE TABLE produse (id INTEGER PRIMARY KEY, nume TEXT, pret INTEGER, stoc INTEGER)");
$db->exec("INSERT INTO produse VALUES (1, 'Laptop', 3200, 5), (2, 'Mouse', 80, 40), (3, 'Monitor', 900, 0), (4, 'Tastatură', 150, 12)");
$pretMaxim = 1000; // de exemplu din $_GET
$stmt = $db->prepare("SELECT nume, pret FROM produse WHERE pret <= ? AND stoc > 0 ORDER BY pret");
$stmt->execute([$pretMaxim]);
foreach ($stmt->fetchAll() as $rand) {
echo "{$rand['nume']}: {$rand['pret']} lei\n";
}
$stmt = $db->prepare("SELECT * FROM produse WHERE id = ?");
$stmt->execute([3]);
var_dump($stmt->fetch());
Rezultat
Mouse: 80 lei
Tastatură: 150 lei
array(4) {
["id"]=>
int(3)
["nume"]=>
string(7) "Monitor"
["pret"]=>
int(900)
["stoc"]=>
int(0)
}
Exemplul 3: UPDATE, rowCount și tranzacție
Într-o tranzacție, modificările se salvează toate cu commit() sau se anulează toate cu rollBack(). La final, PDO::FETCH_KEY_PAIR face din cele două coloane un tablou nume => sold.
<?php
$db = new PDO("sqlite::memory:");
$db->exec("CREATE TABLE conturi (nume TEXT, sold INTEGER)");
$db->exec("INSERT INTO conturi VALUES ('Ana', 500), ('Mihai', 100)");
$stmt = $db->prepare("UPDATE conturi SET sold = sold + ? WHERE nume = ?");
$db->beginTransaction();
try {
$stmt->execute([-200, "Ana"]);
$stmt->execute([200, "Mihai"]);
echo "Rânduri modificate la ultimul pas: " . $stmt->rowCount() . "\n";
$db->commit();
} catch (PDOException $e) {
$db->rollBack();
echo "Transfer anulat: " . $e->getMessage() . "\n";
}
foreach ($db->query("SELECT nume, sold FROM conturi")->fetchAll(PDO::FETCH_KEY_PAIR) as $nume => $sold) {
echo "$nume: $sold lei\n";
}
Rezultat
Rânduri modificate la ultimul pas: 1
Ana: 300 lei
Mihai: 300 lei
Greșeli frecvente
- Construiești SQL-ul prin concatenare și îl dai lui
query(). Ai aceeași problemă de SQL injection ca la mysqli. Foloseșteprepare()șiexecute(). - Uiți
charset=utf8mb4din DSN-ul MySQL și diacriticele se strică. - Te aștepți ca
rowCount()să numere rândurile unuiSELECT. Nu toate bazele de date fac asta (SQLite returnează 0). Numără cucount($stmt->fetchAll())sau cuSELECT COUNT(*).
Comenzi înrudite
mysqli_connect: Deschide o conexiune la un server MySQL sau MariaDB și returnează obiectul conexiunii.mysqli_prepare: Pregătește o interogare SQL cu semne ? în loc de valori. Este metoda sigură împotriva SQL injection.try / catch / finally / throw: Prinde erorile (excepțiile) apărute într-un bloc de cod, ca scriptul să nu se oprească brusc.mysqli_insert_id: Returnează id-ul generat automat (AUTO_INCREMENT) de ultimul INSERT.