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
70
votes
12 answers

PDO::__construct(): Server sent charset (255) unknown to the client. Please, report to the developers

I'm trying to connect to a MySQL database from Symfony 3 application. But when trying to create MySQL schema from a Symfony console command I get this error: PDO::__construct(): Server sent charset (255) unknown to the client. Please, report to the…
Napas
  • 2,692
  • 3
  • 28
  • 33
68
votes
4 answers

Are there good tutorials on how to use PDO?

Maybe someone did a tutorial that shows the important thing: Setting everything up and using it with MySQL?
openfrog
  • 40,201
  • 65
  • 225
  • 373
68
votes
4 answers

How to get numeric types from MySQL using PDO?

I'm using PDO and MySQL, for some reason when getting values from the database that are int type, the PDOStatement is returning a string representation of the number and not a value of numeric type. How do I prevent this from happening? I noticed…
victrnava
  • 1,134
  • 1
  • 9
  • 13
67
votes
6 answers

How to bind parameters to a raw DB query in Laravel that's used on a model?

Re, I have the following query: $property = Property::select( DB::raw("title, lat, lng, ( 3959 * acos( cos( radians(:lat) ) * cos( radians( lat ) ) * cos( radians( lng ) -…
MarkL
  • 8,770
  • 7
  • 26
  • 27
63
votes
3 answers

PDO bindParam vs. execute

I often see code using bindParam or bindValue with PDO. Is simply passing arguments to execute frowned upon for any reason? I understand that bindParam actually binds to the variables and that you can set the type of parameter being bound with both…
Explosion Pills
  • 188,624
  • 52
  • 326
  • 405
63
votes
6 answers

Do SQL connections opened with PDO in PHP have to be closed

When I open a MySQL connection in PHP with just PHP's built-in MySQL functions, I do the following: $link = mysql_connect($servername, $username, $password); mysql_select_db($dbname); //queries etcetera mysql_close($link); When I open a connection…
benjy
  • 4,664
  • 7
  • 38
  • 43
61
votes
4 answers

How to squeeze error message out of PDO?

I can't seem to get any error message from PDO: #$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING ); try { $sth = $dbh->prepare('@$%T$!!!'); print_r($sth); print_r($dbh->errorInfo()); } catch (PDOException $e) { echo…
Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
61
votes
27 answers

php artisan migrate throwing [PDO Exception] Could not find driver - Using Laravel

I have a bad experience while installing laravel. However, I was able to do so and move to the next level. I used generators and created my migrations. But when I type the last command php artisan migrate It's throwing a PDOException - could not…
Vamshi Vangapally
  • 1,972
  • 6
  • 21
  • 25
58
votes
4 answers

Why does PDO print my password when the connection fails?

I have a simple website where I establish a connection to a MySQL server using PDO. $dbh = new PDO('mysql:host=localhost;dbname=DB;port=3306', 'USER', 'SECRET', array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET…
The Surrican
  • 29,118
  • 24
  • 122
  • 168
58
votes
4 answers

Get query back from PDO prepared statement

Is there a way to retrieve the query that was used to generate a PDO Prepared statement object?
ChrisR
  • 14,370
  • 16
  • 70
  • 107
57
votes
2 answers

Why does this PDO statement silently fail?

This is my PHP SQL statement and it's returning false while var dumping $sql = $dbh->prepare('INSERT INTO users(full_name, e_mail, username, password) VALUES (:fullname, :email, :username, :password)'); $result = $sql->execute(array( …
Rifthy
  • 701
  • 1
  • 5
  • 9
56
votes
5 answers

Install pdo for postgres Ubuntu

I am trying to enable the pdo driver for my php installation, but when I runn the command ./configure --prefix=/usr/local/webserver/php --with-apxs2=/usr/local/webserver/apache2/bin/apxs --enable-mbstring --enable-intl --with-icu-dir=/usr…
Richard Pérez
  • 1,467
  • 3
  • 15
  • 18
56
votes
7 answers

Persistent DB Connections - Yea or Nay?

I'm using PHP's PDO layer for data access in a project, and I've been reading up on it and seeing that it has good innate support for persistent DB connections. I'm wondering when/if I should use them. Would I see performance benefits in a…
Brian Warshaw
  • 22,657
  • 9
  • 53
  • 72
56
votes
10 answers

Causes of MySQL error 2014 Cannot execute queries while other unbuffered queries are active

My server runs CentOS 6.4 with MySQL 5.1.69 installed using yum with CentOS's repos, and PHP 5.4.16 installed using yum with ius's repos. Edit3 Upgraded to MySQL Server version: 5.5.31 Distributed by The IUS Community Project, and error still…
user1032531
  • 24,767
  • 68
  • 217
  • 387
55
votes
3 answers

Reference — frequently asked questions about PDO

What is this? This is a list of frequently asked questions regarding PHP Data Objects Why is this? As PDO has some features unknown to a regular PHP user, questions regarding prepared statements and error handling in PDO are quite frequent. So,…
Your Common Sense
  • 156,878
  • 40
  • 214
  • 345