PDO Marijan Šuflaj FER, 2018
Sadržaj PDO Osnove Izvršavanje upita Ranjivosti Dohvaćanje rezultata upita
PDO - PHP Data Objects Jednostavno i konzistetno sučelje za pristup bazama podataka iz PHP-a Isti se kod može spajati na više različitih baza podataka Objektno orjentirani pristup Možemo naslijediti razrede i implementirati Dva bitna razreda PDO PDOStatement
Razred PDO Razred predstavlja konekciju prema poslužitelju Instanciranjem se razreda odmah uspostavlja veza Parametri se konekcije prosljeduju kao DSN (Database source name) niz znakova mysql:dbname=ime-baze;host=host;charset=utf8 Kroz konstruktor se prosljeduju korisničko ime i lozinka Moguće je proslijediti i atribute radi promjene pretpostavljenih ponašanja razreda Promjena načina dojave grešaka na iznimke secure.php.net/manual/en/class.pdo.php
Spajanje na poslužitelj try { $dsn = 'mysql:dbname=oipa-predavanja;'. 'host=oipa-db;charset=utf8'; $db = new PDO( $dsn, 'root', 'oipa-password', [ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ] ); } catch (PDOException $e) { throw $e; }
Zadatak 1 Povucite novu Docker sliku pomoću docker pull oipa/backend:2.0 Rekreirajte kontejner web-poslužitelja Ostvarite uspješnu konekciju na lokalnu bazu podataka koju koristite na predavanjima
Razred PDOStatement Predstavlja pripremljeni upit kao i rezultat izvršavanja tog upita Implementira sučelje Traversable pa se objekt može koristiti unutar foreach Rezultati iteracije ovise o načinu dohvaćanja rezultata Postavili smo kod stvaranja konekcije (PDO::ATTR DEFAULT FETCH MODE) Pruža siguran način izvršavanja upita Ne ubacujemo parametre izravno u upit već naznačujemo mjesta gdje će ti parametri biti ubačeni secure.php.net/manual/en/class.pdostatement.php
Dohvaćanje zapisa iz baze $sql = 'SELECT id, ime FROM kategorija'; $stmt = $db->query($sql); var_dump($stmt->rowcount()); foreach ($stmt as $row) { var_dump($row); }
Parametrizirano dohvaćanje zapisa iz baze $id = $_GET['id']?? 0; $sql = <<<"SQL" SELECT id, ime FROM kategorija WHERE id = $id SQL; $stmt = $db->query($sql); var_dump($stmt->rowcount()); foreach ($stmt as $row) { var_dump($row); } Problemi?
Ranjivost na ubacivanje SQL koda Nezaštićeni su parametri ranjivi na izvršavanje proizvoljnog SQL koda Potrebno je osigurati sigurne vrijednosti parametara $id = $db->quote( $_GET['id']?? '0', PDO::PARAM_INT ); en.wikipedia.org/wiki/sql injection
Ranjivost na ubacivanje SQL koda - nastavak
Zadatak 2 Napravite stranicu za izlist svih kategorija Svaka kategorija neka bude poveznica na stranicu koja će ispisati članke unutar te kategorije sortirane po imenu
Pripremljeni upiti Dovoljan je jedan nezaštićeni parametar za ranjivost PDO nudi mogućnost pripremljenih upita gdje su parametri zamjenjeni rezerviranim mjestima Označava se pomoću? Konkretne se vrijednosti zadaju kasnije U slučaju da poslužitelj baze ne podržava pripremljene upite, PHP ih emulira $sql = <<<'SQL' SELECT id, ime FROM kategorija WHERE id =? SQL; $stmt = $db->prepare($sql); $stmt->execute([$_get['id']?? 0]); foreach ($stmt as $row) { var_dump($row); }
Pripremljeni upiti - imenovani parametri Parametri mogu biti i imenovani Isti je pripremljeni upit moguće više puta izvršiti $sql = <<<'SQL' INSERT INTO kategorija (ime) VALUE (:ime) SQL; $stmt = $db->prepare($sql); foreach (['JavaScript', 'Java'] as $ime) { $stmt->execute([':ime' => $ime]); }
Pripremljeni upiti - nastavak Metoda debugdumpparams nudi pomoć prilikom otklanjanja grešaka Ispisuje trenutne informacije o odredenom upitu $sql = <<<'SQL' SELECT id, ime FROM SQL; kategorija WHERE id = :id $stmt = $db->prepare($sql); $stmt->execute([':id' => 541]); $stmt->debugdumpparams();
Pripremljeni upiti - povezivanje parametara Jednostavnije izvršavanje upita Posebice kod parcijalnih skupova podataka Veća kontrola nad tipovima parametara Dvije metode u razredu PDOStatement bindvalue($stupac, $vrijednost - parametru pridjeljuje vrijednost bindparam($stupac, &$vrijednost - parametru pridjeljuje referencu na varijablu Koristi se vrijednost pohranjena u varijabli u trenutku izvršavanja upita Metodu execute potrebno pozvati bez parametara Neimenovani se parametri broje od jedan, a ne nula
Pripremljeni upiti - metoda bindvalue $sql = <<<'SQL' SELECT id, ime FROM kategorija WHERE id =? SQL; $stmt = $db->prepare($sql); $stmt->bindvalue( 1, $_GET['id']?? 0, PDO::PARAM_INT ); $stmt->execute(); foreach ($stmt as $row) { var_dump($row); }
Pripremljeni upiti - metoda bindparam $sql = <<<'SQL' SELECT id, ime FROM kategorija WHERE id = :id SQL; $stmt = $db->prepare($sql); $stmt->bindparam( ':id', $id, PDO::PARAM_INT ); foreach ([1, 3] as $id) { $stmt->execute(); } foreach ($stmt as $row) { var_dump($row); }
Pripremljeni upiti - opaska kod tipova podataka Tip podatka je bitan u odredenim situacijama Pretpostavljeno se vrijednost priprema kao niz znakova $sql = <<<'SQL' SELECT id, ime FROM kategorija LIMIT :id SQL; $stmt = $db->prepare($sql); $stmt->bindvalue(':id', 2, PDO::PARAM_INT); $stmt->execute(); foreach ($stmt as $row) { var_dump($row); }
Dohvaćanje rezultata - promjena načina dohvaćanja Moguće je za odredeni pripremljeni upit promjeniti način dohvaćanja rezultata $sql = 'SELECT id, ime FROM kategorija'; $stmt = $db->query($sql); $stmt->setfetchmode(pdo::fetch_obj); foreach ($stmt as $row) { var_dump($row); } PHP pretpostavlja vrijednost PDO::FETCH BOTH
Dohvaćanje rezultata - ručno dohvaćanje Iteriranje pomoću petlje foreach predstavlja jedan od načina dohvaćanja podataka Moguće je koristiti metode fetch*() za preciznije dohvaćanje rezultata $sql = 'SELECT id, ime FROM kategorija'; $stmt = $db->query($sql); while (false!== ($red = $stmt->fetch())) { var_dump($red); }
Dohvaćanje rezultata - povezivanje parametara Moguće je povezati kolone rezultata s varijablama preko referenci Analogno metodi bindparam za povezivanje parametara Način dohvaćanja mora biti PDO::FETCH BOUND $sql = 'SELECT id, ime FROM kategorija'; $stmt = $db->query($sql); $stmt->setfetchmode(pdo::fetch_bound); $stmt->bindcolumn('id', $id); $stmt->bindcolumn(2, $ime); foreach ($stmt as $row) { var_dump($id, $ime); }
Oslobadanje resursa upita Eksplicitno oslobadanje resursa upita Neke implementacije ne podržavaju izvršavanje novih upita bez da su svi redovi trenutnog upita dohvaćeni MySQL implementacija implicitno oslobada resurse Pomoću metode closecursor() $stmt = $db->prepare( 'SELECT id, ime FROM ); kategorija' $stmt->execute(); $stmt->closecursor(); $stmt->execute(); foreach ($stmt as $row) { var_dump($row); }
Zadatak 3 Pružite mogućnost dodavanja i uredivanja kategorija i članaka Za kategorije omogućite promjenu imenja postojećih kao i dodavanje novih kategorija Za članke omogućite promjenu naslova, sadržaja članka i kategorije u kojoj se nalazi, kao i dodavanje novih članaka Želite li omogućiti unos svih polja? Odabir kategorije neka bude padajući izbornik
Zatvarnje konekcije na poslužitelj Ne postoji eksplicitna metoda za zatvaranje Konekcija je aktivna za vrijeme postojanja objekta koji predstavlja konekciju Uništavanjem se objekta prekida i konekcija A to radimo kako? Na kraju skripte, izvodač će zatvoriti sve otvorene konekcije To nas, naravno, ne sprečava da čistimo iza sebe čim znamo da nam nešto ne treba :) Postoje i perzistentne konekcije Ne zatvaraju na kraju skripte PDO::ATTR PERSISTENT => true prilikom stvaranja konekcije