Ta strona używa ciasteczek (cookies), dzięki którym nasz serwis może działać lepiej. Dowiedz się więcej OK, rozumiem
WebHelp.pl Warsztat Artykuły Obsługa baz danych za pomocą PDO

Warsztat / Artykuły i tutoriale

Obsługa baz danych za pomocą PDO

Bartosz Romanowski 19 stycznia 2011 komentarze ()

W trakcie tworzenia aplikacji w PHP wykorzystującej bazę danych często okazuje się, że programista nie jest pewien, na jakiej konkretnie bazie danych będzie działać jego dzieło. Istnieje kilka metod uniezależnienia skryptów od typu bazy danych, a jedną z najprostszych jest skorzystanie z PDO.

Co to jest PDO?

PDO (PHP Data Objects) to rozszerzenie PHP o architekturze obiektowej, udostępniające jednolity, uniwersalny interfejs do komunikacji z bazami danych. Jest oparte na sterownikach, z których każdy udostępnia takie same metody, aczkolwiek może również dodawać własne, specyficzne dla konkretnej bazy danych (na przykład sterownik dla PostgreSQL udostępnia trzy dodatkowe metody do obsługi dużych obiektów - large object). PDO jest dostępne w PHP 5.1 i nowszych wersjach i nie wymaga dodatkowej konfiguracji (poza dodaniem sterowników dla wykorzystywanych baz danych).

Największą zaletą PDO jest jednolity interfejs. PHP posiada osobne rozszerzenia dla każdej bazy danych. Jeśli chcemy skorzystać z bazy MySQL, wywołujemy funkcje z prefiksem mysql_ lub mysqli_, jeśli korzystamy z PostgreSQL - funkcje z prefiksem pg_. Dzięki PDO nasze skrypty będą korzystać z tych samych metod, niezależnie od wykorzystywanej bazy. Trzeba jednak pamiętać, że PDO nie uniezależnia nas w stu procentach od wykorzystywanej bazy danych - jeśli w zapytaniu skorzystamy z funkcji specyficznej dla danego silnika, to zapytanie nie zadziała na innej bazie. Drugą zaletą PDO jest walidacja danych wysyłanych w zapytaniach oraz zwolnienie programisty z konieczności filtrowana tych danych pod kątem zabezpieczeń przed atakami typu SQL Injection.

Na chwilę obecną dostępne są sterowniki dla następujących baz danych: MySQL, PostgreSQL, SQLite, Oracle (eksperymentalny), MS SQL/Sybase (eksperymentalny), Firebird/Interbase (eksperymentalny), Informix, IBM DB2, ODBC i 4D (eksperymentalny).

Nawiązywanie połączenia z bazą danych

Połączenie z wybraną bazą danych realizuje konstruktor klasy PDO, w którego parametrach przekazujemy DSN (Data Source Name), nazwę użytkownika i hasło. DSN to pierwsza z różnic pomiędzy standardowymi interfejsami bazodanowymi a PDO. Jest to "opis" połączenia z bazą danych, zawierający takie informacje, jak nazwa sterownika, adres serwera, nazwa bazy czy numer portu.

Przykładowe połączenie z bazą może wyglądać tak:

Kod: Zaznacz cały
// dla bazy danych MySQL
$db = new PDO('mysql:host=localhost;dbname=nazwa_bazy', 'nazwa_uzytkownika', 'haslo');

// dla bazy danych SQLite
$db = new PDO('sqlite:test.db');

PDO posiada obsługę błędów opartą o wyjątki. Warto o tym pamiętać, ponieważ jeśli nie obsłużymy rzucanych wyjątków, a coś pójdzie nie po naszej myśli, to skrypt zakończy się z groźnie brzmiącym i mało przyjemnym w odbiorze komunikatem, który na dodatek (w przypadku błędu podczas łączenia z bazą) zawiera przekazane w parametrach konstruktora login i hasło do bazy danych.

Kod: Zaznacz cały
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[28000] [1045] Access denied for user 'nazwa_uzytkownika'@'localhost' (using password: TAK)' in /home/users/test/public_html/pdo.php:5 Stack trace: #0 /home/users/test/public_html/pdo.php(5): PDO->__construct('mysql:host=127....', 'nazwa_uzytkownika', 'haslo') #1 {main} thrown in /home/users/test/public_html/pdo.php on line 5

Obsłużyć wyjątki można na przykład tak:

Kod: Zaznacz cały
try
{
    $db = new PDO('mysql:host=localhost;dbname=nazwa_bazy', 'nazwa_uzytkownika', 'haslo');
}
catch (PDOException $e)
{
    print "Błąd połączenia z bazą!: " . $e->getMessage() . "<br/>";
    die();
}

Konstruktor klasy PDO może przyjmować czwarty parametr, który ma postać tablicy (array) i zawiera ustawienia dla sterownika, na przykład:

Kod: Zaznacz cały
$db = new PDO('mysql:host=localhost;dbname=nazwa_bazy', 'nazwa_uzytkownika', 'haslo', array(PDO::ATTR_PERSISTENT => true));

spowoduje, że sterownik bazy MySQL będzie tworzył stałe połączenia.

Wykonywanie zapytań

Do wykonywania zapytań służą dwie metody - exec() i query(). Metoda exec() zwraca liczbę rekordów zmodyfikowanych, usuniętych lub dodanych w wyniku wykonania zapytania. Jak więc nietrudno się domyślić, wykorzystuje się ją w przypadku zapytań UPDATE, DELETE i INSERT. Metoda query() zwraca obiekt PDOStatement, z którego łatwo wyciągniemy sobie rekordy, jakie otrzymaliśmy od bazy danych. Obsługuje ona również podpinanie parametrów (bind), ale o tym za chwilę.

Przykład wywołania metody exec():

Kod: Zaznacz cały
$usuniete_rekordy = $db->exec('DELETE FROM tabela WHERE id = 5');
echo('Usuniętych rekordów: '.$usuniete_rekordy);

Przykład wywołania metody query() z wyświetleniem wszystkich zwróconych przez zapytanie rekordów:

Kod: Zaznacz cały
$statement = $db->query('SELECT imie, nazwisko FROM tabela');
foreach($statement as $wiersz)
{
    echo($wiersz['imie']." ".$wiersz['nazwisko']."<br />");
}
$statement->closeCursor();

Podpinanie parametrów

Podpinanie parametrów to jedna z istotniejszych cech PDO. Pozwala na skuteczne zabezpieczenie przez atakami typu SQL Injection, a także automatycznie sprawdza dane przesyłane do bazy danych pod kątem zgodności typu. Polega to na tym, że najpierw tworzymy zapytanie z parametrami, a następnie do tych parametrów podstawiamy odpowiednie dane. Wygląda to mniej więcej tak:

Kod: Zaznacz cały
$statement = $db->prepare('SELECT imie, nazwisko FROM tabela WHERE id = :id');
$statement->bindValue(':id', $_GET['id'], PDO::PARAM_INT);
$statement->execute();
foreach($statement as $row)
{
    echo($row['imie']." ".$row['nazwisko']."<br />");
}
$statement->closeCursor();

W powyższym przykładzie baza (lub sterownik, jeśli konkretna baza nie ma takich możliwości) sprawdzi przekazaną do :id wartość, zarówno pod kątem potencjalnie niebezpiecznej zawartości, jak i typu (w tym przypadku INTEGER). Jeśli nie potrzebujemy sprawdzania danych pod kątem typu, możemy przekazać wartości w metodzie execute():

Kod: Zaznacz cały
$statement = $db->prepare('SELECT imie, nazwisko FROM tabela WHERE id = :id');
$statement->execute(array(':id' => $_GET['id']));

Parametry można również przekazywać bez ich nazywania, na przykład tak:

Kod: Zaznacz cały
$statement = $db->prepare('SELECT imie, nazwisko FROM tabela WHERE id > ? AND id < ?');
$statement->bindValue(1, 15, PDO::PARAM_INT);
$statement->bindValue(2, 30, PDO::PARAM_INT);
// alternatywnie zamiast podpinania (bindValue)
$statement->execute(array(15, 30));

Podpinanie parametrów ma również wpływ na wydajność w momencie, gdy wykonujemy wielokrotnie takie samo zapytanie INSERT, DELETE lub UPDATE, ale z innymi parametrami. Załóżmy, że w tablicy $data mamy dane do dopisania do bazy.

Kod: Zaznacz cały
// ten sposób będzie wolniejszy
foreach($data as $row)
{
    $db->exec("INSERT INTO tabela (imie, nazwisko) VALUES ('".$row['imie']."', '".$row['nazwisko']."')");
}

// ten sposób będzie szybszy
$statement = $db->prepare("INSERT INTO tabela (imie, nazwisko) VALUES (:imie, :nazwisko)");
foreach($data as $row)
{
    $statement->bindValue(':imie', $row['imie'], PDO::PARAM_STR);
    $statement->bindValue(':nazwisko', $row['nazwisko'], PDO::PARAM_STR);
    $statement->execute();
}

Obsługa błędów

Do obsługi błędów możemy wykorzystać wyjątki (tak jak w przykładzie poświęconym połączeniu z bazą danych) lub metody PDO::errorInfo() i PDOStatement::errorInfo().

Jeśli chcemy skorzystać z wyjątków, musimy ustawić odpowiednie parametry, najlepiej zaraz po nawiązaniu połączenia z bazą:

Kod: Zaznacz cały
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Jeśli natomiast chcemy korzystać z wymienionych wyżej metod, to może to wyglądać na przykład tak:

Kod: Zaznacz cały
$statement = $db->prepare('SELECT imie, nazwisko FROM tabela WHERE id = :id');
// sprawdzamy czy wywołanie prepare() się udało
if(!$statement)
{
    var_dump($db->errorInfo());
    die();
}
$statement->bindValue(':id', 15, PDO::PARAM_INT);
$statement->execute(array(':id' => 15));
// sprawdzamy czy wywołanie execute() się udało
$error = $statement->errorInfo();
if($error[0])
{
    var_dump($error);
    die();
}

Transakcje

Interfejs PDO wspiera transakcje, do których obsługi udostępnia metody beginTransaction(), commit() i rollback(). Mimo że sprawdzanie dostępności transakcji odbywa się na poziomie sterownika, to trzeba pamiętać, że metoda beginTransaction() może zwrócić wartość true, mimo że transakcje nie są dostępne. Za przykład może posłużyć próba otwarcia transakcji na tabelach MyISAM w bazie MySQL, które to tabele transakcji nie obsługują.

Niniejszy tekst jest tylko wstępem do PDO i nie obejmuje wszystkich aspektów korzystania z tego interfejsu. Aby poznać pozostałe jego możliwości polecam zapoznanie się z dokumentacją.

Masz pytania lub wątpliwości? Odwiedź nasze forum dyskusyjne.

Bartosz Romanowski

Programista, gadżeciarz, krytyczny miłośnik produktów Apple, fan ciężkich brzmień i niepoprawny pesymista.


Komentarze


HTML CSS JavaScript PHP bazy danych MySQL Flash grafika framework hosting domeny pozycjonowanie wordpress Facebook