Questions tagged [pdo]

PDO (PHP Data Objects) is a data-access abstraction layer (interface) for PHP. It works with most database systems.

PDO provides a data-access abstraction layer, which means that, regardless of which database you're using, you use the same functions to issue queries and fetch data. PDO does not provide a database abstraction; it doesn't rewrite SQL or emulate missing features. You should use a full-blown abstraction layer if you need that facility.

Source — https://php.net/manual/en/intro.pdo.php

Connection

PDO uses a DSN to define the connection to the database. It also has a number of connection options which can help you to fine-tune your PDO instance. Some of these options are worth setting by default. Here is an example:

$dsn = "mysql:host=localhost;dbname=test;charset=utf8";
$opt = array(
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
$pdo = new PDO($dsn,'root','', $opt);

Let's take a closer look at this code:

  • $dsn contains the database driver (mysql), host (localhost), database name (test), and character set (utf8). Of course, these parameters can be replaced with variables as well.
  • After $dsn comes the username and password.
  • The $opt parameter is an array contains configuration options.

It is recommended to set ERRMODE_EXCEPTION as it will let PDO throw exceptions on errors; this mode is the most reliable way to handle PDO errors.
Setting ATTR_DEFAULT_FETCH_MODE is also a good idea. It saves you having to include it with every fetch, making your application code less bloated.

There are many bad examples around telling you to wrap every PDO statement into try..catch - so, I have to make a distinct note:

DO NOT use the try..catch operator just to handle an error message. Uncaught exceptions are already excellent for this purpose, as they will treat PDO errors in just the same way as other PHP errors - so, you can define the behavior using site-wide settings.
A custom exception handler could be added later, but it is not required. For new users especially, it is recommended to use unhandled exceptions, as they are extremely informative, helpful and secure.
More info...

Prepared statements

Prepared statements are one of the main reasons for using PDO.
The way how it works is explained here: How can prepared statements protect from SQL injection attacks? So, here follows the rules of using PDO:

  • Every dynamic data literal has to be represented in a query by either name (:name) or regular placeholder (?).
  • Every query has to be run in 3 (or 4) steps:
    • prepare() - will prepare the query and create a statement object.
    • bindValue() / bindParam() - this is an optional step as variables can be passed directly into execute().
    • execute() - will actually run the query.
    • fetch* - will return the query result in a usable form.

Some rules of thumb:

  • Use named placeholders only if you need a complex query or if you already have an associative array which keys are equal to table field names. Otherwise, regular placeholders are simpler to use.
  • Use "lazy" binding when possible - passing data into execute will dramatically shorten your code.
  • If you don't know if you need bindValue() or bindParam(), go for the former. bindValue() is less ambiguous and has fewer side effects.

So, here is an example:

$id  = 1;
$stm = $pdo->prepare("SELECT name FROM table WHERE id=?");
$stm->execute(array($id));
$name = $stm->fetchColumn();

Getting results

PDO has some extremely handy methods to return the query result in different formats:

  • fetch() - a general purpose fetch method similar to mysql_fetch_array().
  • fetchAll() to get all the rows without while loop.
  • fetchColumn() to get a single scalar value without getting an array first.

fetchAll() is a very handy function when you make yourself familiar with separating business logic from presentation logic. It lets you get data first and then use it to display:

$stm = $pdo->prepare("SELECT id,name FROM news WHERE dt=curdate()");
$stm->execute();
$data = $stm->fetchAll();

Now we have all the news in the $data array and we can move to presentation part:

?>
<table>
<? foreach ($data as $row): ?>
  <tr>
    <td>
      <a href="news.php?<?=$row['id']?>">
        <?=htmlspecialchars($row['name'])?>
      </a>
    </td>
  </tr>
<? endforeach ?>

Complex cases

Although prepared statements are good things in general, there are some good tips, tricks and pitfalls to know about. First of all, one have to understand that placeholders cannot represent an arbitrary part of the query, but a complete data literal only. Neither part of literal, nor whatever complex expression or a syntax keyword can be substituted with prepared statement.

Here are some typical cases:

PDO Prepared statements and LIKE

Prepare the full literal first:

$name = "%$name%";
$stm  = $pdo->prepare("SELECT * FROM table WHERE name LIKE ?");
$stm->execute(array($name));
$data = $stm->fetchAll();

PDO Prepared statements and LIMIT

When in emulation mode (which is on by default), PDO substitutes placeholders with actual data. And with "lazy" binding (using array in execute()), PDO treats every parameter as a string. As a result, the prepared LIMIT ?,? query becomes LIMIT '10', '10' which is invalid syntax that causes the query to fail.

There are two solutions:

  • Turn emulation off (as MySQL can sort all placeholders out properly).
  • Bind the number explicitly and setting proper type (PDO::PARAM_INT) for this variable.

To turn emulation off, one can run this code (or set in a connection options array):

$conn->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );

Or to bind these variables explicitly with param type:

$stm = $pdo->prepare('SELECT * FROM table LIMIT ?, ?');
$stm->bindParam(1, $limit_from,PDO::PARAM_INT);
$stm->bindParam(2, $per_page,PDO::PARAM_INT);
$stm->execute();
$data = $stm->fetchAll();

PDO Prepared statements and IN

It is impossible to substitute an arbitrary query part using PDO prepared statements. For such cases as the IN() operator, one must create a set of ?s manually and put them into the query:

$arr = array(1,2,3);
$in  = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT * FROM table WHERE column IN ($in)";
$stm = $db->prepare($sql);
$stm->execute($arr);
$data = $stm->fetchAll();

PDO Prepared statements and identifiers.

PDO has no placeholder for identifiers such as database or table names, so a developer must manually format them. To properly format an identifier, follow these two rules:

  • Enclose identifier in backticks.
  • Escape backticks inside by doubling them.

The code would be:

$table = "`".str_replace("`","``",$table)."`";

After such formatting, it is safe to insert the $table variable into query.

It is also important to always check dynamic identifiers against a list of allowed values. Here is a brief example (from How can I prevent SQL injection in PHP?):

$orders  = array("name","price","qty"); //field names
$key     = array_search($_GET['sort'],$orders); // see if we have such a name
$orderby = $orders[$key]; //if not, first one will be set automatically. smart enuf :)
$query   = "SELECT * FROM `table` ORDER BY $orderby"; //value is safe

another example could be found below:

PDO Prepared statements and INSERT/UPDATE query

(from Insert/update helper function using PDO)
A usual PDO-prepared INSERT query statement consists of 2-5 kilobytes of repeated code, with every field name being repeated six to ten times. Instead, we need a compact helper function to handle a variable number of inserted fields. Of course with face control for these fields, to allow only approved fields into query.

The following code is based on the assumption that HTML form field names are equal to SQL table field names. It is also using the unique MySQL feature of allowing SET statements both for INSERT and UPDATE queries:

function pdoSet($fields, &$values, $source = array()) {
  $set = '';
  $values = array();
  if (!$source) $source = &$_POST;
  foreach ($fields as $field) {
    if (isset($source[$field])) {
      $set.="`".str_replace("`","``",$field)."`". "=:$field, ";
      $values[$field] = $source[$field];
    }
  }
  return substr($set, 0, -2); 
}

This function will produce a correct sequence for the SET operator,

`field1`=:field1,`field2`=:field2

to be inserted into query and $values array for execute().
Can be used this way:

$allowed = array("name","surname","email"); // allowed fields
$sql = "INSERT INTO users SET ".pdoSet($allowed,$values);
$stm = $dbh->prepare($sql);
$stm->execute($values);

Or, for a more complex case:

$allowed = array("name","surname","email","password"); // allowed fields
$_POST['password'] = MD5($_POST['login'].$_POST['password']);
$sql = "UPDATE users SET ".pdoSet($allowed,$values)." WHERE id = :id";
$stm = $dbh->prepare($sql);
$values["id"] = $_POST['id'];
$stm->execute($values);
23916 questions
111
votes
6 answers

Installing PDO driver on MySQL Linux server

I was suggested, not long ago, to change my code to use PDO in order to parameterize my queries and safely save HTML in the database. Well, here are the main problems: I looked at http://php.net/manual/en/ref.pdo-mysql.php, and I don't really get…
Yuri Scarbaci
  • 1,475
  • 2
  • 11
  • 13
105
votes
2 answers

Cleansing User Passwords

How should I escape or cleanse user-provided passwords before I hash them and store them in my database? When PHP developers consider hashing users' passwords for security purposes, they often tend to think of those passwords like they would any…
Jay Blanchard
  • 34,243
  • 16
  • 77
  • 119
96
votes
5 answers

How to properly set up a PDO connection

From time to time I see questions regarding connecting to database. Most answers is not the way I do it, or I might just not get the answers correctly. Anyway; I've never thought about it because the way I do it works for me. But here's a crazy…
ThomasK
  • 2,210
  • 3
  • 26
  • 35
94
votes
3 answers

How do I return integer and numeric columns from MySQL as integers and numerics in PHP?

The problem is that a DB query should return integer data types in PHP for integer columns. Instead the query returns every column as a string type. I've ensured that "PDO::ATTR_STRINGIFY_FETCHES" if false just to make sure results aren't being cast…
stephenfrank
  • 2,805
  • 2
  • 18
  • 16
86
votes
4 answers

SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax — PHP — PDO

I've looked through all the other StackOverflow (and google) posts with the same problem, but none seemed to address my problem. I am using PDO and PHP. My code: $vals = array( ':from' => $email, ':to' => $recipient, ':name' =>…
willium
  • 2,048
  • 5
  • 25
  • 34
85
votes
6 answers

How can I properly use a PDO object for a parameterized SELECT query

I've tried following the PHP.net instructions for doing SELECT queries but I am not sure the best way to go about doing this. I would like to use a parameterized SELECT query, if possible, to return the ID in a table where the name field matches the…
Joe Phillips
  • 49,743
  • 32
  • 103
  • 159
85
votes
4 answers

PDO::PARAM for type decimal?

I have 2 database fields `decval` decimal(5,2) `intval` int(3) I have 2 pdo queries that update them. The one that updates the int works ok $update_intval->bindParam(':intval', $intval, PDO::PARAM_INT); but I can't update the decimal field. I've…
dmontain
  • 1,621
  • 4
  • 14
  • 16
84
votes
7 answers

PDO::fetchAll vs. PDO::fetch in a loop

Just a quick question. Is there any performance difference between using PDO::fetchAll() and PDO::fetch() in a loop (for large result sets)? I'm fetching into objects of a user-defined class, if that makes any difference. My initial uneducated…
Lotus Notes
  • 6,302
  • 7
  • 32
  • 47
82
votes
5 answers

How to get a query error from prepare() in PDO PHP?

$st = $db->prepare("SELECT * FROM c6ode"); How can I check the intentional mysql error for the query in above case?
TPSstar
  • 881
  • 1
  • 8
  • 13
82
votes
6 answers

PDO::PARAM for dates?

Does some PDO::PARAM_??? exist which can be used for dates or timestamps? Sample code: $sql = "UPDATE my_table SET current_date = :date WHERE id = 43"; $statement = $pdo->prepare ($sql); $statement->bindValue (":date", strtotime (date ("Y-m-d…
vitto
  • 19,094
  • 31
  • 91
  • 130
80
votes
14 answers

What is the advantage of using try {} catch {} versus if {} else {}

I am switching from plain mysql in php to PDO and I have noticed that the common way to test for errors is using a try / catch combination instead of if / else combinations. What is the advantage of that method, can I use one try / catch block…
jeroen
  • 91,079
  • 21
  • 114
  • 132
76
votes
10 answers

PHP Connection failed: SQLSTATE[HY000] [2002] Connection refused

I am trying to use a PHP connection to connect MySQL Database which is on phpmyadmin. Nothing fancy about the connection just trying to see whether the connection is successful or not. I am using MAMP to host the database, the connection I am trying…
Jonck
  • 1,587
  • 1
  • 10
  • 15
76
votes
8 answers

PDO binding values for MySQL IN statement

I have an issue with PDO that I'd really like to get an answer for after being plagued by it for quite some time. Take this example: I am binding an array of ID's to a PDO statement for use in a MySQL IN statement. The array would be say: $values =…
hd82
  • 763
  • 1
  • 6
  • 4
75
votes
2 answers

Is is possible to set a default PDO fetch mode?

Before I retrieve data I always have to type: $STH->setFetchMode(PDO::FETCH_OBJ); In the interest of making my code more readable it would be great if I could set a default mode somewhere.... Thanks! Edit. I was originally hoping I could add…
Matt
  • 7,022
  • 16
  • 53
  • 66
75
votes
13 answers

PHP 7 RC3: How to install missing MySQL PDO

I am trying to setup webserver with PHP 7 RC3 + Nginx on Ubuntu 14.04 (for test purposes). I installed Ubuntu in Vagrant using ubuntu/trusty64 and PHP 7 RC 3 from Ondřej Surý (https://launchpad.net/~ondrej/+archive/ubuntu/php-7.0). I can not find…
SmxCde
  • 5,053
  • 7
  • 25
  • 45