Przedstawmy zalety i wady tego rozszerzenia w porównaniu z MySQLi:
- PDO obsługuje wiele interfejsów baz danych. MySQLi (jak sama nazwa wskazuje) skupia się jedynie na systemie MySQL, natomiast w PDO wystarczą niewielkie zmiany, aby pasował on pod takie systemy baz danych jak np. PostgreSQL, SQLite oraz oczywiście MySQL. Oznacza to, że kod w PDO jest wysoce przenośny i doskonale nadaje się do projektów na sprzedaż (np. silniki gier).
- Mamy do dyspozycji podpinanie danych, dzięki któremu programista zaoszczędzi sobie pracy z pisaniem dodatkowego kodu. PDO pomoże nam zabezpieczyć zapytania w zaledwie 2 linijkach.
- Łatwy i przejrzysty zapis instrukcji.
- Duża wygoda korzystania.
- Multum poradników.
- W niektórych przypadkach MySQLi okazuję się być szybsze, jednak to nie przysłania wszystkich zalet PDO.
Bez względu na to co wybierzesz i tak będzie to lepszy sposób niż stare mysql_*

1. Pierwszą czynnością będzie ustanowienie nowego połączenia. Najprościej zrobimy to w ten sposób:
try { $baza = new PDO('mysql:host=localhost;dbname=testowa_baza;charset=utf8', 'użytkownik', 'hasło'); // kod php } catch(PDOException $err) { }
mysql: definiuję system bazy danych, na którym będziemy opierać swoje działania. Dalej podajemy host, nazwę bazy, ustalamy na szybko kodowanie (utf8) oraz wpisujemy login użytkownika + jego hasło.
Blok try { ... } catch { ... } po prostu obsługuję zaistniałe błędy. Pomiędzy klamrami od try zamieszczamy cały skrypt naszej strony, a w catch ustalamy sposób raportowania błędów. Robimy to poprzez wyjątek PDOException, który zwraca nam kilka możliwych metod. Skorzystaliśmy z getMessage() przechowującą pełną informację o błędzie. Co należy wiedzieć o PDOException? Jest na tyle fajne, że nie musimy umieszczać dotychczasowego or die mysql_error.. a jedną linijką dostaniemy wszystkie informację.
Choć kod działa i dodatkowo prosto, to nie oznacza, że w pełni swoich możliwości. Wprowadźmy parę zmian:
// pobraliśmy skądś dane do połączenia (np. pliku) $host = 'localhost'; $user = 'root'; $pass = ''; $base = 'testowa'; try { $baza = new PDO('mysql:host='.$host.';dbname='.$base.';charset=utf8', $user, $pass, // wyłączenie zbędnego emulate prepares PDO::ATTR_EMULATE_PREPARES => false, // ustalenie sposobu raportowania błędów PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION )); // kod php } catch(PDOException $err) { }
Dodaliśmy kilka parametrów i przy okazji przedstawiłem sposób zamieszczania zmiennych w połączeniu.
2. Będąc już połączonym z bazą danych, przyszedł czas na parę zapytań. Odpowiednikiem mysql_query() jest query():
// pierwszy przykład $zapytanie = $baza->query("SELECT login FROM gracze WHERE id = 2"); $zapytanie->closeCursos(); // zamknięcie pierwszego zapytania // drugi przykład $nazwa_nadawcy = 'shady'; $minimum_id = 5; $zapytanie = $baza->query("SELECT * FROM poczta WHERE nadawca = '".$nazwa_nadawcy."' AND id >= ".$minimum_id);
Jak widać nie ma tu praktycznie nic nowego.
Omówić należy closeCursor(). Używamy go wtedy, gdy wykonamy zapytanie typu SELECT, a zaraz po nim chcemy wykonać inne zapytanie POBIERAJĄCE dane. Wtedy zamykamy tzw. kursor zapytania. Jest wiele teorii, dla których ten zapis powinien być stosowany. W sieci spotkamy się z tym, że pozwala to nadać większej kompatybilności między różnymi interfejsami, a nawet, iż przyśpiesza to działanie, choć osobiście nie sprawdzałem różnic..
3. Należy zwrócić uwagę, że polecenia query() użyjemy tylko do zapytań SELECT, a wszelkie inne jak UPDATE, DELETE, INSERT itp. zamieszczamy w exec():
// przykład pierwszy $baza->exec("UPDATE gracze SET zloto = zloto + 5 WHERE id = ".$gracz['id']); // przykład drugi $baza->exec("INSERT INTO przedmioty(nazwa, gracz_id, sila, obrona) VALUES('".$item['nazwa']."', ".$gracz['id'].", ".$item['sila'].", ".$item['obrona'].")");
W przeciwieństwie do query() metoda exec() zwraca liczbę, a dokładniej liczbę zoperowanych rekordów. Przykład:
$ile = $baza->exec("UPDATE gracze SET energia = 100 WHERE energia <= 20");
4. Nie pokazaliśmy jeszcze jak pobrane dane przy użyciu query() przypisać do elementu dokumentu:
$zapytanie = $baza->query("SELECT login, level FROM gracze WHERE id = ".$_SESSION['id']); $gracz = $zapytanie->fetch();
Proste? Proste

- $dane = $zap->fetch(PDO::FETCH_BOTH) - ustawiane domyślnie, możemy stosować zarówno $dane['kolumna'] jak i $dane[1]
- $dane = $zap->fetch(PDO::FETCH_ASSOC) - pozwala na stosowanie wyłącznie indeksów asocjacyjnych ($dane['kolumna'])
- $dane = $zap->fetch(PDO::FETCH_OBJ) - zwraca tablice z indeksami wyłącznie numerycznymi ($dane[1])
- $dane = $zap->fetch(PDO::FETCH_OBJ) - zwraca dane jako anonimowy obiekt ($dane->kolumna)
oraz 4 inne (patrz manual)
A tak wygląda fetchowanie z wykorzystaniem pętli (przykład na liście graczy z levelem większym lub równym 50):
$gracze = $baza->query("SELECT login, level FROM gracze WHERE level >= 50"); while ($rekord = $gracze->fetch()) { }
Warto napisać o sposobie wyświetlania liczby pobranych rekordów:
$gracze = $baza->query("SELECT login, level FROM gracze WHERE level >= 50");
5. Bindowanie danych, a mówiąc polskim odpowiednikiem: podpinanie danych. To jedna z największych zalet PDO, a zarazem wnosząca największą ze zmian w zapisie kodu. Na czym ono polega? Nie wykonujemy od razu zapytania, lecz najpierw je pod to przygotowujemy. Ustalamy dane, które mają ulec filtracji i dopiero teraz możemy je wykonać.
Przyjmijmy, że mamy formularz logowania i chcemy sprawdzić istnienie danego konta. W tym celu wykonujemy do bazy danych zapytanie, mające na celu odnalezienie konta o wprowadzonym nicku.
$zapytanie = $baza->query("SELECT id, haslo FROM gracze WHERE login = '".$_POST['login']."'"); $konto = $zapytanie->fetch();
Powyższy zapis naraża bezpieczeństwo naszej aplikacji na katastrofalne straty. Potencjalny hacker wykorzystujący takie nie zabezpieczone dane może wykonać dosłownie KAŻDE zapytanie! W tym usunąć użytkowników, a także całą bazę..
Jak sobie z tym poradzić? Nie jest to trudne, a dzięki PDO wręcz banalne. Każde dane otrzymywane od użytkowników (zmienne super globalne GET, POST, ale także ciastka i sesje) odkazić z potencjalnego niebezpieczeństwa. Po prostu sprawdzić, czy nie zawierają one cudzysłowów, kodu html/js i innego świństwa, które należałoby zamienić.
Nowy zapis ostatniego kodu powinien mieć taką formę:
// przygotowanie zapytania $zapytanie = $baza->prepare("SELECT id, haslo FROM gracze WHERE login = :login"); // filtracja wskazanych danych $zapytanie->bindValue(':login', $_POST['login'], PDO::PARAM_STR); // właściwe wykonanie $zapytanie->execute(); $konto = $zapytanie->fetch();
bindValue przyjmuję 3 parametry:
- nazwa używana w zapytaniu, poprzedzona dwukropkiem (:)
- dane, które mają ulec filtracji
- pod jakim kątem przefiltrować te dane
Jak widać w 3 parametr (kąt filtracji) podaliśmy akurat PDO::PARAM_STR, co oznacza, że PDO ma filtrować odebrane dane jako ciąg znaków. Drugą wartością dla tego parametru może być PDO::PARAM_INT, czyli filtracja dla liczb. Inne znajdziemy >tutaj<. Bindować możemy nie tylko SELECT, ale i całą resztę
W celach lepszego zrozumienia dam kolejny przykład. Panel administratora wyświetlający adresy email graczy o podanym levelu i klasie postaci.
// przygotowanie zapytania $zapytanie = $baza->prepare("SELECT login, email FROM gracze WHERE level = :poziom AND klasa= :klasa"); // filtracja wskazanych danych $zapytanie->bindValue(':poziom', $_POST['poziom'], PDO::PARAM_INT); // właściwe wykonanie $zapytanie->execute(); if ($gracze->rowCount() > 0) { while ($gracz = $zapytanie->fetch()) { }
Bindowanie nie ma w sobie trimowania (usuwania z początku i końca linii białych znaków) dlatego przy klasie postaci (ogólna tendencja głównie do stringów) użyliśmy jeszcze funkcji trim().
Nie wspomniałem o przydatnej funkcji, która zwróci numer id ostatnio dodanego wiersza przez polecenie INSERT.
$baza->exec("INSERT INTO kurs_pdo(uzytkownik, stan) VALUES('Ty', 'zaliczony')");
Po więcej wiedzy warto zajrzeć oczywiście do manuala.
Przepraszam, jeśli temat wykracza poza tematykę działu, ale lubię się czymś dzielić, a poza tym nie znalazłem tu innego działu do zamieszczenia tego
