PHP Refresher: migration to PDO (PHP Data Objects) 1
Background PHP & MySQL Three MySQL APIs in PHP mysql, mysqli and PDO <?php // mysql $c = mysql_connect("example.com", "user", "password"); mysql_select_db("database"); $result = mysql_query("select 'Hello, dear MySQL user!' AS _message FROM DUAL"); $row = mysql_fetch_assoc($result); echo htmlentities($row['_message']); // mysqli $mysqli = new mysqli("example.com", "user", "password", "database"); $result = $mysqli >query("select 'Hello, dear MySQL user!' AS _message FROM DUAL"); $row = $result >fetch_assoc(); echo htmlentities($row['_message']);?> Note that both mysql and mysqli APIs are highly MySQL specific and not portable between different RDBMS! Copyright 2001-2013 The PHP Group hint: you should use the excellent manual! 2
PHP Data Objects - PDO <?php // PDO $pdo = new PDO('mysql:host=example.com;dbname=database', 'user', 'password'); $statement = $pdo >query("select 'Hello, dear MySQL user!' AS _message FROM DUAL"); $row = $statement >fetch(pdo::fetch_assoc); echo htmlentities($row['_message']);?> Note that PDO API is not MySQL specific! Copyright 2001-2013 The PHP Group hint: you should use the excellent manual! 3
Choosing an API Three MySQL APIs in PHP mysql, mysqli and PDO mysql API deprecated from PHP5.5 onwards maintenance only not for new projects commonly found in tutorials and textbooks! mysqli (improved) API object oriented and suitable for new projects most fully featured for MySQL RDBMS highly MySQL specific and not portable between different RDBMS PDO also object oriented and suitable for new projects consistent interface for accessing databases ease of switching databases (in theory, just change the connection parameters on a single line) drivers for CUBRID, MS SQL Server, Firebird/Interbase, IBM, Informix, MySQL, MS SQL Server, Oracle, ODBC and DB2, PostgreSQL, SQLite & 4D object oriented requiring OO features of PHP 5 core prepared statements and bind values are more secure ORM-like features (Object Relational Mapping) fields within record properties of object a common feature of MVC frameworks some employers insist on PDO http://www.php.net/manual/en/mysqlinfo.api.choosing.php hint: you should use the excellent manual! 4
PDO Concepts PDO PHP DB Driver Database Abstraction layer: consistent interface for accessing different databases Driver required for your chosen database Database technology of your choice 5
PDO - PHP Data Objects PDO extension defines a lightweight, consistent interface for accessing databases in PHP PDO provides a data-access abstraction layer, which means that, regardless of which database you're using, you use the same functions to issue queries and fetch data <?php try { $dbh = new PDO('mysql:host=localhost;dbname=test',$user,$pass); foreach($dbh >query('select * from FOO') as $row) { print_r($row); } $dbh = null; } catch (PDOException $e) { print "Error!: ". $e >getmessage(). "<br/>"; die(); }?> Copyright 2001-2013 The PHP Group hint: you should use the excellent manual! 6
PDO: Prepared statements, placeholders and bind values $stmt = $dbh >prepare("insert INTO REG (name, value) VALUES (:name, :value);"); $stmt >bindparam(':name', $name); $stmt >bindparam(':value', $value); // insert one row $name = 'one'; $value = 1; $stmt >execute(); The parameters to prepared statements don't need to be quoted; the PDO driver automatically handles this. $stmt = $dbh >prepare("insert INTO REG (name, value) VALUES (?,?);"); $stmt >bindparam(1, $name); $stmt >bindparam(2, $value); // insert another row with different values $name = 'two'; $value = 2; $stmt >execute(); If an application exclusively uses prepared statements, the developer can be sure that no SQL injection will occur. Note that the PDO functions are not MySQL specific! 7
MySQL functions lack prepared statements, placeholders and bind values // insert a third row with different values $name = 'three'; $value = 3; $res=mysql_query("insert INTO REG (name, value) VALUES ('$name', $value);"); Note that string variable must be enclosed in quotes for valid SQL. String interpolation is open to SQL injection attack. // insert a fourth row with different values $name = 'four'; $value = 4; $res=mysql_query( "INSERT INTO REG (name, value) VALUES ('". $name. "',". $value. ");" ); Note that these functions are very MySQL specific and less secure! Note that string variable has been enclosed in single quotes for valid SQL. String concatenation is open to SQL injection attack. 8
PDO: error handling $id = $_GET['id']; try { $dbh = new PDO("mysql:host=localhost;dbname=$database", $username, $password); $dbh >setattribute(pdo::attr_errmode, PDO::ERRMODE_EXCEPTION); // SQL errors will not be silent if (array_key_exists('name',$_get)) { // Update row (marker) with user data $name = $_GET['name']; $address = $_GET['address']; $type = $_GET['type']; $stmt=$dbh >prepare("update markers SET name=?,address=?,type=? WHERE id=?;"); $stmt >execute( array($name, $address, $type, $id) ); } else { // Update row (marker) with new position $lat = $_GET['lat']; $lng = $_GET['lng']; $stmt= $dbh >prepare( "UPDATE markers SET lat=?,lng=? WHERE id=?;" ); $stmt >execute( array($lat, $lng,$id) ); } $dbh = null; } catch (PDOException $e) { print "Error!: ". $e >getmessage(). "<br/>"; print "PHP Line Number: ". $e >getline(). "<br/>"; print "PHP File: ". $e >getfile(). "<br/>"; die(); } 9
require("phpsqlajax_dbinfo.php"); $dom = new DOMDocument("1.0"); $dom >formatoutput = true; $node = $dom >createelement("products"); $parnode = $dom >appendchild($node); try { PDO::FETCH_OBJ returns an anonymous object with property names that correspond to the column names returned in your result set (ORM-like behaviour ) $dbh = new PDO("mysql:host=localhost;dbname=$database", $username, $password); $dbh >setattribute(pdo::attr_errmode, PDO::ERRMODE_EXCEPTION); $stmt = $dbh >prepare("select catid, descr, stocklevel from products"); $stmt >execute(); while ( $result = $stmt >fetch(pdo::fetch_obj) ) { // ADD TO XML DOCUMENT NODE $node = $dom >createelement("product"); $newnode = $parnode >appendchild($node); $newnode >setattribute( "catid", $result >catid ); $newnode >setattribute( "descr", $result >descr ); $newnode >setattribute( "stocklevel", $result >stocklevel ); } $dbh = null; } catch (PDOException $e) { // as before } header("content Type: text/xml;"); echo $dom >savexml(); 10
PDO::FETCH_BOUND returns TRUE and assigns the values of the columns in your result set to the PHP variables to which they were bound with the PDOStatement::bindColumn() method <?php try { $stmt= $dbh >prepare( "select name,email,phone from users order by name asc;" ); $stmt >execute(); $stmt >bindcolumn( 'name', $name ); $stmt >bindcolumn( 'email', $email ); $stmt >bindcolumn( 'phone', $phone ); while ( $stmt >fetch(pdo::fetch_bound) ): echo <<<EOD <tr><form action='' method='post'> <td>name: <input type='text' value='$name' name='name'></td> <td>email: <input type='text' value='$email' name='email'></td> <td>phone: <input type='text' value='$phone' name='phone'></td> <td>name: <input type='submit' value='update' name='update'></td> </form></tr> EOD; <?php endwhile; } catch (PDOException $e) { $dbh = null; print "Error displaying existing user!: ". $e >getmessage(). "<br/>"; die(); }?> 11
Additional reading http://www.php.net/manual/en/mysqlinfo.api.choosing.php http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/ http://net.tutsplus.com/tutorials/php/pdo-vs-mysqli-which-should-you-use/ http://www.php.net/manual/en/book.pdo.php (you should use the excellent manual!) 12