PHP PDO

官方资料:http://www.php.net/pdo
http://cn2.php.net/manual/en/book.pdo.php

■PDO为何物?
PDO(PHP Data Object)扩展在PHP5中加入,PHP6中将默认识用PDO连接数据库,所有非PDO扩展将会在PHP6被从扩展中移除。该扩展提供PHP内置类 PDO来对数据库进行访问,不同数据库使用相同的方法名,解决数据库连接不统一的问题。
我是配置在windows下做开发用的。
■PDO的目标
•提供一种轻型、清晰、方便的 API
•统一各种不同 RDBMS 库的共有特性,但不排除更高级的特性。
•通过 PHP 脚本提供可选的较大程度的抽象/兼容性。
■PDO的特点:
•性能。PDO 从一开始就吸取了现有数据库扩展成功和失败的经验教训。因为 PDO 的代码是全新的,所以我们有机会重新开始设计性能,以利用 PHP 5 的最新特性。
•能力。PDO 旨在将常见的数据库功能作为基础提供,同时提供对于 RDBMS 独特功能的方便访问。
•简单。PDO 旨在使您能够轻松使用数据库。API 不会强行介入您的代码,同时会清楚地表明每个函数调用的过程。
• 运行时可扩展。PDO 扩展是模块化的,使您能够在运行时为您的数据库后端加载驱动程序,而不必重新编译或重新安装整个 PHP 程序。例 如,PDO_OCI 扩展会替代 PDO 扩展实现 Oracle 数据库 API。还有一些用于 MySQL、PostgreSQL、 ODBC 和 Firebird 的驱动程序,更多的驱动程序尚在开发。

■安装PDO
我这里是WINDOWS下开发用的PDO扩展,要是你要在Linux下安装配置,请到别的地方寻找。
版本要求:
php5.1以及以后版本的程序包里已经带了;
php5.0.x则要到pecl.php.net下载,放到你的扩展库,就是PHP所在的文件夹的ext文件夹下;
手册上说5.0之前的版本不能运行PDO扩展。

配置:
修改你的php.ini配置文件,使它支持pdo.(php.ini这个东西没有弄懂的话,先弄清楚,要修改调用你的phpinfo()函数所显示的那个php.ini)

extension=php_pdo.dll前面的分号去掉,分毫是php配置文件注释符号,这个扩展是必须的。
往下还有
;extension=php_pdo.dll
;extension=php_pdo_firebird.dll
;extension=php_pdo_informix.dll
;extension=php_pdo_mssql.dll
;extension=php_pdo_mysql.dll
;extension=php_pdo_oci.dll
;extension=php_pdo_oci8.dll
;extension=php_pdo_odbc.dll
;extension=php_pdo_pgsql.dll
;extension=php_pdo_sqlite.dll

各各扩展所对应的数据库是:

Driver name Supported databases
PDO_DBLIB FreeTDS / Microsoft SQL Server / Sybase
PDO_FIREBIRD Firebird/Interbase 6
PDO_INFORMIX IBM Informix Dynamic Server
PDO_MYSQL MySQL 3.x/4.x
PDO_OCI Oracle Call Interface
PDO_ODBC ODBC v3 (IBM DB2, unixODBC and win32 ODBC)
PDO_PGSQL PostgreSQL
PDO_SQLITE SQLite 3 and SQLite 2

你要使用哪种数据库,只要把相应的扩展前的注释符号”;”去掉就可以了。

■使用PDO
我这里假设你已经装好mysql了,要是没装的话,麻烦先想办法装上,我的是mysql5.0.22,黑夜路人用的是MySQL 4.0.26也可以用。
★数据库的连接:
我们通过下面的例子来分析PDO连接数据库,

<?php
$dbms='mysql';     //数据库类型 Oracle 用ODI,对于开发者来说,使用不同的数据库,只要改这个,不用记住那么多的函数了
$host='localhost'; //数据库主机名
$dbName='test';    //使用的数据库
$user='root';      //数据库连接用户名
$pass='';          //对应的密码
$dsn="$dbms:host=$host;dbname=$dbName";
//

try {
$dbh = new PDO($dsn, $user, $pass); //初始化一个PDO对象,就是创建了数据库连接对象$dbh
echo "连接成功<br/>";
/*你还可以进行一次搜索操作
    foreach ($dbh->query('SELECT * from FOO') as $row) {
print_r($row); //你可以用 echo($GLOBAL); 来看到这些值
}
*/
$dbh = null;
} catch (PDOException $e) {
die ("Error!: " . $e->getMessage() . "<br/>");
}
//默认这个不是长连接,如果需要数据库长连接,需要最后加一个参数:array(PDO::ATTR_PERSISTENT => true) 变成这样:
$db = new PDO($dsn, $user, $pass, array(PDO::ATTR_PERSISTENT => true));

?>

★数据库查询:
上面我们已经进行了一次查询,我们还可以使用如下的查询:

<?php
$db->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); //设置属性
$rs = $db->query("SELECT * FROM foo");
$rs->setFetchMode(PDO::FETCH_ASSOC);
$result_arr = $rs->fetchAll();
print_r($result_arr);
?>

以上因为用到setAttribute()方法,放上那两个参数,把字段名强制转换成大写。
下面列出多有PDO::setAttribute()的参数:

•PDO::ATTR_CASE: 强制列名变成一种格式,详细如下(第二个参数):
◦PDO::CASE_LOWER: 强制列名是小写.
◦PDO::CASE_NATURAL: 列名按照原始的方式
◦PDO::CASE_UPPER: 强制列名为大写.
•PDO::ATTR_ERRMODE: 错误提示.
◦PDO::ERRMODE_SILENT: 不显示错误信息,只显示错误码.
◦PDO::ERRMODE_WARNING: 显示警告错误.
◦PDO::ERRMODE_EXCEPTION: 抛出异常.
•PDO::ATTR_ORACLE_NULLS (不仅仅是ORACLE有效,别的数据库也有效): )指定数据库返回的NULL值在php中对应的数值。
◦PDO::NULL_NATURAL: 不变.
◦PDO::NULL_EMPTY_STRING: Empty string is converted to NULL.
◦PDO::NULL_TO_STRING: NULL is converted to an empty string.
•PDO::ATTR_STRINGIFY_FETCHES: Convert numeric values to strings when fetching. Requires bool.
•PDO::ATTR_STATEMENT_CLASS: Set user-supplied statement class derived from PDOStatement. Cannot be used with persistent PDO instances. Requires array(string classname, array(mixed constructor_args)).
•PDO::ATTR_AUTOCOMMIT (available in OCI, Firebird and MySQL): Whether to autocommit every single statement.
•PDO::MYSQL_ATTR_USE_BUFFERED_QUERY (available in MySQL): Use buffered queries.
例子中的$rs->setFetchMode(PDO::FETCH_ASSOC);是PDOStatement::setFetchMode(),对返回类型的声明。
有如下:
PDO::FETCH_ASSOC — 关联数组形式
PDO::FETCH_NUM   — 数字索引数组形式
PDO::FETCH_BOTH  — 两者数组形式都有,这是缺省的
PDO::FETCH_OBJ   — 按照对象的形式,类似于以前的 mysql_fetch_object()

更多返回类型声明(PDOStatement::方法名)看手册。

★插入,更新,删除数据,

$db->exec("DELETE FROM `xxxx_menu` where mid=43"); 

•简单的总结一下上面的操作:
查询操作主要是PDO::query()、PDO::exec()、PDO::prepare()。
PDO::query()主要是用于有记录结果返回的操作,特别是SELECT操作,
PDO::exec()主要是针对没有结果集合返回的操作,比如INSERT、UPDATE、DELETE等操作,它返回的结果是当前操作影响的列数。
PDO::prepare()主要是预处理操作,需要通过$rs->execute()来执行预处理里面的SQL语句,这个方法可以绑定参数,功能比较强大,不是本文能够简单说明白的,大家可以参考手册和其他文档。
获取结果集操作主要是:PDOStatement::fetchColumn()、PDOStatement::fetch()、PDOStatement::fetchALL()。
PDOStatement::fetchColumn() 是获取结果指定第一条记录的某个字段,缺省是第一个字段。
PDOStatement::fetch() 是用来获取一条记录,
PDOStatement::fetchAll()是获取所有记录集到一个中,获取结果可以通过PDOStatement::setFetchMode来设置需要结果集合的类型。
另外有两个周边的操作,一个是PDO::lastInsertId()和PDOStatement::rowCount()。PDO::lastInsertId()是返回上次插入操作,主键列类型是自增的最后的自增ID。
PDOStatement::rowCount()主要是用于PDO::query()和PDO::prepare()进行DELETE、INSERT、UPDATE操作影响的结果集,对PDO::exec()方法和SELECT操作无效。

★事务和自动提交

至 此,您已经通过 PDO 连接到了 mysql,在发出查询之前,您应该理解 PDO 是如何管理事务的。如果之前没有接触过事务,那么首先要知道事务 的 4 个特征:原子性(Atomicity)、一致性(Consistency)、独立性(Isolation)和持久性(Durability), 即 ACID。用外行人的话说,对于在一个事务中执行的任何工作,即使它是分阶段执行的,也一定可以保证该工作会安全地应用于数据库,并且在工作被提交 时,不 会受到来自其他连接的影响。事务性工作可以根据请求自动撤销(假设您还没有提交它),这使得脚本中的错误处理变得更加容易。
事务通常是通过把一批更改积蓄起来、使之同时生效而实现的。这样做的好处是可以大大提高这些更新的效率。换句话说,事务可以使脚本更快,而且可能更健壮(不过需要正确地使用事务才能获得这样的好处)。
不 幸的是,并不是每种数据库都支持事务(Mysql5支持事务,mysql4我不知道),所以当第一次打开连接时,PDO 需要在所谓的“自动提交 (auto-commit)”模式下运行。自动提交模式意味着,如果数据库支持事务,那么您所运行的每一个查询都有它自己的隐式事 务,如果数据库不支持 事务,每个查询就没有这样的事务。如果您需要一个事务,那么必须使用 PDO::beginTransaction() 方法来启动一个事务。如果底层驱 动程序不支持事务,那么将会抛出一个 PDOException(无论错误处理设置是怎样的:这总是一个严重错误状态)。在一个事务中,可以使 用 PDO::commit() 或 PDO::rollBack() 来结束该事务,这取决于事务中运行的代码是否成功。
当脚本结束时,或者当 一个连接即将被关闭时,如果有一个未完成的事务,那么 PDO 将自动回滚该事务。这是一种安全措施,有助于避免在脚本非正常结束时出现不一致的情 况 —— 如果没有显式地提交事务,那么假设有某个地方会出现不一致,所以要执行回滚,以保证数据的安全性。

//例子来自http://www.ibm.com/developerworks/cn/db2/library/techarticles/dm-0505furlong/index.html
try {
$dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2',
array(PDO_ATTR_PERSISTENT => true));
echo "Connected\n";
$dbh->setAttribute(PDO_ATTR_ERRMODE, PDO_ERRMODE_EXCEPTION);
$dbh->beginTransaction();
$dbh->exec("insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')");
$dbh->exec("insert into salarychange (id, amount, changedate)
values (23, 50000, NOW())");
$dbh->commit();

} catch (Exception $e) {
$dbh->rollBack();
echo "Failed: " . $e->getMessage();
}

在 上面的示例中,假设我们为一个新雇员创建一组条目,这个雇员有一个 ID 号,即 23。除了输入这个人的基本数据外,我们还需要记录雇员的薪水。两个更 新分别完成起来很简单,但通过将这两个更新包括在 beginTransaction() 和 commit() 调用中,就可以保证在更改完成之前,其 他人无法看到更改。如果发生了错误,catch 块可以回滚事务开始以来发生的所有更改,并打印出一条错误消息。

并不是一定要在事务中作出更新。您也可以发出复杂的查询来提取数据,还可以使用那种信息构建更多的更新和查询。当事务在活动时,可以保证其他人在工作进行当中无法作出更改。事实上,这不是 100% 的正确,但如果您之前没有听说过事务的话,这样介绍也未尝不可。
★预处理语句和存储过程
很多更成熟的数据库都支持预处理语句的概念。什么是预处理语句?您可以把预处理语句看作您想要运行的 SQL 的一种编译过的模板,它可以使用变量参数进行定制。预处理语句可以带来两大好处:
• 查询只需解析(或准备)一次,但是可以用相同或不同的参数执行多次。当查询准备好后,数据库将分析、编译和优化执行该查询的计划。对于复杂的查 询,这个 过程要花比较长的时间,如果您需要以不同参数多次重复相同的查询,那么该过程将大大降低应用程序的速度。通过使用预处理语句,可以避免重复分析/ 编译 /优化周期。简言之,预处理语句使用更少的资源,因而运行得更快。
•提供给预处理语句的参数不需要用引号括起来,驱动程序会处理这些。如果应用程序独占地使用预处理语句,那么可以确保没有 SQL 入侵发生。(然而,如果您仍然将查询的其他部分建立在不受信任的输入之上,那么就仍然存在风险)。
预处理语句是如此有用,以致 PDO 实际上打破了在目标 4 中设下的规则:如果驱动程序不支持预处理语句,那么 PDO 将仿真预处理语句。

实例:PDO的应用例子:

<?php
$dbms='mysql'; //数据库类型 Oracle 用ODI,对于开发者来说,使用不同的数据库,只要改这个,不用记住那么多的函数了

$host='localhost'; //数据库主机名
$dbName='test'; //使用的数据库
$user='root'; //数据库连接用户名
$pass=''; //对应的密码
$dsn="$dbms:host=$host;dbname=$dbName";
class db extends PDO {
public function __construct(){
try {
parent::__construct("$GLOBALS[dsn]", $GLOBALS['user'], $GLOBALS['pass']);
} catch (PDOException $e) {
die("Error: " . $e->__toString() . "<br/>");
}
}

public final function query($sql){
try {
return parent::query($this->setString($sql));
}catch (PDOException $e){
die("Error: " . $e->__toString() . "<br/>");
}
}

private final function setString($sql){
echo "我要处理一下$sql";
return $sql;
}
}

$db=new db();
$db->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
foreach ($db->query('SELECT * from xxxx_menu') as $row) {
print_r($row);
}
$db->exec('DELETE FROM  `xxxx_menu` where mid=43');
?>

Introduction to PHP PDO
http://www.phpro.org/tutorials/Introduction-to-PHP-PDO.html
Introduction to PHP PDO
(PHP Data Objects)
Contents
What is PDO
What Databases does PDO support
Where do I begin?
Connect to a Database
Connect to PgSQL
Connect to SQLite
Connect to MySQL
Connect to Firebird
Connect to Informix
Connect to Oracle
Connect to ODBC
Connect to DBLIB
Connect to IBM
There is no five
Close a Database Connection
Query a Database
INSERT
SELECT
UPDATE
Fetch Modes
FETCH ASSOC
FETCH NUM
FETCH BOTH
FETCH OBJECT
FETCH LAZY
FETCH CLASS
FETCH INTO
Error Handling
Prepared Statements
Transactions
Get Last Insert ID
Global Instance
Conclusions
What is PDO.
PDO is a PHP extension to formalise PHP’s database connections by creating a uniform interface. This allows developers to create code which is portable across many databases and platforms. PDO is _not_ just another abstraction layer like PearDB although PearDB may use PDO as a backend. Those of you familiar with Perls DBI may find the syntax disturbingly familiar.
Note: Your must read the section on Error Handling to benifit from this tutorial
During this tutorial we will be using a database called animals, which, as you might have guessed, is a database of animals, genius! The animals table is described here.

CREATE TABLE animals ( animal_id MEDIUMINT(8) NOT NULL AUTO_INCREMENT PRIMARY KEY,
animal_type VARCHAR(25) NOT NULL,
animal_name VARCHAR(25) NOT NULL
) ENGINE = MYISAM ;
INSERT INTO `animals` (`animal_id`, `animal_type`, `animal_name`) VALUES
(1, 'kookaburra', 'bruce'),
(2, 'emu', 'bruce'),
(3, 'goanna', 'bruce'),
(4, 'dingo', 'bruce'),
(5, 'kangaroo', 'bruce'),
(6, 'wallaby', 'bruce'),
(7, 'wombat', 'bruce'),
(8, 'koala', 'bruce');

What databases does PDO support?
PDO supports many of the popular databases as seen on the list below.
DBLIB: FreeTDS / Microsoft SQL Server / Sybase
Firebird (http://firebird.sourceforge.net/): Firebird/Interbase 6 
IBM (IBM DB2)
INFORMIX – IBM Informix Dynamic Server
MYSQL (http://www.mysql.com/): MySQL 3.x/4.0 
OCI (http://www.oracle.com): Oracle Call Interface 
ODBC: ODBC v3 (IBM DB2 and unixODBC)
PGSQL (http://www.postgresql.org/): PostgreSQL 
SQLITE (http://sqlite.org/): SQLite 3.x 
To see if the PDO driver is available for your database, check phpinfo() and you should have a section named PDO and another pdo_mysql or pdo_sqlite depending on your choice of database. You may also check the available drivers with the static method PDO::getAvailableDrivers().

<?php
foreach(PDO::getAvailableDrivers() as $driver)
    {
    echo $driver.'';
    }
?>

To enable PDO simply configure –enable-pdo and –with-pdo_sqlite –with_pdo_mysql or whatever database needs supporting by PDO.
Windows users will need to un-comment the appropriate line in php.ini and restart the web server.
Where do I begin?
If you are reading this you are more than likely to have connected to a database using PHP before using a database specific function such as mysql_connect() or pg_connect or, for the truely evolved coder, SQLite. To use PDO with your database you need to have the correct PDO driver installed for it. For the SQLite PDO driver you need to configure PHP –with-pdo-sqlite. If you are using a RPM based system there are pdo-sqlite.rpm’s available. Before we go any further, lets connect to a database and see what all the fuss is about.
Connect to a database
Every interaction with a database begins with a connection. Regardless of the database you use, you must connect first and establish a database handler. After connecting you your database of choice, much of the PDO methods are similar. This is why PDO is such a powerful and useful tool for PHP. Here we show how to connect to various databases and establish a database handler object that we can use for further interaction with the database.
Connect with PgSQL
As mentioned above, you may have previously tried to connect to a PgSQL database using pg_connect. Here we connect with PDO.

<?php
try {
    $db = new PDO("pgsql:dbname=pdo;host=localhost", "username", "password" );
    echo "PDO connection object created";
    }
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

So that was a bit of a no-brainer to start with, we see the use of “new” to create the PDO object followed by the database type username and password. This should be familiar to most people who have connected to a database before using traditional methods.. As we have used try{} and catch(){} we see upon failure that an exception is thrown with the error message “could not find driver”. This tells us the PDO_PGSQL driver is not present and needs to be loaded. As noted, an exception is thrown. PDO can handle errors in several ways, more on this later.
How did it connect to the database?
The database connection is handled internally by PDO’s __construct() and this represents our database connection.
Lets see what happens if we try to connect to database as we did above without catching the exception and see what happens..

$db= new PDO("pgsql:dbname=no_database;host=localhost", "username", "password" );

From the above snippet you will get a result something like this below
Fatal error: Uncaught exception ‘PDOException’ with message ‘could not find driver’ in /www/pdo.php:2 Stack trace: #0 /www/pdo.php(2): PDO->__construct(‘pgsql:dbname=pd…’, ‘username’, ‘password’) #1 {main} thrown in /www/pdo.php on line 2
This is the default behaviour when an exception is not caught, a backtrace is generated and the script is terminated. As you can see, all the information is dumped including the file path and the database username and password. It is the responsibility of the coder to catch exceptions or to deal with the errors using set_exception_handler() function to prevent this happening. More about handling errors and exceptions later.
Connect to SQLite
When PDO is used with SQLite, database creation becomes even easier. Simply specify the path to the database file and it will be loaded. If the database file does not exist, PDO will attempt to create it. Lets see how we go with the same code but change the database to SQLite.

<?php
try {
    /*** connect to SQLite database ***/
    $dbh = new PDO("sqlite:/path/to/database.sdb");
    }
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Because the database path does not exist and cannot be created, an exception is thrown, the exception is caught in the catch block and the error message is displayed with $e->Message(). Now that we know how to create a database, we can create tables and INSERT some data.
Another feature of SQLite is the ability to create tables in memory. This can be amazingly helpful if you wish to create tempory databases or tables or even for development code.

<?php
try {
    /*** connect to SQLite database ***/
    $db = new PDO("sqlite::memory");
    /*** a little message to say we did it ***/
    echo 'database created in memory';
    }
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

We see above that a database is created in memory and a message is displayed to let us know. If the creation of the database failed, a PDO exception would be thrown and the script terminated at that point, passing control to the catch block.
Connect to MySQL
MySQL is the choice of many web developers and will be used as the database of choice for much of this tutorial. Here we see how to connect to a MySQL database.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=mysql", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    }
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Connect to Firebird
Often used by developers using windows, Firebird is a good database and connection is just as simple as the examples above.

<?php
try {
    $dbh = new PDO("firebird:dbname=localhost:C:\Programs\Firebird\DATABASE.FDB", "SYSDBA", "masterkey");
    }
catch (PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Connect to Informix
Informix is popular with many windows users also, this example shows how to connect to an informix database cataloged as InformixDB in odbc.ini:

<?php
try {
    $dbh = new PDO("informix:DSN=InformixDB", "username", "password");
    }
catch (PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Connect to Oracle
The Oracle database is used by many ‘enterprise’ companies but these days there are sleeker options. Lets see a simple connection to Oracle

<?php
try {
    $dbh = new PDO("OCI:", "username", "password")
    }
catch (PDOException $e)
    {
    echo $e->getMessage();
    }
?>

This works fine for a simple Oracle connection. The oracle driver may take two optional parameters, The database name, and the character set. To connect with a database name of “accounts” and a charset of UTF-8 the following code should be used.

<?php
try {
    $dbh = new PDO("OCI:dbname=accounts;charset=UTF-8", "username", "password");
    }
catch (PDOException $e)
    {     echo $e->getMessage();     } ?>

Connect to ODBC
There are many connections ODBC can create, here we show how to connect to a MS Access database named accounts. The specified path is c:\\accounts.mdb.

<?php
try {
    $dbh = new PDO("odbc:Driver={Microsoft Access Driver (*.mdb)};Dbq=C:\accounts.mdb;Uid=Admin");
    }
catch (PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Connect to DBLIB
Once again a Windows specific database, DBLIB can be used as follows

<?php
try {
    $hostname = "localhost";
    $port     = 10060;
    $dbname   = "my_database";
    $username = "username";
    $password = "password";
    $dbh = new PDO ("dblib:host=$hostname:$port;dbname=$dbname","$username","$password");
    }
catch (PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Connect to IBM
This example shows connecting to an IBM DB2 database named accounts

<?php
try {
    $db = new PDO("ibm:DRIVER={IBM DB2 ODBC DRIVER};DATABASE=accounts; HOSTNAME=1.2.3,4;PORT=56789;PROTOCOL=TCPIP;", "username", "password");
    }
catch (PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Close a Database Connection
Up to this point we have seen how to connect to a database using PDO. But of course, we also need to disconnect when we have finished. To close the connection the object needs to be destroyed so that no reference to it remains. This is normally done at the end of a script where PHP will automatically close the connection. However, the connection may be close implicitly by assigning the value of null to the object as seen below.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=mysql", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** close the database connection ***/
    $dbh = null;
    }
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

If the database connection fails, the code to assign a null value is never called as the exception throws control to the catch block.
PDO Query
Now that we can open and close a connection to the database with PDO, we can make use of it for what databases are made for, storing and retrieving information. The simplest form of query is the PDO query method. As the name suggests, this is used to perform database queries. Before we begin to query a database, lets create a small database with a table for animals. This will be a MySQL database for use throughout much of this tutorial. Remember, because PDO provides a common set of tools for databases, once we have the correct connection, the rest of the code is the same, regardless of the database you choose. When using PDO to query a database, the function used to do so depends on the statement you wish to send to the database. Below we will see three queries on how to INSERT, SELECT and UPDATE.
INSERT
To gather information from a database, we first need to put some info into it. We use the same code from above to connect and disconnect from the database and the INSERT query is accomplished using the PDO::exec method.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** INSERT data ***/
    $count = $dbh->exec("INSERT INTO animals(animal_type, animal_name) VALUES ('kiwi', 'troy')");
    /*** echo the number of affected rows ***/
    echo $count;
    /*** close the database connection ***/
    $dbh = null;
    }
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

The output of the script above will look like this:

Connected to database
1

This shows us that we connected successfully to the database and then we have displayed the number of affected rows. PDO::exec returns the number of affected rows if successful, or zero (0) if no rows are affected. This may cause issues if you are checking for a boolean value and why it is recommended using === when to check for type also, as zero (0) may evaluate to boolean FALSE.
The PDO::exec method should be used for SQL statements that do not return a result set. We could use this same method to INSERT many more animals to our database, but a more effecient method would be to use a transaction. This is covered in the section on Transactions.
SELECT
Unlike PDO::exec the PDO::query method returns a result set, that is, a group of information from the database in the form of a PDOStatement object. Our database should look a little like the example in the What is PDO section. Using this we can SELECT information.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** The SQL SELECT statement ***/
    $sql = "SELECT * FROM animals";
    foreach ($dbh->query($sql) as $row)
        {
        print $row['animal_type'] .' - '. $row['animal_name'] . '';
        }
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

From the script above, we can expect the results to look like this:

Connected to database
emu - bruce
funnel web - bruce
lizard - bruce
dingo - bruce
kangaroo - bruce
wallaby - bruce
wombat - bruce
koala - bruce
kiwi - troy

You will have noticed that we can iterate over the result set directly with foreach. This is because internally the PDO statement implements the SPL traversble iterator, thus giving all the benifits of using SPL. For more on SPL refer to the Introduction to SPL page. The greatest benifit of this is that SPL iterators know only one element at a time and thus large result sets become manageable without hogging memory.
UPDATE
To update a field in a database with PDO we once again use the PDO::exec method in the same manner as we did with the INSERT

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** INSERT data ***/
    $count = $dbh->exec("UPDATE animals SET animal_name='bruce' WHERE animal_name='troy'");
    /*** echo the number of affected rows ***/
    echo $count;
    /*** close the database connection ***/
    $dbh = null;
    }
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Once again we see that the connection is made to the database and one row is affected as now the kiwi has become a true Australian like the rest of the creatures. PDO::exec should be used for all database queries where no result set is required.
FETCH Modes
The section above showed how using PDO::query we can fetch information from the database. The PDO::query method returns a PDOStatement object that can be utilized in much the same was as mysql_fetch_object() or pg_fetch_object(). Of course there are times when an numerical index is needed or an associative index. PDO::query provides for this also by allowing the coder to set the fetch mode for via the PDOStatement object or via PDOStatement::setFetchMode().
FETCH ASSOC
To fetch an associative array from our results the constant PDO::FETCH_ASSOC is used and returns the column names as indexes or keys of the resulting array.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** The SQL SELECT statement ***/
    $sql = "SELECT * FROM animals";
    /*** fetch into an PDOStatement object ***/
    $stmt = $dbh->query($sql);
    /*** echo number of columns ***/
    $result = $stmt->fetch(PDO::FETCH_ASSOC);
    /*** loop over the object directly ***/
    foreach($result as $key=>$val)
    {
    echo $key.' - '.$val.'';
    }
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

The above code will give a result like this:

Connected to database
animal_id - 1
animal_type - emu
animal_name - bruce

PDO has returned the results as a PDOStatement object that we can iterate over directly. The resulting indexes are the names of the fields within the animals database.
FETCH NUM
Like PDO::FETCH_ASSOC, the PDO::FETCH_NUM produces a numerical index of the result set rather than the field names.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** The SQL SELECT statement ***/
    $sql = "SELECT * FROM animals";
    /*** fetch into an PDOStatement object ***/
    $stmt = $dbh->query($sql);
    /*** echo number of columns ***/
    $result = $stmt->fetch(PDO::FETCH_NUM);
    /*** loop over the object directly ***/
    foreach($result as $key=>$val)
    {
    echo $key.' - '.$val.'';
    }
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

The above code will give a result like this:

Connected to database
0 - 1
1 - emu
2 - bruce

As you can see above the indexes are now numeric in the result set
FETCH BOTH
There may be times you need to fetch both numerical and associative indexes. PDO::FETCH_BOTH produces a numerical and associative index of the result set so you can use either, or both.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** The SQL SELECT statement ***/
    $sql = "SELECT * FROM animals";
    /*** fetch into an PDOStatement object ***/
    $stmt = $dbh->query($sql);
    /*** echo number of columns ***/
    $result = $stmt->fetch(PDO::FETCH_BOTH);
    /*** loop over the object directly ***/
    foreach($result as $key=>$val)
    {
    echo $key.' - '.$val.'';
    }
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Now we see the results have included both indexes.

Connected to database
animal_id - 1
0 - 1
animal_type - emu
1 - emu
animal_name - bruce
2 - bruce

FETCH OBJECT
This little gem takes the result set and returns it as an anonymous object or stdClass and maps the field names from the database as object properties with the values the values of stored in the database.

/*** mysql hostname ***/
$hostname = ‘localhost’;
/*** mysql username ***/
$username = ‘username’;
/*** mysql password ***/
$password = ‘password’;
try {
$dbh = new PDO(“mysql:host=$hostname;dbname=animals”, $username, $password);
/*** echo a message saying we have connected ***/
echo ‘Connected to database’;
/*** The SQL SELECT statement ***/
$sql = “SELECT * FROM animals”;
/*** fetch into an PDOStatement object ***/
$stmt = $dbh->query($sql);
/*** echo number of columns ***/
$obj = $stmt->fetch(PDO::FETCH_OBJ);
/*** loop over the object directly ***/
echo $obj->animal_id.”;
echo $obj->animal_type.”;
echo $obj->animal_name;
/*** close the database connection ***/
$dbh = null;
}
catch(PDOException $e)
{
echo $e->getMessage();
}
?>

The above code gives the results like this:

Connected to database
1
emu
bruce

The use of the field names as class properties makes integrating results into an Object Oriented envioronment simple.
FETCH LAZY
PDO::FETCH_LAZY is odd as it combines PDO::FETCH_BOTH and PDO::FETCH_OBJ. I am unsure why you would want to do this, but it must have been important enough for somebody to create it. The code below is that of PDO::FETCH_BOTH and is reproduced here for examples sake.

class animals{
public $animal_id;
public $animal_type;
public $animal_name;
/***
 *
 * @capitalize first words
 *
 * @access public
 *
 * @return string
 *
 */
public function capitalizeType(){
 return ucwords($this->animal_type);
}
} /*** end of class ***/
/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** The SQL SELECT statement ***/
    $sql = "SELECT * FROM animals";
    /*** fetch into an PDOStatement object ***/
    $stmt = $dbh->query($sql);
    /*** fetch into the animals class ***/
    $obj = $stmt->fetchALL(PDO::FETCH_CLASS, 'animals');
    /*** loop of the object directly ***/
    foreach($obj as $animals)
        {
        /*** call the capitalizeType method ***/
        echo $animals->capitalizeType().'';
        }
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

The above code will give a result the same as that of PDO::FETCH_BOTH. Genius!
FETCH CLASS
PDO::FETCH_CLASS instantiates a new instance of the specified class. The field names are mapped to properties (variables) within the class called. This saves quite a bit of code and speed is enhanced as the mappings are dealt with internally.

class animals{
public $animal_id;
public $animal_type;
public $animal_name;
/***
 *
 * @capitalize first words
 *
 * @access public
 *
 * @return string
 *
 */
public function capitalizeType(){
 return ucwords($this->animal_type);
}
} /*** end of class ***/
/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** The SQL SELECT statement ***/
    $sql = "SELECT * FROM animals";
    /*** fetch into an PDOStatement object ***/
    $stmt = $dbh->query($sql);
    /*** fetch into the animals class ***/
    $obj = $stmt->fetchALL(PDO::FETCH_CLASS, 'animals');
    /*** loop of the object directly ***/
    foreach($obj as $animals)
        {
        /*** call the capitalizeType method ***/
        echo $animals->capitalizeType().'';
        }
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

The code above produces a list of animal types, with the first letter capitalized like this:

Connected to database
Emu
Funnel Web
Lizard
Dingo
Kangaroo
Wallaby
Wombat
Koala
Kiwi

The PDO::FETCH_CLASS constant has fetched the results directly into the animals class where we were able to directly manipulate the results, nifty.
PDO provides an alternative to PDO::fetch and PDO::FETCH_CLASS. PDOStatement::fetchObject() will bundle them together to give the same result as shown here.

class animals{
public $animal_id;
public $animal_type;
public $animal_name;
/***
 *
 * @capitalize first words
 *
 * @access public
 *
 * @return string
 *
 */
public function capitalizeType(){
 return ucwords($this->animal_type);
}
} /*** end of class ***/
/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** The SQL SELECT statement ***/
    $sql = "SELECT * FROM animals";
    /*** fetch into an PDOStatement object ***/
    $stmt = $dbh->query($sql);
    /*** fetch into the animals class ***/
    $animals = $stmt->fetchObject('animals');
    /*** echo the class properties ***/
    echo $animals->animal_id.'';
    echo $animals->capitalizeType().'';
    echo $animals->animal_name;
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

The above code gives the results like this:

Connected to database
1
Emu
bruce

Note that we have called the animals::capitalizeType() method to show that we are in fact working with an instance of the animals class. PDO::fetchObject() will also work as a substitute for PDO::FETCH_OBJ.
FETCH INTO
The PDO::FETCH_INTO constant allows us to fetch the data into an existing instance of a class. Like PDO::FETCH_CLASS the field names are mapped to the class properties. With this in mind, we should be able to replicate the behaviour of PDO::FETCH_CLASS by instantiating the new object when setting the fetch mode. In this instance, the fetch mode is set using PDO::setFetchMode() method.

class animals{
public $animal_id;
public $animal_type;
public $animal_name;
public function capitalizeType(){
 return ucwords($this->animal_type);
}
} /*** end of class ***/
/*** instantiate a new animals instance ***/
$animals = new animals;
$animals->animal_id = 10;
$animals->animal_type = 'crocodile';
$animals->animal_name = 'bruce';
/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** The SQL SELECT statement ***/
    $sql = "SELECT * FROM animals";
    /*** fetch into an PDOStatement object ***/
    $stmt = $dbh->query($sql);
    /*** set the fetch mode with PDO::setFetchMode() ***/
    $stmt->setFetchMode(PDO::FETCH_INTO, new animals);
    /*** loop over the PDOStatement directly ***/
    foreach($stmt as $animals)
    {
    echo $animals->capitalizeType().'';
    }
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Once again, the above code produces a list of animal types, with the first letter capitalized like this:

Connected to database
Emu
Funnel Web
Lizard
Dingo
Kangaroo
Wallaby
Wombat
Koala
Kiwi

Error Handling
PDO error handling is comes in several flavours. Previously in this tutorial we have have only used the simplest of try{} catch(){} blocks to catch an error in the database connection, but what of other errors? perhaps a field name does not exist? Lets see how we go with a simple error with the previous code.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** The SQL SELECT statement with incorrect fieldname ***/
    $sql = "SELECT username FROM animals";
    foreach ($dbh->query($sql) as $row)
        {
        print $row['animal_type'] .' - '. $row['animal_name'] . '';
        }
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

The above script will produce an error such as this:
Connected to database
Warning: Invalid argument supplied for foreach() in /www/pdo.php on line 18 

This is because there is no error handling. The SELECT statement has a field name ‘username’ which does not exist and an error is generated by the database. The only default error handling is done with the initial connection. Unless we deal with the error, we have a problem with displaying full path to the world. To deal with this we need to set an attribute to the type of error handling we wish to utilize. The types of error handling are
Exception
Warning
Silent
Lets begin with exception as we have the try{} catch(){} blocks in place already.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** set the error reporting attribute ***/
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    /*** The SQL SELECT statement ***/
    $sql = "SELECT username FROM animals";
    foreach ($dbh->query($sql) as $row)
        {
        print $row['animal_type'] .' - '. $row['animal_name'] . '';
        }
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Now with the error mode set to Exception the error generated looks like this:
Connected to database
SQLSTATE[42S22]: Column not found: 1054 Unknown column ‘username’ in ‘field list’

Normally we would not show this type of error to the end user, and the exception would be handled perhaps with message saying No Results Found or something vague, but this does show how we can set the error mode as we wish. To set the error mode to Warning should look easy from here.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** set the error reporting attribute ***/
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
    /*** The SQL SELECT statement ***/
    $sql = "SELECT username FROM animals";
    foreach ($dbh->query($sql) as $row)
        {
        print $row['animal_type'] .' - '. $row['animal_name'] . '';
        }
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Now a different error is displayed.
Connected to database
Warning: PDO::query() [function.PDO-query]: SQLSTATE[42S22]: Column not found: 1054 Unknown column ‘username’ in ‘field list’ in /www/pdo.php on line 21
Warning: Invalid argument supplied for foreach() in /www/pdo.php on line 21 

Here and E_WARNING has been generated and if display_errors is on the error would be seen by an end user. It is hoped that if you are in a production environment this is not the case.
Lastly, there is the Silent mode. As the name suggests, this mode silences the errors so no output is sent from the error. However, it does not stop the code at the point of error and any further errors are still sent.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    }
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
/*** an invalide fieldname ***/
$sql = "SELECT username FROM animals";
/*** run the query ***/
$result = $dbh->query($sql);
/*** show the error code ***/
echo $dbh->errorCode();
?>

Now we see the script above produces the following output.
Connected to database
Warning: Invalid argument supplied for foreach() in /www/pdo.php on line 21 
As you can see, the error has been silenced, but the following error has not been attended to, and would need further checks to ensure the value passed to the foreach is a valid arguement.
As we saw with the exception code, the SQLSTATE code was part of the error message. This error code is also available with the PDO::errorCode() method.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    }
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
/*** an invalid table name ***/
$sql = "SELECT animal_id FROM users";
/*** run the query ***/
$result = $dbh->query($sql);
/*** show the error info ***/
foreach($dbh->errorInfo() as $error)
    {
    echo $error.'';
    }
?>

The code above shows the error code relevant to the SQLSTATE. This is a five character string as defined by the ANSI SQL standard.

Connected to database
42S22

Further information about an error may be gained from the PDO::errorInfo() method. This returns an array containing the SQLSTATE, the error code, and the error message.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    }
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
/*** an invalid table name ***/
$sql = "SELECT animal_id FROM users";
/*** run the query ***/
$result = $dbh->query($sql);
/*** show the error info ***/
foreach($dbh->errorInfo() as $error)
    {
    echo $error.'';
    }
?>

With this code, the error information looks like this:

Connected to database
42S02
1146

Table ‘animals.users’ doesn’t exist
If there is no error, the SQLSTATE will be the only value shown, with a value of 00000.
Prepared statements
What is a prepared statement? A prepared statement is a pre-compiled SQL statement that accepts zero or more named parameters. Ok, so thats my attempt at describing what it is, if you have a better description, let us know.
The SQL is prepared for execution. This is especially useful when using the same statement or query multiple times with different parameters, or field values. The boost in speed is hidden from userland code as the PDO driver allows client and server side caching of the query and meta data. It also helps prevent SQL injection by calling the PDO::quote() method internally.
PDO accepts two kinds of parameter markers.
named – :name
question mark – ?
You must choose one or the other, they cannot be mixed.
Lets dive in and have a look at how PDO::prepare and PDOStatement::execute work together.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** set the error reporting attribute ***/
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    /*** some variables ***/
    $animal_id = 6;
    $animal_name = 'bruce';
    /*** prepare the SQL statement ***/
    $stmt = $dbh->prepare("SELECT * FROM animals WHERE animal_id = :animal_id AND animal_name = :animal_name");
    /*** bind the paramaters ***/
    $stmt->bindParam(':animal_id', $animal_id, PDO::PARAM_INT);
    $stmt->bindParam(':animal_name', $animal_name, PDO::PARAM_STR, 5);
    /*** execute the prepared statement ***/
    $stmt->execute();
    /*** fetch the results ***/
    $result = $stmt->fetchAll();
    /*** loop of the results ***/
    foreach($result as $row)
        {
        echo $row['animal_id'].'';
        echo $row['animal_type'].'';
        echo $row['animal_name'];
        }
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

The above code will produce the following:

Connected to database
6
wallaby
bruce

Errr, what was that?
What is this name = :variable business
What we have done is bind the variable named $animal_id and $animal_name to the statement. Remember this as many find it difficult to grasp. You are not binding the value of the variable, you are binding the variable itself. Lets change the value of the animal_id after the variable is bound and see what happens..

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** set the error reporting attribute ***/
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    /*** some variables ***/
    $animal_id = 6;
    $animal_name = 'bruce';
    /*** prepare the SQL statement ***/
    $stmt = $dbh->prepare("SELECT * FROM animals WHERE animal_id = :animal_id AND animal_name = :animal_name");
    /*** bind the paramaters ***/
    $stmt->bindParam(':animal_id', $animal_id, PDO::PARAM_INT);
    $stmt->bindParam(':animal_name', $animal_name, PDO::PARAM_STR, 5);
    /*** reassign the animal_id ***/
    $animal_id = 3;
    /*** execute the prepared statement ***/
    $stmt->execute();
    /*** loop over the results ***/
    while($row = $stmt->fetch())
        {
        echo $row['animal_id'].'';
        echo $row['animal_type'].'';
        echo $row['animal_name'];
        }
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Now see the results have changed

Connected to database
3
lizard
bruce

Because we have bound the variable $animal_id to the $stmt object any change to the value of that varible will be reflected in the statement. This format can be used for both SELECT and INSERT statements. But this is a bit cumbersome for a single query and the above PDO query could have done the job equally as well, so lets run the query multiple times. Ssimply by changing the animal_id and animal_name variables we can run the query over and over without re-writing as it is already ‘prepared’.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** set the error reporting attribute ***/
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    /*** some variables ***/
    $animal_id = 6;
    $animal_name = 'bruce';
    /*** prepare the SQL statement ***/
    $stmt = $dbh->prepare("SELECT * FROM animals WHERE animal_id = :animal_id AND animal_name = :animal_name");
    /*** bind the paramaters ***/
    $stmt->bindParam(':animal_id', $animal_id, PDO::PARAM_INT);
    $stmt->bindParam(':animal_name', $animal_name, PDO::PARAM_STR, 5);
    /*** reassign the animal_id ***/
    $animal_id = 3;
    $animal_name = 'kevin';
    /*** execute the prepared statement ***/
    $stmt->execute();
    /*** loop over the results ***/
    while($row = $stmt->fetch())
        {
        echo $row['animal_id'].'';
        echo $row['animal_type'].'';
        echo $row['animal_name'].'';
        }
    /*** reassign the animal_id ***/
    $animal_id = 7;
    $animal_name = 'bruce';
    /*** execute the prepared statement ***/
    $stmt->execute();
    /*** loop over the results ***/
    while($row = $stmt->fetch())
        {
        echo $row['animal_id'].'';
        echo $row['animal_type'].'';
        echo $row['animal_name'].'';
        }
    /*** reassign the animal_id ***/
    $animal_id = 4;
    /*** execute the prepared statement ***/
    $stmt->execute();
    /*** loop over the results ***/
    while($row = $stmt->fetch())
        {
        echo $row['animal_id'].'';
        echo $row['animal_type'].'';
        echo $row['animal_name'];
        }
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Now we have run the query 3 times using the same prepared statement. The results look like this:

Connected to database
7
wombat
bruce
4
dingo
bruce

The second result set is missing as there is no animal named \’kevin\’, all Australians are named \’bruce\’. Note also in the above code we have changed the loop from foreach and PDOStatement::fetchAll() to a while loop using PDOStatement::fetch()As has been mentioned we can run this over and over, but while it is shorter than coding the query over and over, we can also use an array of values!

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** set the error reporting attribute ***/
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    /*** some variables ***/
    $data = array('animal_id'=>6, 'animal_name'=>'bruce');
    /*** prepare the SQL statement ***/
    $stmt = $dbh->prepare("SELECT * FROM animals WHERE animal_id = :animal_id AND animal_name = :animal_name");
    /*** bind the paramaters ***/
    $stmt->bindParam(':animal_id', $animal_id, PDO::PARAM_INT);
    $stmt->bindParam(':animal_name', $animal_name, PDO::PARAM_STR, 5);
    /*** reassign the variables ***/
    $data = array('animal_id'=>3, 'animal_name' => 'bruce');
    /*** execute the prepared statement ***/
    $stmt->execute($data);
    /*** loop over the results ***/
    while($row = $stmt->fetch())
        {
        echo $row['animal_id'].'';
        echo $row['animal_type'].'';
        echo $row['animal_name'].'';
        }
    /*** reassign the variables again ***/
    $data = array('animal_id'=>4, 'animal_name' => 'bruce');
    /*** execute the prepared statement ***/
    $stmt->execute($data);
    /*** loop over the results ***/
    while($row = $stmt->fetch())
        {
        echo $row['animal_id'].'';
        echo $row['animal_type'].'';
        echo $row['animal_name'].'';
        }
    /*** reassign the variables ***/
    $data = array('animal_id'=>9, 'animal_name' => 'bruce');
    /*** execute the prepared statement ***/
    $stmt->execute($data);
    /*** loop over the results ***/
    while($row = $stmt->fetch())
        {
        echo $row['animal_id'].'';
        echo $row['animal_type'].'';
        echo $row['animal_name'];
        }
    /*** close the database connection ***/
    $dbh = null;
}
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

Transactions
At the beginning of this tutorial was saw multiple INSERT statements to set up the initial database. This works fine but is code intensive and with a database like SQLite a problem arises with file locking for each access. The process can be bundled into a single access by using a transaction. Transactions are quite simple and have the benifit of rolling back changes should an error occur, perhaps a system crash.
A PDO transaction begins with the with PDO::beginTransaction() method. This method turns off auto-commit and any database statements or queries are not committed to the database until the transaction is committed with PDO::commit. When PDO::commit is called, all statements/queries are enacted and the database connection is returned to auto-commit mode.
This example shows how we might set up the animals database used in this tutorial.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
/*** database name ***/
$dbname = 'animals';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=$dbname", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** set the PDO error mode to exception ***/
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    /*** begin the transaction ***/
    $dbh->beginTransaction();
    /*** CREATE table statements ***/
    $table = "CREATE TABLE animals ( animal_id MEDIUMINT(8) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    animal_type VARCHAR(25) NOT NULL,
    animal_name VARCHAR(25) NOT NULL
    )";
    $dbh->exec($table);
    /***  INSERT statements ***/
    $dbh->exec("INSERT INTO animals (animal_type, animal_name) VALUES ('emu', 'bruce')");
    $dbh->exec("INSERT INTO animals (animal_type, animal_name) VALUES ('funnel web', 'bruce')");
    $dbh->exec("INSERT INTO animals (animal_type, animal_name) VALUES ('lizard', 'bruce')");
    $dbh->exec("INSERT INTO animals (animal_type, animal_name) VALUES ('dingo', 'bruce')");
    $dbh->exec("INSERT INTO animals (animal_type, animal_name) VALUES ('kangaroo', 'bruce')");
    $dbh->exec("INSERT INTO animals (animal_type, animal_name) VALUES ('wallaby', 'bruce')");
    $dbh->exec("INSERT INTO animals (animal_type, animal_name) VALUES ('wombat', 'bruce')");
    $dbh->exec("INSERT INTO animals (animal_type, animal_name) VALUES ('koala', 'bruce')");
    $dbh->exec("INSERT INTO animals (animal_type, animal_name) VALUES ('kiwi', 'bruce')");
    /*** commit the transaction ***/
    $dbh->commit();
    /*** echo a message to say the database was created ***/
    echo 'Data entered successfully';
}
catch(PDOException $e)
    {
    /*** roll back the transaction if we fail ***/
    $dbh->rollback();
    /*** echo the sql statement and error message ***/
    echo $sql . '' . $e->getMessage();
    }
?>

Get Last Insert Id
This is a common task required when you need to get the id of the last INSERT. This is done with PDO::lastInserId() method as shown here.

/*** mysql hostname ***/
$hostname = 'localhost';
/*** mysql username ***/
$username = 'username';
/*** mysql password ***/
$password = 'password';
try {
    $dbh = new PDO("mysql:host=$hostname;dbname=animals", $username, $password);
    /*** echo a message saying we have connected ***/
    echo 'Connected to database';
    /*** set the error reporting attribute ***/
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    /*** INSERT a new row ***/
    $dbh->exec("INSERT INTO animals(animal_type, animal_name) VALUES ('galah', 'polly')");
    /*** display the id of the last INSERT ***/
    echo $dbh->lastInsertId();
    /*** close the database connection ***/
    $dbh = null;
    }
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

A Global Instance
Ever need a global instance of your database connection? Here we achieve this with the use of the Singleton design patern. The goal of a singleton is to ensure the class has only a single instance and provide a global point of access to it. Here we use the getInstance() method to achieve this. A new instance is only created the first time it is accessed and all subsequent accesses are simply returned the existing instance.

class db{
/*** Declare instance ***/
private static $instance = NULL;
/**
*
* the constructor is set to private so
* so nobody can create a new instance using new
*
*/
private function __construct() {
  /*** maybe set the db name here later ***/
}
/**
*
* Return DB instance or create intitial connection
*
* @return object (PDO)
*
* @access public
*
*/
public static function getInstance() {
if (!self::$instance)
    {
    self::$instance = new PDO("mysql:host='localhost';dbname='animals'", 'username', 'password');;
    self::$instance-> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }
return self::$instance;
}
/**
*
* Like the constructor, we make __clone private
* so nobody can clone the instance
*
*/
private function __clone(){
}
} /*** end of class ***/
try    {
    /*** query the database ***/
    $result = DB::getInstance()->query("SELECT * FROM animals");
    /*** loop over the results ***/
    foreach($result as $row)
        {
        print $row['animal_type'] .' - '. $row['animal_name'] . '';
        }
    }
catch(PDOException $e)
    {
    echo $e->getMessage();
    }
?>

The above code will produce a result like this:

emu - bruce
funnel web - bruce
lizard - bruce
dingo - bruce
kangaroo - bruce
wallaby - bruce
wombat - bruce
koala - bruce

This method of access saves the overhead created when a new instance of an object is called each time it is referenced, so you have have few references to it. Also, if you wish to pass the objects state from one reference to another there is no need to create from the initial state.
Note that the constructor and clone methods have been made private to ensure that an instance of the class cannot be instantiated or cloned.
Conclusions.
If you have got this far you will have seen how to create a connection, prepare a statement and exceute, and to bind Params using bindParam(). This is what most folks will be using to begin with and shows the effectiveness of using PDO to make code more portable. We highly recommend you visit http://www.php.net/manual/en/ref.pdo.php and read up on all that PDO has to offer.

发表回复