Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Learning PHP Data Objects
Learning PHP Data Objects

Learning PHP Data Objects: A Beginner's Guide to PHP Data Objects, Database Connection Abstraction Library for PHP 5

eBook
$23.39 $25.99
Paperback
$43.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with Print?

Product feature icon Instant access to your digital copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Redeem a companion digital copy on all Print orders
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Table of content icon View table of contents Preview book icon Preview Book

Learning PHP Data Objects

Chapter 1. Introduction

PHP Data Objects, (PDO) is a PHP5 extension that defines a lightweight DBMS connection abstraction library (sometimes called data access abstraction library). The need for a tool like PDO was dictated by the great number of database systems supported by PHP. Each of these database systems required a separate extension that defined its own API for performing the same tasks, starting from establishing a connection to advanced features such as preparing statements and error handling.

The fact that these APIs were not unified made transition between underlying databases painful, often resulting in the rewriting of many lines of code, which in turn, led to new programming errors that required time to track, debug and correct. On the other hand, the absence of a unified library, like JDBC for Java, was putting PHP behind the big players in the programming languages world. Now that such library exists, PHP is regaining its position and is a platform of choice for millions of programmers.

It should be noted, however, that there exist several libraries written in PHP, that serve the same purpose as PDO. The most popular are the ADOdb library and the PEAR DB package. The key difference between them and PDO is speed. PDO is a PHP extension written in a compiled language (C/C++), while the PHP libraries are written in an interpreted language. Also, once PDO is enabled, it does not require you to include source files in your scripts and redistribute them with your application. This makes installing your applications easier, as the end user does not need to take care of third-party software.

Note

Here, we are neither comparing these libraries with PDO nor advocating the use of PDO over such libraries. We are just showing the advantages and disadvantages of this extension. For example, the PEAR package, MDB2, has richer functionality of an advanced database abstraction library, which PDO does not.

PDO being a PECL extension, itself relies on database-specific drivers and on other PECL extensions. These drivers must also be installed in order to use PDO (you only need the drivers for the databases you are using). Since the description of installation of PDO and database-specific drivers is beyond the scope of this book, you can refer to PHP manual at www.php.net/pdo for technical information regarding installation and upgrade issues.

Note

PECL is PHP Extension Community Library, a repository of PHP extensions written in C. These extensions offer functionality that would be impossible to implement in PHP, as well as some extensions that exist for performance reasons as the C code is much faster than PHP. The home page of PECL is at http://pecl.php.net

Using PDO

As it has been noted in the previous section, PDO is a connection, or data access abstraction library. This means that PDO defines a unified interface for creating and maintaining database connections, issuing queries, quoting parameters, traversing result sets, dealing with prepared statements, and error handling.

We will give a quick overview of these topics here and look at them in greater detail in the following chapters.

Connecting to the Database

Let's consider the well-known MySQL connection scenario:

mysql_connect($host, $user, $password);
mysql_select_db($db);

Here, we establish a connection and then select the default database for the connection. (We ignore the issue of possible errors.)

In SQLite, for example, we would write something like the following:

$dbh = sqlite_open($db, 0666);

Here again we ignore errors (we will cover more on this later). For completeness, let's see how we would connect to a PostgreSQL:

pg_connect("host=$host dbname=$db user=$user password=$password");

As you can see, all three databases require quite different ways of opening a connection. While this is not a problem now, but if you always use the same database management system in case you need to migrate, you will have to rewrite your scripts.

Now, let's see what PDO has to offer. As PDO is fully object-oriented, we will be dealing with connection objects, and further interaction with the database will involve calling various methods of these objects. The examples above implied the need for something analogous to these connection objects—calls to mysql_connect or pg_connect return link identifiers and PHP variables of a special type: resource. However, we didn't use connection objects then since these two database APIs don't require us to explicitly use them if we only have one connection in our scripts. However, SQLite always requires a link identifier.

With PDO, we will always have to explicitly use the connection object, since there is no other way of calling its methods. (Those unfamiliar with object-oriented programming should refer to Appendix A).

Each of the three above connections could be established in the following manner:

// For MySQL:
$conn = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
// For SQLite:
$conn = new PDO("sqlite:$db");
// And for PostgreSQL:
$conn = new PDO("pgsql:host=$host dbname=$db", $user, $pass);

As you can see, the only part that is changing here is the first argument passed to the PDO constructor. For SQLite, which does not utilize username and password, the second and third arguments can be skipped.

Note

SQLite is not a database server, but it is an embedded SQL database library that operates on local files. More information about SQLite can be found at www.sqlite.org and more information about using SQLite with PHP can be found at www.php.net/sqlite. Information about using SQLite with PDO can be obtained from www.php.net/manual/en/ref.pdo-sqlite.php

Connection Strings

As you have seen in previous example, PDO uses the so-called connection strings (or Data Source Names, abbreviated to DSN) that allow the PDO constructor to select proper driver and pass subsequent method calls to it. These connection strings or DSNs are different for every database management system and are the only things that you will have to change.

If you are designing a big application that will be able to work with different databases, then this connection string (together with a connection username and a password) can be defined in a configuration file and later used in the following manner (assuming your configuration file is similar to php.ini)

$config = parse_ini_file($pathToConfigFile);
$conn = new PDO($config['db.conn'], $config['db.user'],
$config['db.pass']);

Your configuration file might then look like this:

db.conn="mysql:host=localhost;dbname=test"
db.user="johns"
db.pass="mypassphrase"

We will cover connection strings in more detail in Chapter 2; here we gave a quick example so that you can see how easy it is to connect to different database systems with PDO.

Issuing SQL Queries, Quoting Parameters, and Handling Result Sets

PDO would not be worth a whole book, if it didn't go beyond the single interface for creating database connections. The PDO object introduced in the previous example has all the methods needed to uniformly execute queries regardless of the database used.

Let's consider a simple query that would select all the car make attributes from an imaginary database employed at a used car lot. The query is as simple as the following SQL command:

SELECT DISTINCT make FROM cars ORDER BY make;

Previously, we would have had to call different functions, depending on the database:

// Let's keep our SQL in a single variable
$sql = 'SELECT DISTINCT make FROM cars ORDER BY make';
// Now, assuming MySQL:
mysql_connect('localhost', 'boss', 'password');
mysql_select_db('cars');
$q = mysql_query($sql);
// For SQLite we would do:
$dbh = sqlite_open('/path/to/cars.ldb', 0666);
$q = sqlite_query($sql, $dbh);
// And for PostgreSQL:
pg_connect("host=localhost dbname=cars user=boss
password=password");
$q = pg_query($sql);

Now that we are using PDO, we can do the following:

// assume the $connStr variable holds a valid connection string
// as discussed in previous point
$sql = 'SELECT DISTINCT make FROM cars ORDER BY make';
$conn = new PDO($connStr, 'boss', 'password');
$q = $conn->query($sql);

As you can see, doing things the PDO way is not too different from traditional methods of issuing queries. Also, here it should be underlined, that a call to $conn->query() is returning another object of class PDOStatement, unlike the calls to mysql_query(), sqlite_query(), and pg_query(), which return PHP variables of the resource type.

Now, let's make our simplistic SQL query a bit more complicated so that it selects the total value of all Fords on sale in our imaginary car lot. The query would then look something like this:

SELECT sum(price) FROM cars WHERE make='Ford'

To make our example even more interesting, let's assume that the name of the car manufacturer is held in a variable ($make) so that we must quote it, before passing it to the database. Our non-PDO queries would now look like this:

$make = 'Ford';
// MySQL:
$m = mysql_real_escape_string($make);
$q = mysql_query("SELECT sum(price) FROM cars WHERE make='$m'");
// SQLite:
$m = sqlite_escape_string($make);
$q = sqlite_query("SELECT sum(price) FROM cars WHERE make='$m'",
$dbh);
// and PostgreSQL:
$m = pg_escape_string($make);
$q = pg_query("SELECT sum(price) FROM cars WHERE make='$m'");

The PDO class defines a single method for quoting strings so that they can be used safely in queries. We will discuss security issues such as SQL injection, in Chapter 3. This method does a neat thing; it will automatically add quotes around the value if necessary:

$m = $conn->quote($make);
$q = $conn->query("SELECT sum(price) FROM cars WHERE make=$m");

Again, you can see that PDO allows you to use the same pattern as you would have used before, but the names of all the methods are unified.

Now that we have issued our query, we will want to see its results. As the query in the last example will always return just one row, we will want more rows. Again, the three databases will require us to call different functions on the $q variable that was returned from one of the three calls to mysql_query(), sqlite_query(), or pg_query(). So our code for getting all the cars will look similar to this:

// assume the query is in the $sql variable
$sql = "SELECT DISTINCT make FROM cars ORDER BY make";
// For MySQL:
$q = mysql_query($sql);
while($r = mysql_fetch_assoc($q))
{
echo $r['make'], "\n";
}
// For SQLite:
$q = sqlite_query($dbh, $sql);
while($r = sqlite_fetch_array($q, SQLITE_ASSOC))
{
echo $r['make'], "\n";
}
// and, finally, PostgreSQL:
$q = pg_query($sql);
while($r = pg_fetch_assoc($q))
{
echo $r['make'], "\n";
}

As you can see, the idea is the same, but we have to use different function names. Also, note that SQLite requires an extra parameter if we want to get the rows in the same way as with MySQL and PostgreSQL (of course, this could be omitted, but then the returned rows would contain both column name indexed and numerically indexed elements.)

As you may already have guessed, things are pretty straightforward when it comes to PDO: We don't care what the underlying database is, and the methods for fetching rows are the same across all databases. So, the above code could be rewritten for PDO in the following way:

$q = $conn->query("SELECT DISTINCT make FROM cars ORDER BY make");
while($r = $q->fetch(PDO::FETCH_ASSOC))
{
echo $r['make'], "\n";
}

Nothing is different from what happens before. One thing to note here is that we explicitly specified the PDO::FETCH_ASSOC fetch style constant here, since PDO's default behavior is to fetch the result rows as arrays indexed both by column name and number. (This behavior is similar to mysql_fetch_array(), sqlite_fetch_array() without the second parameter, or pg_fetch_array().) We will discuss the fetch styles that PDO has to offer in Chapter 2.

Note

The last example was not intended to be used to render HTML pages as it used the newline character to separate lines of output. To use it in a real webpage, you will have to change echo $r['make'], "\n"; to echo $r['make'], "<br>\n";

Error Handling

Of course, the above examples didn't provide for any error checking, so they are not very useful for real-life applications.

When working with a database, we should check for errors when opening the connection to the database, when selecting the database and after issuing every query. Most web applications, however, just need to display an error message when something goes wrong (without going into error detail, which could reveal some sensitive information). However, when debugging an error, you (as the developer) would need the most detailed error information possible so that you can debug the error in the shortest possible time.

One simplistic scenario would be to abort the script and present the error message (although this is something you probably would not want to do). Depending on the database, our code might look like this:

// For SQLite:
$dbh = sqlite_open('/path/to/cars.ldb', 0666) or die
('Error opening SQLite database: ' .
sqlite_error_string(sqlite_last_error($dbh)));
$q = sqlite_query("SELECT DISTINCT make FROM cars ORDER BY make",
$dbh) or die('Could not execute query because: ' .
sqlite_error_string(sqlite_last_error($dbh)));
// and, finally, for PostgreSQL:
pg_connect("host=localhost dbname=cars user=boss
password=password") or die('Could not connect to
PostgreSQL: . pg_last_error());
$q = pg_query("SELECT DISTINCT make FROM cars ORDER BY make")
or die('Could not execute query because: ' . pg_last_error());

As you can see, error handling is starting to get a bit different for SQLite compared to MySQL and PostgreSQL. (Note the call to sqlite_error_string (sqlite_last_error($dbh)).)

Before we take a look at how to implement the same error handling strategy with PDO, we should note that this will be only one of the three possible error handling strategies in PDO. We will cover them in detail later in this book. Here we will just use the simplest one:

// PDO error handling
// Assume the connection string is one of the following:
// $connStr = 'mysql:host=localhost;dbname=cars'
// $connStr = 'sqlite:/path/to/cars.ldb';
// $connStr = 'pgsql:host=localhost dbname=cars';
try
{
$conn = new PDO($connStr, 'boss', 'password');
}
catch(PDOException $pe)
{
die('Could not connect to the database because: ' .
$pe->getMessage();
}
$q = $conn->query("SELECT DISTINCT make FROM cars ORDER BY make");
if(!$q)
{
$ei = $conn->errorInfo();
die('Could not execute query because: ' . $ei[2]);
}

This example shows that PDO will force us to use a slightly different error handling scheme from the traditional one. We wrapped the call to the PDO constructor in a try catch block. (Those who are new to PHP5's object-oriented features should refer to Appendix A.) This is because while PDO can be instructed not to use exceptions, (in fact, it is PDO's default behavior not to use exceptions), however, you cannot avoid exceptions here. If the call to the constructor fails, an exception will always be thrown.

It is a very good idea to catch that exception because, by default, PHP will abort the script execution and will display an error message like this:

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[28000] [1045] Access denied for user 'bosss'@'localhost' (using password: YES)' in /var/www/html/pdo.php5:3 Stack trace: #0 c:\www\hosts\localhost\pdo.php5(3): PDO->__construct('mysql:host=loca...', 'bosss', 'password', Array) #1 {main} thrown in /var/www/html/pdo.php5 on line 3

We made this exception by supplying the wrong username, bosss, in the call to the PDO constructor. As you can see from this output, it contains some details that we would not like others to see: Things like file names and script paths, the type of database being used, and most importantly, usernames and passwords. Suppose that this exception had happened when we had supplied the right username and something had gone wrong with the database server. Then the screen output would have contained the real username and password.

If we catch the exception properly, the error output might look like this:

SQLSTATE[28000] [1045] Access denied for user 'bosss'@'localhost' (using password: YES)

This error message contains much less sensitive information. (In fact, this output is very similar to the error output that would be produced by one of our non-PDO examples.) But we will again warn you that the best policy is just show some neutral error message like: "Sorry, the service is temporarily unavailable. Please try again later." Of course, you should also log all errors so that you can find out later whether anything bad has happened.

Prepared Statements

This is a rather advanced topic, but you should become familiar with it. If you are a user of PHP with MySQL or SQLite, then you probably didn't even hear of prepared statements, since PHP's MySQL and SQLite extensions don't offer this functionality. PostgreSQL users might have already used pg_prepare() and pg_execute() in tandem. MySQLi (the improved MySQL extension) also offers the prepared statements functionality, but in a somewhat awkward way (despite the possible object-oriented style).

For those who are not familiar with prepared statements, we will now give a short explanation.

When developing database-driven, interactive dynamic applications, you will sooner or later need to take user input (which may originate from a form) and pass it as a part of a query to a database. For example, given our cars' database, you might design a feature that will output a list of cars made between any two years. If you allow the user to enter these years in a form, the code will look something like this:

// Suppose the years come in the startYear and endYear
// request variables:
$sy = (int)$_REQUEST['startYear'];
$ey = (int)$_REQUEST['endYear'];
if($ey < $sy)
{
// ensure $sy is less than $ey
$tmp = $ey;
$ey = $sy;
$sy = $tmp;
}
$sql = "SELECT * FROM cars WHERE year >= $sy AND year <= $ey";
// send the query in $sql…

In this simple example the query depends on two variables, which are part of the resulting SQL. A corresponding prepared statement in PDO would look something like this:

$sql = 'SELECT * FROM cars WHERE year >= ? AND year <= ?';

As you can see, we replaced the $sy and $ey variables with placeholders in the query body. We can now manipulate this query to create the prepared statement and execute it:

// Assuming we have already connected and prepared
// the $sy and $ey variables
$sql = 'SELECT * FROM cars WHERE year >= ? AND year <= ?';
$stmt = $conn->prepare($sql);
$stmt->execute(array($sy, $ey));

These three lines of code tells us that the prepared statements are objects (with class PDOStatement). They are created using calls to PDO::prepare() method that accepts an SQL statement with placeholders as its parameters.

The prepared statements then have to be executed in order to obtain the query results by calling the PDOStatement::execute() method. As the example shows, we call this method with an array that holds the values for the placeholders. Note how the order of the variables in that array matches the order of the placeholders in the $sql variable. Obviously, the number of elements in the array must be the same as the number of placeholders in the query.

You have probably noticed that we are not saving the result of the call to the PDOStatement::execute() method in any variable. This is because the statement object itself is used to access the query results, so that we can complete our example to look like this:

// Suppose the years come in the startYear and endYear
// request variables:
$sy = (int)$_REQUEST['startYear'];
$ey = (int)$_REQUEST['endYear'];
if($ey < $sy)
{
// ensure $sy is less than $ey
$tmp = $ey;
$ey = $sy;
$sy = $tmp;
}
$sql = 'SELECT * FROM cars WHERE year >= ? AND year <= ?';
$stmt = $conn->prepare($sql);
$stmt->execute(array($sy, $ey));
// now iterate over the result as if we obtained
// the $stmt in a call to PDO::query()
while($r = $stmt->fetch(PDO::FETCH_ASSOC))
{
echo "$r[make] $r[model] $r[year]\n";
}

As this complete example shows, we call the PDOStatement::fetch() method until it returns a false value, at which point the loop quits—just like we did in previous examples when discussing result sets traversal.

Of course, the replacement of question mark placeholders with actual values is not the only thing that prepared statements can do. Their power lies in the possibility of being executed as many times as needed. This means that we can call the PDOStatement::execute() method as many times as we want, and every time we can supply different values for the placeholders. For example, we can do this:

$sql = 'SELECT * FROM cars WHERE year >= ? AND year <= ?';
$stmt = $conn->prepare($sql);
// Fetch the 'new' cars:
$stmt->execute(array(2005, 2007));
$newCars = $stmt->fetchAll(PDO::FETCH_ASSOC);
// now, 'older' cars:
$stmt->execute(array(2000, 2004));
$olderCars = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Show them
echo 'We have ', count($newCars), ' cars dated 2005-2007';
print_r($newCars);
echo 'Also we have ', count($olderCars), ' cars dated 2000-2004';
print_r($olderCars);

Prepared statements tend to execute faster than calls to PDO::query() methods, since the database drivers optimize them only once, in a call to PDO::prepare() methods. Another advantage of using prepared statements is that you don't have to quote the parameters passed in a call to PDOStatement::execute().

In our example we used an explicit cast of the request parameters into integer variables, but we could also have done the following:

// Assume we also want to filter by make
$sql = 'SELECT * FROM cars WHERE make=?';
$stmt = $conn->prepare($sql);
$stmt->execute(array($_REQUEST['make']));

The prepared statement here will take care of the proper quoting made before executing the query.

And just to finish the introduction of the prepared statements here, probably the best feature about them is that PDO emulates them for every supported database. This means you can use prepared statements with any databases; even if they don’t know what they are.

Appropriate Understanding of PDO

Our introduction would not be complete if we didn't mention that. PDO is a database connection abstraction library, and as such, cannot ensure that your code will work for each and every database that it supports. This will only happen if your SQL code is portable. For example, MySQL extends the SQL syntax with this form of insert:

INSERT INTO mytable SET x=1, y='two';

This kind of SQL code is not portable, as other databases do not understand this way of doing inserts. To ensure that your inserts work across databases, you should replace the above code with :

INSERT INTO mytable(x, y) VALUES(1, 'two');

This is just one example of incompatibilities that may arise when you use PDO. It is only by making your database schema and SQL portable that can ensure you that your code will be compatible with other databases. However, ensuring this portability is beyond this text.

Using PDO


As it has been noted in the previous section, PDO is a connection, or data access abstraction library. This means that PDO defines a unified interface for creating and maintaining database connections, issuing queries, quoting parameters, traversing result sets, dealing with prepared statements, and error handling.

We will give a quick overview of these topics here and look at them in greater detail in the following chapters.

Connecting to the Database

Let's consider the well-known MySQL connection scenario:

mysql_connect($host, $user, $password);
mysql_select_db($db);

Here, we establish a connection and then select the default database for the connection. (We ignore the issue of possible errors.)

In SQLite, for example, we would write something like the following:

$dbh = sqlite_open($db, 0666);

Here again we ignore errors (we will cover more on this later). For completeness, let's see how we would connect to a PostgreSQL:

pg_connect("host=$host dbname=$db user=$user password=$password");

As you can see, all three databases require quite different ways of opening a connection. While this is not a problem now, but if you always use the same database management system in case you need to migrate, you will have to rewrite your scripts.

Now, let's see what PDO has to offer. As PDO is fully object-oriented, we will be dealing with connection objects, and further interaction with the database will involve calling various methods of these objects. The examples above implied the need for something analogous to these connection objects—calls to mysql_connect or pg_connect return link identifiers and PHP variables of a special type: resource. However, we didn't use connection objects then since these two database APIs don't require us to explicitly use them if we only have one connection in our scripts. However, SQLite always requires a link identifier.

With PDO, we will always have to explicitly use the connection object, since there is no other way of calling its methods. (Those unfamiliar with object-oriented programming should refer to Appendix A).

Each of the three above connections could be established in the following manner:

// For MySQL:
$conn = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
// For SQLite:
$conn = new PDO("sqlite:$db");
// And for PostgreSQL:
$conn = new PDO("pgsql:host=$host dbname=$db", $user, $pass);

As you can see, the only part that is changing here is the first argument passed to the PDO constructor. For SQLite, which does not utilize username and password, the second and third arguments can be skipped.

Note

SQLite is not a database server, but it is an embedded SQL database library that operates on local files. More information about SQLite can be found at www.sqlite.org and more information about using SQLite with PHP can be found at www.php.net/sqlite. Information about using SQLite with PDO can be obtained from www.php.net/manual/en/ref.pdo-sqlite.php

Connection Strings

As you have seen in previous example, PDO uses the so-called connection strings (or Data Source Names, abbreviated to DSN) that allow the PDO constructor to select proper driver and pass subsequent method calls to it. These connection strings or DSNs are different for every database management system and are the only things that you will have to change.

If you are designing a big application that will be able to work with different databases, then this connection string (together with a connection username and a password) can be defined in a configuration file and later used in the following manner (assuming your configuration file is similar to php.ini)

$config = parse_ini_file($pathToConfigFile);
$conn = new PDO($config['db.conn'], $config['db.user'],
$config['db.pass']);

Your configuration file might then look like this:

db.conn="mysql:host=localhost;dbname=test"
db.user="johns"
db.pass="mypassphrase"

We will cover connection strings in more detail in Chapter 2; here we gave a quick example so that you can see how easy it is to connect to different database systems with PDO.

Issuing SQL Queries, Quoting Parameters, and Handling Result Sets

PDO would not be worth a whole book, if it didn't go beyond the single interface for creating database connections. The PDO object introduced in the previous example has all the methods needed to uniformly execute queries regardless of the database used.

Let's consider a simple query that would select all the car make attributes from an imaginary database employed at a used car lot. The query is as simple as the following SQL command:

SELECT DISTINCT make FROM cars ORDER BY make;

Previously, we would have had to call different functions, depending on the database:

// Let's keep our SQL in a single variable
$sql = 'SELECT DISTINCT make FROM cars ORDER BY make';
// Now, assuming MySQL:
mysql_connect('localhost', 'boss', 'password');
mysql_select_db('cars');
$q = mysql_query($sql);
// For SQLite we would do:
$dbh = sqlite_open('/path/to/cars.ldb', 0666);
$q = sqlite_query($sql, $dbh);
// And for PostgreSQL:
pg_connect("host=localhost dbname=cars user=boss
password=password");
$q = pg_query($sql);

Now that we are using PDO, we can do the following:

// assume the $connStr variable holds a valid connection string
// as discussed in previous point
$sql = 'SELECT DISTINCT make FROM cars ORDER BY make';
$conn = new PDO($connStr, 'boss', 'password');
$q = $conn->query($sql);

As you can see, doing things the PDO way is not too different from traditional methods of issuing queries. Also, here it should be underlined, that a call to $conn->query() is returning another object of class PDOStatement, unlike the calls to mysql_query(), sqlite_query(), and pg_query(), which return PHP variables of the resource type.

Now, let's make our simplistic SQL query a bit more complicated so that it selects the total value of all Fords on sale in our imaginary car lot. The query would then look something like this:

SELECT sum(price) FROM cars WHERE make='Ford'

To make our example even more interesting, let's assume that the name of the car manufacturer is held in a variable ($make) so that we must quote it, before passing it to the database. Our non-PDO queries would now look like this:

$make = 'Ford';
// MySQL:
$m = mysql_real_escape_string($make);
$q = mysql_query("SELECT sum(price) FROM cars WHERE make='$m'");
// SQLite:
$m = sqlite_escape_string($make);
$q = sqlite_query("SELECT sum(price) FROM cars WHERE make='$m'",
$dbh);
// and PostgreSQL:
$m = pg_escape_string($make);
$q = pg_query("SELECT sum(price) FROM cars WHERE make='$m'");

The PDO class defines a single method for quoting strings so that they can be used safely in queries. We will discuss security issues such as SQL injection, in Chapter 3. This method does a neat thing; it will automatically add quotes around the value if necessary:

$m = $conn->quote($make);
$q = $conn->query("SELECT sum(price) FROM cars WHERE make=$m");

Again, you can see that PDO allows you to use the same pattern as you would have used before, but the names of all the methods are unified.

Now that we have issued our query, we will want to see its results. As the query in the last example will always return just one row, we will want more rows. Again, the three databases will require us to call different functions on the $q variable that was returned from one of the three calls to mysql_query(), sqlite_query(), or pg_query(). So our code for getting all the cars will look similar to this:

// assume the query is in the $sql variable
$sql = "SELECT DISTINCT make FROM cars ORDER BY make";
// For MySQL:
$q = mysql_query($sql);
while($r = mysql_fetch_assoc($q))
{
echo $r['make'], "\n";
}
// For SQLite:
$q = sqlite_query($dbh, $sql);
while($r = sqlite_fetch_array($q, SQLITE_ASSOC))
{
echo $r['make'], "\n";
}
// and, finally, PostgreSQL:
$q = pg_query($sql);
while($r = pg_fetch_assoc($q))
{
echo $r['make'], "\n";
}

As you can see, the idea is the same, but we have to use different function names. Also, note that SQLite requires an extra parameter if we want to get the rows in the same way as with MySQL and PostgreSQL (of course, this could be omitted, but then the returned rows would contain both column name indexed and numerically indexed elements.)

As you may already have guessed, things are pretty straightforward when it comes to PDO: We don't care what the underlying database is, and the methods for fetching rows are the same across all databases. So, the above code could be rewritten for PDO in the following way:

$q = $conn->query("SELECT DISTINCT make FROM cars ORDER BY make");
while($r = $q->fetch(PDO::FETCH_ASSOC))
{
echo $r['make'], "\n";
}

Nothing is different from what happens before. One thing to note here is that we explicitly specified the PDO::FETCH_ASSOC fetch style constant here, since PDO's default behavior is to fetch the result rows as arrays indexed both by column name and number. (This behavior is similar to mysql_fetch_array(), sqlite_fetch_array() without the second parameter, or pg_fetch_array().) We will discuss the fetch styles that PDO has to offer in Chapter 2.

Note

The last example was not intended to be used to render HTML pages as it used the newline character to separate lines of output. To use it in a real webpage, you will have to change echo $r['make'], "\n"; to echo $r['make'], "<br>\n";

Error Handling

Of course, the above examples didn't provide for any error checking, so they are not very useful for real-life applications.

When working with a database, we should check for errors when opening the connection to the database, when selecting the database and after issuing every query. Most web applications, however, just need to display an error message when something goes wrong (without going into error detail, which could reveal some sensitive information). However, when debugging an error, you (as the developer) would need the most detailed error information possible so that you can debug the error in the shortest possible time.

One simplistic scenario would be to abort the script and present the error message (although this is something you probably would not want to do). Depending on the database, our code might look like this:

// For SQLite:
$dbh = sqlite_open('/path/to/cars.ldb', 0666) or die
('Error opening SQLite database: ' .
sqlite_error_string(sqlite_last_error($dbh)));
$q = sqlite_query("SELECT DISTINCT make FROM cars ORDER BY make",
$dbh) or die('Could not execute query because: ' .
sqlite_error_string(sqlite_last_error($dbh)));
// and, finally, for PostgreSQL:
pg_connect("host=localhost dbname=cars user=boss
password=password") or die('Could not connect to
PostgreSQL: . pg_last_error());
$q = pg_query("SELECT DISTINCT make FROM cars ORDER BY make")
or die('Could not execute query because: ' . pg_last_error());

As you can see, error handling is starting to get a bit different for SQLite compared to MySQL and PostgreSQL. (Note the call to sqlite_error_string (sqlite_last_error($dbh)).)

Before we take a look at how to implement the same error handling strategy with PDO, we should note that this will be only one of the three possible error handling strategies in PDO. We will cover them in detail later in this book. Here we will just use the simplest one:

// PDO error handling
// Assume the connection string is one of the following:
// $connStr = 'mysql:host=localhost;dbname=cars'
// $connStr = 'sqlite:/path/to/cars.ldb';
// $connStr = 'pgsql:host=localhost dbname=cars';
try
{
$conn = new PDO($connStr, 'boss', 'password');
}
catch(PDOException $pe)
{
die('Could not connect to the database because: ' .
$pe->getMessage();
}
$q = $conn->query("SELECT DISTINCT make FROM cars ORDER BY make");
if(!$q)
{
$ei = $conn->errorInfo();
die('Could not execute query because: ' . $ei[2]);
}

This example shows that PDO will force us to use a slightly different error handling scheme from the traditional one. We wrapped the call to the PDO constructor in a try catch block. (Those who are new to PHP5's object-oriented features should refer to Appendix A.) This is because while PDO can be instructed not to use exceptions, (in fact, it is PDO's default behavior not to use exceptions), however, you cannot avoid exceptions here. If the call to the constructor fails, an exception will always be thrown.

It is a very good idea to catch that exception because, by default, PHP will abort the script execution and will display an error message like this:

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[28000] [1045] Access denied for user 'bosss'@'localhost' (using password: YES)' in /var/www/html/pdo.php5:3 Stack trace: #0 c:\www\hosts\localhost\pdo.php5(3): PDO->__construct('mysql:host=loca...', 'bosss', 'password', Array) #1 {main} thrown in /var/www/html/pdo.php5 on line 3

We made this exception by supplying the wrong username, bosss, in the call to the PDO constructor. As you can see from this output, it contains some details that we would not like others to see: Things like file names and script paths, the type of database being used, and most importantly, usernames and passwords. Suppose that this exception had happened when we had supplied the right username and something had gone wrong with the database server. Then the screen output would have contained the real username and password.

If we catch the exception properly, the error output might look like this:

SQLSTATE[28000] [1045] Access denied for user 'bosss'@'localhost' (using password: YES)

This error message contains much less sensitive information. (In fact, this output is very similar to the error output that would be produced by one of our non-PDO examples.) But we will again warn you that the best policy is just show some neutral error message like: "Sorry, the service is temporarily unavailable. Please try again later." Of course, you should also log all errors so that you can find out later whether anything bad has happened.

Prepared Statements

This is a rather advanced topic, but you should become familiar with it. If you are a user of PHP with MySQL or SQLite, then you probably didn't even hear of prepared statements, since PHP's MySQL and SQLite extensions don't offer this functionality. PostgreSQL users might have already used pg_prepare() and pg_execute() in tandem. MySQLi (the improved MySQL extension) also offers the prepared statements functionality, but in a somewhat awkward way (despite the possible object-oriented style).

For those who are not familiar with prepared statements, we will now give a short explanation.

When developing database-driven, interactive dynamic applications, you will sooner or later need to take user input (which may originate from a form) and pass it as a part of a query to a database. For example, given our cars' database, you might design a feature that will output a list of cars made between any two years. If you allow the user to enter these years in a form, the code will look something like this:

// Suppose the years come in the startYear and endYear
// request variables:
$sy = (int)$_REQUEST['startYear'];
$ey = (int)$_REQUEST['endYear'];
if($ey < $sy)
{
// ensure $sy is less than $ey
$tmp = $ey;
$ey = $sy;
$sy = $tmp;
}
$sql = "SELECT * FROM cars WHERE year >= $sy AND year <= $ey";
// send the query in $sql…

In this simple example the query depends on two variables, which are part of the resulting SQL. A corresponding prepared statement in PDO would look something like this:

$sql = 'SELECT * FROM cars WHERE year >= ? AND year <= ?';

As you can see, we replaced the $sy and $ey variables with placeholders in the query body. We can now manipulate this query to create the prepared statement and execute it:

// Assuming we have already connected and prepared
// the $sy and $ey variables
$sql = 'SELECT * FROM cars WHERE year >= ? AND year <= ?';
$stmt = $conn->prepare($sql);
$stmt->execute(array($sy, $ey));

These three lines of code tells us that the prepared statements are objects (with class PDOStatement). They are created using calls to PDO::prepare() method that accepts an SQL statement with placeholders as its parameters.

The prepared statements then have to be executed in order to obtain the query results by calling the PDOStatement::execute() method. As the example shows, we call this method with an array that holds the values for the placeholders. Note how the order of the variables in that array matches the order of the placeholders in the $sql variable. Obviously, the number of elements in the array must be the same as the number of placeholders in the query.

You have probably noticed that we are not saving the result of the call to the PDOStatement::execute() method in any variable. This is because the statement object itself is used to access the query results, so that we can complete our example to look like this:

// Suppose the years come in the startYear and endYear
// request variables:
$sy = (int)$_REQUEST['startYear'];
$ey = (int)$_REQUEST['endYear'];
if($ey < $sy)
{
// ensure $sy is less than $ey
$tmp = $ey;
$ey = $sy;
$sy = $tmp;
}
$sql = 'SELECT * FROM cars WHERE year >= ? AND year <= ?';
$stmt = $conn->prepare($sql);
$stmt->execute(array($sy, $ey));
// now iterate over the result as if we obtained
// the $stmt in a call to PDO::query()
while($r = $stmt->fetch(PDO::FETCH_ASSOC))
{
echo "$r[make] $r[model] $r[year]\n";
}

As this complete example shows, we call the PDOStatement::fetch() method until it returns a false value, at which point the loop quits—just like we did in previous examples when discussing result sets traversal.

Of course, the replacement of question mark placeholders with actual values is not the only thing that prepared statements can do. Their power lies in the possibility of being executed as many times as needed. This means that we can call the PDOStatement::execute() method as many times as we want, and every time we can supply different values for the placeholders. For example, we can do this:

$sql = 'SELECT * FROM cars WHERE year >= ? AND year <= ?';
$stmt = $conn->prepare($sql);
// Fetch the 'new' cars:
$stmt->execute(array(2005, 2007));
$newCars = $stmt->fetchAll(PDO::FETCH_ASSOC);
// now, 'older' cars:
$stmt->execute(array(2000, 2004));
$olderCars = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Show them
echo 'We have ', count($newCars), ' cars dated 2005-2007';
print_r($newCars);
echo 'Also we have ', count($olderCars), ' cars dated 2000-2004';
print_r($olderCars);

Prepared statements tend to execute faster than calls to PDO::query() methods, since the database drivers optimize them only once, in a call to PDO::prepare() methods. Another advantage of using prepared statements is that you don't have to quote the parameters passed in a call to PDOStatement::execute().

In our example we used an explicit cast of the request parameters into integer variables, but we could also have done the following:

// Assume we also want to filter by make
$sql = 'SELECT * FROM cars WHERE make=?';
$stmt = $conn->prepare($sql);
$stmt->execute(array($_REQUEST['make']));

The prepared statement here will take care of the proper quoting made before executing the query.

And just to finish the introduction of the prepared statements here, probably the best feature about them is that PDO emulates them for every supported database. This means you can use prepared statements with any databases; even if they don’t know what they are.

Appropriate Understanding of PDO

Our introduction would not be complete if we didn't mention that. PDO is a database connection abstraction library, and as such, cannot ensure that your code will work for each and every database that it supports. This will only happen if your SQL code is portable. For example, MySQL extends the SQL syntax with this form of insert:

INSERT INTO mytable SET x=1, y='two';

This kind of SQL code is not portable, as other databases do not understand this way of doing inserts. To ensure that your inserts work across databases, you should replace the above code with :

INSERT INTO mytable(x, y) VALUES(1, 'two');

This is just one example of incompatibilities that may arise when you use PDO. It is only by making your database schema and SQL portable that can ensure you that your code will be compatible with other databases. However, ensuring this portability is beyond this text.

Summary


This introductory chapter showed you the basics of using PDO when developing dynamic, database-driven applications with the PHP5 language. Also we looked at how PDO can be effectively used to eliminate the differences between different traditional database access APIs and to produce a clearer and more portable code.

In the subsequent chapters, we will be looking at each of the features discussed in this chapter in a greater detail so that you fully master the PHP Data Objects extension.

Left arrow icon Right arrow icon

Key benefits

  • An overview of PDO
  • Creating a database and connecting to it
  • Error Handling
  • Advanced features

Description

PDO is lighter, faster, and more powerful than existing PHP data abstraction interfaces. PDO is a common interface to different databases that must be used with a database-specific PDO driver to access a particular database server: the PDO extension does not provide a database abstraction by itself; it doesn't rewrite SQL, emulate missing database features, or perform any database functions using by itself. It performs the same role as other classic database abstraction layers such as ODBC and JDBC: it's a query abstraction layer that abstracts the mechanism for accessing a database and manipulating the returned records; each database driver that implements the PDO interface can also expose database-specific features as regular extension functions. ¬ PDO ships with PHP 5.1, and is available as an extension for PHP 5.0; it requires the new object-oriented features of PHP 5, and cannot run with earlier versions of PHP.This book will teach you how to use the PDO, including its advanced features. Readers need to be aware of the basics of data abstraction and should be familiar with PHP.

Who is this book for?

PHP developers who need to use PDO for data abstraction.alt="Learning PHP Data Objects" title="Learning PHP Data Objects

What you will learn

  • This book covers:
  • An overview of the technology
  • Getting started with PDO
  • Error handling, prepared statements, and handling rowsets
  • Advanced features, like getting column metadata and setting connection parameters with examples
  • Chapter 1 gives an overview of PDO along with a few features like single interface for creating a connection, connection strings, uniform statement methods, and use of exceptions and a singe system of error codes.Chapter 2 helps to get you started with PDO, by creating sample database and then by creating a connection object. It also introduces PDOStatement classes.Chapter 3 deals with various error handling processes and their uses.Chapter 4 is about prepared statements. It deals with using prepared statements without binding values, binding a variable and a parameter to a prepared statement, and using LOBs as streams.Chapter 5 talks about handling rowsets. It covers different ways to retrieve multiple rows. Scrollable cursors, and MySQL unbuffered queries and closing the cursor along with multiple queries, and rowsets are also explained.Chapter 6 talks about advanced uses of PDO, and includes setting connection parameters, transactions, and methods of PDO and PDOStatement class.Chapter 7 gives an example where creation of the method part of MVC application is discussed. Appendix explains the object-oriented features like inheritance, encapsulation, polymorphism, and exception handling.
Estimated delivery fee Deliver to United States

Economy delivery 10 - 13 business days

Free $6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Aug 30, 2007
Length: 188 pages
Edition : 1st
Language : English
ISBN-13 : 9781847192660
Languages :

What do you get with Print?

Product feature icon Instant access to your digital copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Redeem a companion digital copy on all Print orders
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Estimated delivery fee Deliver to United States

Economy delivery 10 - 13 business days

Free $6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Publication date : Aug 30, 2007
Length: 188 pages
Edition : 1st
Language : English
ISBN-13 : 9781847192660
Languages :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
$199.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts
$279.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total $ 147.97
Joomla! 3 Beginner's Guide Second Edition
$54.99
Learning PHP Data Objects
$43.99
Designing Next Generation Web Projects with CSS3
$48.99
Total $ 147.97 Stars icon

Table of Contents

7 Chapters
Introduction Chevron down icon Chevron up icon
Using PHP Data Objects: First Steps Chevron down icon Chevron up icon
Error Handling Chevron down icon Chevron up icon
Prepared Statements Chevron down icon Chevron up icon
Handling Rowsets Chevron down icon Chevron up icon
Advanced PDO Usage Chevron down icon Chevron up icon
An Advanced Example Chevron down icon Chevron up icon

Customer reviews

Top Reviews
Rating distribution
Full star icon Full star icon Full star icon Full star icon Half star icon 4.2
(10 Ratings)
5 star 50%
4 star 30%
3 star 10%
2 star 10%
1 star 0%
Filter icon Filter
Top Reviews

Filter reviews by




M.Cotten Apr 29, 2015
Full star icon Full star icon Full star icon Full star icon Full star icon 5
A me some read with very practical applications. It was great that author cover multiple DBs and their syntax in most examples.
Amazon Verified review Amazon
Fran Jan 27, 2013
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Im new in PDO, I felt curiosity for this. The book was enough to start to work with it. step by step I learned and in only one day I finished the whole book. Very handy
Amazon Verified review Amazon
George Russell Jun 22, 2016
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Excellent
Amazon Verified review Amazon
Christopher Govender Jan 04, 2015
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Really great for any PHP programmer at beginner and intermediate level.
Amazon Verified review Amazon
Chylle's Jan 11, 2015
Full star icon Full star icon Full star icon Full star icon Full star icon 5
My son loves it!
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the digital copy I get with my Print order? Chevron down icon Chevron up icon

When you buy any Print edition of our Books, you can redeem (for free) the eBook edition of the Print Book you’ve purchased. This gives you instant access to your book when you make an order via PDF, EPUB or our online Reader experience.

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
Modal Close icon
Modal Close icon