(The Only Proper) PDO Tutorial - Treating PHP Delusions PDF
(The Only Proper) PDO Tutorial - Treating PHP Delusions PDF
> PDO
There are many tutorials on PDO already, but unfortunately, most of them fail to explain the
real benefits of PDO, or even promote rather bad practices. The only two exceptions are
phptherightway.com (http://www.phptherightway.com/#pdo_extension) and hashphp.org
(http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers), but they miss a lot of
important information. As a result, half of PDO's features remain in obscurity and are almost
never used by PHP developers, who, as a result, are constantly trying to reinvent the wheel
which already exists in PDO.
Unlike those, this tutorial is written by someone who has used PDO for many years, dug
through it, and answered thousands questions on Stack Overflow (the sole gold PDO badge
bearer (http://stackoverflow.com/help/badges/4220/pdo)). Following the mission of this site
(/), this article will disprove various delusions and bad practices, while showing the right way
instead.
Although this tutorial is based on mysql driver, the information, in general, is applicable for
any driver supported.
Why PDO?
Everyone knows that PDO offers unified interface to access many different databases
(http://php.net/manual/en/pdo.drivers.php). Although this feature is magnificent by itself, it
doesn't make a big deal for the particular application, where only one database backend is
used anyway. And, despite some rumors, it is impossible to switch database backends by
changing a single line in PDO config - due to different SQL flavors (to do so, one needs to
use an averaged query language like DQL (http://doctrine-
orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-
language.html)). Thus, for the average LAMP developer, this point is rather insignificant, and
to him, PDO is just a more complicated version of familiar mysql(i)_query() function.
However, it is not; it is much, much more.
PDO abstracts not only a database API, but also basic operations that otherwise have to be
repeated hundreds of times in every application, making your code extremely WET (Write
Everything Twice). Unlike mysql and mysqli
(https://phpdelusions.net/pdo/mysqli_comparison), both of which are low level bare APIs not
intended to be used directly (but only as a building material for some higher level abstraction
layer), PDO is such an abstraction already. Still incomplete though, but at least usable.
https://phpdelusions.net/pdo#in 2/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Note that although PDO is the best out of native db drivers, for a modern web-application
consider to use an ORM with a Query Builder, or any other higher level abstraction library,
with only occasional fallback to vanilla PDO. Good ORMs are Doctrine, Eloquent, RedBean,
and Yii::AR. Aura.SQL is a good example of a PDO wrapper with many additional features.
Either way, it's good to know the basic tools first. So, let's begin:
Connecting. DSN
database driver , host , db (schema) name and charset , as well as less frequently
used port and unix_socket go into DSN;
username and password go to constructor;
all other options go into options array.
where DSN is a semicolon-delimited string, consists of param=value pairs, that begins from
the driver name and a colon:
mysql:host=localhost;dbname=test;port=3306;charset=utf8mb4
driver^ ^ colon ^param=value pair ^semicolon
Note that it's important to follow the proper format - no spaces or quotes or other
decorations have to be used in DSN, but only parameters, values and delimiters, as
shown in the manual (http://php.net/manual/en/pdo.construct.php).
https://phpdelusions.net/pdo#in 3/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
$host = '127.0.0.1';
$db = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
With all aforementioned variables properly set, we will have proper PDO instance in $pdo
variable.
1. Unlike old mysql_* functions, which can be used anywhere in the code, PDO instance
is stored in a regular variable, which means it can be inaccessible inside functions - so,
one has to make it accessible, by means of passing it via function parameters or using
more advanced techniques, such as IoC container.
2. The connection has to be made only once! No connects in every function. No connects
in every class constructor. Otherwise, multiple connections will be created, which will
eventually kill your database server. Thus, a sole PDO instance has to be created and
then used through whole script execution.
3. It is very important to set charset through DSN - that's the only proper way because it
tells PDO which charset is going to be used. Therefore forget about running SET
NAMES query manually, either via query() or PDO::MYSQL_ATTR_INIT_COMMAND . Only if
your PHP version is unacceptably outdated (namely below 5.3.6), you have to use SET
NAMES query and always turn emulation mode off.
More details regarding Mysql can be found in the corresponding chapter, Connecting to
MySQL (https://phpdelusions.net/pdo_examples/connect_to_mysql)
There are two ways to run a query in PDO. If no variables are going to be used in the query,
you can use the PDO::query() (http://php.net/manual/en/pdo.query.php) method. It will run
your query and return special object of PDOStatement class
(http://php.net/manual/en/class.pdostatement.php) which can be roughly compared to a
resource, returned by mysql_query() , especially in the way you can get actual rows out of it:
https://phpdelusions.net/pdo#in 4/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Also, the query() method allows us to use a neat method chaining for SELECT queries,
which will be shown below.
This is the main and the only important reason why you were deprived from your beloved
mysql_query() function and thrown into the harsh world of Data Objects: PDO has prepared
statements support out of the box. Prepared statement is the only proper way to run a
query, if any variable is going to be used in it. The reason why it is so important is explained
in detail in The Hitchhiker's Guide to SQL Injection prevention (/sql_injection).
So, for every query you run, if at least one variable is going to be used, you have to
substitute it with a placeholder, then prepare your query, and then execute it, passing
variables separately.
Long story short, it is not as hard as it seems. In most cases, you need only two functions -
prepare() (http://php.net/manual/en/pdo.prepare.php) and execute()
(http://php.net/manual/en/pdostatement.execute.php).
First of all, you have to alter your query, adding placeholders in place of variables. Say, a
code like this
will become
or
Note that PDO supports positional ( ? ) and named ( :email ) placeholders, the latter always
begins from a colon and can be written using letters, digits and underscores only. Also note
that no quotes have to be ever used around placeholders.
Having a query with placeholders, you have to prepare it, using the PDO::prepare()
method. This function will return the same PDOStatement object we were talking about
above, but without any data attached to it.
https://phpdelusions.net/pdo#in 5/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Finally, to get the query executed, you must run execute() method of this object, passing
variables in it, in the form of array. And after that, you will be able to get the resulting data out
of statement (if applicable):
As you can see, for the positional placeholders, you have to supply a regular array with
values, while for the named placeholders, it has to be an associative array, where keys have
to match the placeholder names in the query. You cannot mix positional and named
placeholders in the same query.
Please note that positional placeholders let you write shorter code, but are sensitive to the
order of arguments (which have to be exactly the same as the order of the corresponding
placeholders in the query). While named placeholders make your code more verbose, they
allow random binding order.
Also note that despite a widespread delusion, no " : " in the keys is required.
After the execution you may start getting your data, using all supported methods, as
described down in this article.
Binding methods
Passing data into execute() (like shown above) should be considered default and most
convenient method. When this method is used, all values will be bound as strings (save for
NULL values, that will be sent to the query as is, i.e. as SQL NULL ), but most of time it's all
right and won't cause any problem.
However, sometimes it's better to set the data type explicitly. Possible cases are:
LIMIT clause (or any other SQL clause that just cannot accept a string operand) if
emulation mode is turned ON.
complex queries with non-trivial query plan that can be affected by a wrong operand
type
peculiar column types, like BIGINT or BOOLEAN that require an operand of exact type
to be bound (note that in order to bind a BIGINT value with PDO::PARAM_INT you
need a mysqlnd-based installation).
https://phpdelusions.net/pdo#in 6/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
In such a case explicit binding have to be used, for which you have a choice of two
functions, bindValue() (http://php.net/manual/en/pdostatement.bindvalue.php) and
bindParam() (http://php.net/manual/en/pdostatement.bindparam.php). The former one has to
be preferred, because, unlike bindParam() it has no side effects to deal with.
It is very important to understand which query parts you can bind using prepared statements
and which you cannot. In fact, the list is overwhelmingly short: only string and numeric
literals can be bound. So you can tell that as long as your data can be represented in the
query as a numeric or a quoted string literal - it can be bound. For all other cases you cannot
use PDO prepared statements at all: neither an identifier, or a comma-separated list, or a
part of a quoted string literal or whatever else arbitrary query part cannot be bound using a
prepared statement.
Workarounds for the most frequent use cases can be found in the corresponding part of the
article
Sometimes you can use prepared statements for the multiple execution of a prepared query.
It is slightly faster than performing the same query again and again, as it does query parsing
only once. This feature would have been more useful if it was possible to execute a
statement prepared in another PHP instance. But alas - it is not. So, you are limited to
repeating the same query only within the same instance, which is seldom needed in regular
PHP scripts and which is limiting the use of this feature to repeated inserts or updates:
$data = [
1 => 1000,
5 => 300,
9 => 200,
];
$stmt = $pdo->prepare('UPDATE users SET bonus = bonus + ? WHERE id = ?');
foreach ($data as $id => $bonus)
{
$stmt->execute([$bonus, $id]);
}
Note that this feature is a bit overrated. Not only it is needed too seldom to talk about, but
the performance gain is not that big - query parsing is real fast these times.
Note that you can get this advantage only when emulation mode is turned off.
https://phpdelusions.net/pdo#in 7/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Come on folks. There is absolutely nothing special in these queries. To PDO they all the
same. It doesn't matter which query you are running.
Just like it was shown above, what you need is to prepare a query with placeholders, and
then execute it, sending variables separately. Either for DELETE and SELECT query the
process is essentially the same. The only difference is (as DML (Data Manipulation
Language, INSERT, UPDATE and DELETE queries) queries do not return any data), that
you can use the method chaining and thus call execute() right along with prepare() :
However, if you want to get the number of affected rows, the code will have to be the same
boresome three lines:
The most basic and direct way to get multiple rows from a statement would be foreach()
loop. Thanks to Traversable (http://php.net/manual/en/class.traversable.php) interface,
PDOStatement can be iterated over by using foreach() operator:
Note that this method is memory-friendly, as it doesn't load all the resulting rows in the
memory but delivers them one by one (though keep in mind this issue).
We have seen this function already, but let's take a closer look. It fetches a single row from
database, and moves the internal pointer in the result set, so consequent calls to this
function will return all the resulting rows one by one. Which makes this method a rough
analogue to mysql_fetch_array() but it works in a slightly different way: instead of many
separate functions ( mysql_fetch_assoc() , mysql_fetch_row() , etc), there is only one, but
its behavior can be changed by a parameter. There are many fetch modes in PDO, and we
will discuss them later, but here are few for starter:
https://phpdelusions.net/pdo#in 8/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
From the above you can tell that this function have to be used in two cases:
1. When only one row is expected - to get that only row. For example,
$row = $stmt->fetch(PDO::FETCH_ASSOC);
Will give you single row from the statement, in the form of associative array.
2. When we need to process the returned data somehow before use. In this case it have
to be run through usual while loop, like one shown above.
Another useful mode is PDO::FETCH_CLASS , which can create an object of particular class
will produce an array filled with objects of News class, setting class properties from returned
values. Note that in this mode
Note that default mode is PDO::FETCH_BOTH , but you can change it using
PDO::ATTR_DEFAULT_FETCH_MODE configuration option as shown in the connection example.
Thus, once set, it can be omitted most of the time.
Return types.
Only when PDO is built upon mysqlnd and emulation mode is off, then PDO will return int
and float values with respective types. Say, if we create a table
create table typetest (string varchar(255), `int` int, `float` float, `null` int);
insert into typetest values('foo',1,1.1,NULL);
And then query it from mysqlnd-based PDO with emulation turned off, the output will be
https://phpdelusions.net/pdo#in 9/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
array(4) {
["string"] => string(3) "foo"
["int"] => int(1)
["float"] => float(1.1)
["null"] => NULL
}
Otherwise the familiar mysql_fetch_array() behavior will be followed - all values returned
as strings with only NULL returned as NULL .
If for some reason you don't like this behavior and prefer the old style with strings and
NULLs only, then you can use the following configuration option to override it:
$pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
Note that for the DECIMAL type the string is always returned, due to nature of this type
intended to retain the precise value, unlike deliberately non-precise FLOAT and DOUBLE
types.
A neat helper function that returns value of the singe field of returned row. Very handy when
we are selecting only one field:
That's most interesting function, with most astonishing features. Mostly thanks to its
existence one can call PDO a wrapper, as this function can automate many operations
otherwise performed manually.
PDOStatement::fetchAll() returns an array that consists of all the rows returned by the
query. From this fact we can make two conclusions:
1. This function should not be used, if many rows has been selected. In such a case
conventional while loop ave to be used, fetching rows one by one instead of getting
them all into array at once. "Many" means more than it is suitable to be shown on the
average web page.
https://phpdelusions.net/pdo#in 10/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
2. This function is mostly useful in a modern web application that never outputs data right
away during fetching, but rather passes it to template.
You'd be amazed, in how many different formats this function can return data in (and how
little an average PHP user knows of them), all controlled by PDO::FETCH_* variables. Some
of them are:
By default, this function will return just simple enumerated array consists of all the returned
rows. Row formatting constants, such as PDO::FETCH_NUM , PDO::FETCH_ASSOC ,
PDO::FETCH_OBJ etc can change the row format.
Getting a column.
It is often very handy to get plain one-dimensional array right out of the query, if only one
column out of many rows being fetched. Here you go:
Also extremely useful format, when we need to get the same column, but indexed not by
numbers in order but by another field. Here goes PDO::FETCH_KEY_PAIR constant:
Note that you have to select only two columns for this mode, first of which have to be unique.
https://phpdelusions.net/pdo#in 11/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Same as above, but getting not one column but full row, yet indexed by an unique field,
thanks to PDO::FETCH_UNIQUE constant:
Note that first column selected have to be unique (in this query it is assumed that first
column is id, but to be sure better list it explicitly).
PDO::FETCH_GROUP will group rows into a nested array, where indexes will be unique values
from the first columns, and values will be arrays similar to ones returned by regular
fetchAll() . The following code, for example, will separate boys from girls and put them into
different arrays:
https://phpdelusions.net/pdo#in 12/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
So, this is the ideal solution for such a popular demand like "group events by date" or "group
goods by category". Some real life use cases:
Other modes
Although there are several error handling modes in PDO, the only proper one is
PDO::ERRMODE_EXCEPTION . So, one ought to always set it this way, either by adding this line
after creation of PDO instance,
or as a connection option, as demonstrated in the example above. And this is all you need
for the basic error reporting.
https://phpdelusions.net/pdo#in 13/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
TL;DR:
Despite what all other tutorials say, you don't need a try..catch operator to report PDO
errors. Catch an exception only if you have a handling scenario other than just reporting it.
Otherwise just let it bubble up to a site-wide handler (note that you don't have to write one,
there is a basic built-in handler in PHP, which is quite good).
The only exception (pun not intended) is the creation of the PDO instance, which in case of
error might reveal the connection credentials (that would be the part of the stack trace). In
order to hide them, we can wrap the connection code into a try..catch operator and then
throw a new ErrorException that contains only the message but not the credentials.
Despite a widespread delusion, you should never catch errors to report them. A module (like
a database layer) should not report its errors. This function has to be delegated to an
application-wide handler. All we need is to raise an error (in the form of exception) - which
we already did. That's all. Nor should you "always wrap your PDO operations in a
try/catch " like the most popular tutorial from tutsplus recommends. Quite contrary,
catching an exception should be rather an exceptional case (pun intended).
In fact, there is nothing special in PDO exceptions - they are errors all the same. Thus, you
have to treat them exactly the same way as other errors. If you had an error handler before,
you shouldn't create a dedicated one for PDO. If you didn't care - it's all right too, as PHP is
good with basic error handling and will conduct PDO exceptions all right.
Exception handling is one of the problems with PDO tutorials. Being acquainted with
exceptions for the first time when starting with PDO, authors consider exceptions dedicated
to this library, and start diligently (but improperly) handling exceptions for PDO only. This is
utter nonsense. If one paid no special attention to any exceptions before, they shouldn't have
changed their habit for PDO. If one didn't use try..catch before, they should keep with
that, eventually learning how to use exceptions and when it is suitable to catch them.
So now you can tell that the PHP manual is wrong, stating that
(http://php.net/manual/en/pdo.connections.php)
If your application does not catch the exception thrown from the PDO
constructor, the default action taken by the zend engine is to terminate the
script and display a back trace. This back trace will likely reveal the full
database connection details, including the username and password.
However, there is no such thing as "the displaying of a back trace"! What zend engine
really does is just convert an uncaught exception into a fatal error. And then this fatal error is
treated like any other error - so it will be displayed only if appropriate php.ini directive is
set. Thus, although you may or you may not catch an exception, it has absolutely nothing to
https://phpdelusions.net/pdo#in 14/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
ini_set('display_errors', 1);
While on a production server turn displaying errors off while logging errors on:
ini_set('display_errors', 0);
ini_set('log_errors', 1);
keep in mind that there are other errors that shouldn't be revealed to the user as well.
1. If you are writing a wrapper for PDO, and you want to augment the error info with some
additional data, like query string. In this case, catch the exception, gather the required
information, and re-throw another Exception.
2. If you have a certain scenario for handling errors in the particular part of code. Some
examples are:
if the error can be bypassed, you can use try..catch for this. However, do not
make it a habit. Empty catch in every aspect works as error suppression
operator, and so equally evil it is
(http://programmers.stackexchange.com/questions/219788/is-error-suppressing-
bad-practice).
if there is an action that has to be taken in case of failure, i.e. transaction
rollback.
if you are waiting for a particular error to handle. In this case, catch the exception,
see if the error is one you're looking for, and then handle this one. Otherwise just
throw it again - so it will bubble up to the handler in the usual way.
E.g.:
try {
$pdo->prepare("INSERT INTO users VALUES (NULL,?,?,?,?)")->execute($data);
} catch (PDOException $e) {
$existingkey = "Integrity constraint violation: 1062 Duplicate entry";
if (strpos($e->getMessage(), $existingkey) !== FALSE) {
// Take some action if there is a key constraint violation, i.e. duplicate name
} else {
throw $e;
}
}
https://phpdelusions.net/pdo#in 15/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
However, in general, no dedicated treatment for PDO exceptions is ever needed. In short, to
have PDO errors properly reported:
As a result, you will be always notified of all database errors without a single line of extra
code! Further reading (/try-catch).
Although PDO offers a function for returning the number of rows found by the query,
PDOstatement::rowCount() , you scarcely need it. Really.
If you think it over, you will see that this is a most misused function in the web. Most of time it
is used not to count anything, but as a mere flag - just to see if there was any data returned.
But for such a case you have the data itself! Just get your data, using either fetch() or
fetchAll() - and it will serve as such a flag all right! Say, to see if there is any user with
such a name, just select a row:
Exactly the same thing with getting either a single row or an array with rows:
Remember that here you don't need the count, the actual number of rows, but rather a
boolean flag. So you got it.
Not to mention that the second most popular use case for this function should never be used
at all. One should never use the rowCount() to count rows in database! Instead, one has to
ask a database to count them, and return the result in a single row:
https://phpdelusions.net/pdo#in 16/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
In essence:
if you need to know how many rows in the table, use SELECT COUNT(*) query.
if you need to know whether your query returned any data - check that data.
if you still need to know how many rows has been returned by some query (though I
hardly can imagine a case), then you can either use rowCount() or simply call
count() on the array returned by fetchAll() (if applicable).
Thus you could tell that the top answer for this question on Stack Overflow
(http://stackoverflow.com/a/883382/285587/) is essentially pointless and harmful - a call to
rowCount() could be never substituted with SELECT count(*) query - their purpose is
essentially different, while running an extra query only to get the number of rows returned by
other query makes absolutely no sense.
PDO is using the same function for returning both number of rows returned by SELECT
statement and number of rows affected by DML (Data Manipulation Language, INSERT,
UPDATE and DELETE queries) queries - PDOstatement::rowCount() . Thus, to get the
number of rows affected, just call this function after performing a query.
Another frequently asked question is caused by the fact that mysql won't update the row, if
new value is the same as old one. Thus number of rows affected could differ from the
number of rows matched by the WHERE clause. Sometimes it is required to know this latter
number.
Although you can tell rowCount() to return the number of rows matched instead of rows
affected by setting PDO::MYSQL_ATTR_FOUND_ROWS option to TRUE, but, as this is a
connection-only option and thus you cannot change it's behavior during runtime, you will
have to stick to only one mode for the application, which could be not very convenient.
Unfortunately, there is no PDO counterpart for the mysql(i)_info() function which output
can be easily parsed and desired number found. This is one of minor PDO drawbacks.
https://phpdelusions.net/pdo#in 17/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Despite PDO's overall ease of use, there are some gotchas anyway, and I am going to
explain some.
One of them is using placeholders with LIKE SQL clause. At first one would think that such
a query will do:
but soon they will learn that it will produce an error. To understand its nature one has to
understand that, like it was said above, a placeholder have to represent a complete data
literal only - a string or a number namely. And by no means can it represent either a part of a
literal or some arbitrary SQL part. So, when working with LIKE, we have to prepare our
complete literal first, and then send it to the query the usual way:
$search = "%$search%";
$stmt = $pdo->prepare("SELECT * FROM table WHERE name LIKE ?");
$stmt->execute([$search]);
$data = $stmt->fetchAll();
Just like it was said above, it is impossible to substitute an arbitrary query part with a
placeholder. Any string you bind through a placeholder will be put into query as a single
string literal. For example, a string '1,2,3' will be bound as a string, resulting in
To make it right, one needs separated values, to make a query look like
Thus, for the comma-separated values, like for IN() SQL operator, one must create a set of
? s manually and put them into the query:
$arr = [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();
https://phpdelusions.net/pdo#in 18/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
In case there are other placeholders in the query, you could use array_merge() function to
join all the variables into a single array, adding your other variables in the form of arrays, in
the order they appear in your query:
$arr = [1,2,3];
$in = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT * FROM table WHERE foo=? AND column IN ($in) AND bar=? AND baz=?";
$stm = $db->prepare($sql);
$params = array_merge([$foo], $arr, [$bar, $baz]);
$stm->execute($params);
$data = $stm->fetchAll();
In case you are using named placeholders, the code would be a little more complex, as you
have to create a sequence of the named placeholders, e.g. :id0,:id1,:id2 . So the code
would be:
$ids = [1,2,3];
$in = "";
foreach ($ids as $i => $item)
{
$key = ":id".$i;
$in .= "$key,";
$in_params[$key] = $item; // collecting values into key-value array
}
$in = rtrim($in,","); // :id0,:id1,:id2
$sql = "SELECT * FROM table WHERE foo=:foo AND id IN ($in) AND bar=:bar";
$stm = $db->prepare($sql);
$stm->execute(array_merge($params,$in_params)); // just merge two arrays
$data = $stm->fetchAll();
Luckily, for the named placeholders we don't have to follow the strict order, so we can merge
our arrays in any order.
On Stack Overflow I've seen overwhelming number of PHP users implementing the most
fatal PDO code (/pdo/lame_update), thinking that only data values have to be protected. But
of course it is not.
Unfortunately, PDO has no placeholder for identifiers (table and field names), so a developer
must manually format them.
$table = "`".str_replace("`","``",$table)."`";
After such formatting, it is safe to insert the $table variable into query.
For other databases rules will be different but it is essential to understand that using only
delimiters is not enough - delimiters themselves should be escaped.
It is also important to always check dynamic identifiers against a list of allowed values. Here
is a brief example:
Or, extending this approach for the INSERT/UPDATE statements (as Mysql supports SET for
both),
$data = ['name' => 'foo', 'submit' => 'submit']; // data for insert
$allowed = ["name", "surname", "email"]; // allowed fields
$values = [];
$set = "";
foreach ($allowed as $field) {
if (isset($data[$field])) {
$set.="`".str_replace("`", "``", $field)."`". "=:$field, ";
$values[$field] = $data[$field];
}
}
$set = substr($set, 0, -2);
This code will produce the correct sequence for SET operator that will contain only allowed
fields and placeholders like this:
`name`=:foo
as well as $values array for execute() , which can be used like this
Yes, it looks extremely ugly, but that is all PDO can offer.
Another problem is related to the SQL LIMIT clause. When in emulation mode (which is on
by default), PDO substitutes placeholders with actual data, instead of sending it separately.
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 query to fail.
https://phpdelusions.net/pdo#in 20/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
One is turning emulation off (as MySQL can sort all placeholders properly). To do so one can
run this code:
Another way would be to bind these variables explicitly while setting the proper param type:
One peculiar thing about PDO::PARAM_INT : for some reason it does not enforce the type
casting. Thus, using it on a number that has a string type will cause the aforementioned
error:
Transactions
To successfully run a transaction, you have to make sure that error mode is set to
exceptions, and learn three canonical methods:
Exceptions are essential for transactions because they can be caught. So in case one of the
queries failed, the execution will be stopped and moved straight to the catch block, where
the whole transaction will be rolled back.
https://phpdelusions.net/pdo#in 21/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
try {
$pdo->beginTransaction();
$stmt = $pdo->prepare("INSERT INTO users (name) VALUES (?)");
foreach (['Joe','Ben'] as $name)
{
$stmt->execute([$name]);
}
$pdo->commit();
}catch (Exception $e){
$pdo->rollback();
throw $e;
}
There is one thing about stored procedures any programmer stumbles upon at first: every
stored procedure always returns one extra result set: one (or many) results with actual data
and one just empty. Which means if you try to call a procedure and then proceed to another
query, then "Cannot execute queries while other unbuffered queries are active" error
will occur, because you have to clear that extra empty result first. Thus, after calling a stored
procedure that is intended to return only one result set, just call
PDOStatement::nextRowset() (http://php.net/manual/en/pdostatement.nextrowset.php) once
(of course after fetching all the returned data from statement, or it will be discarded):
While for the stored procedures returning many result sets the behavior will be the same as
with multiple queries execution:
https://phpdelusions.net/pdo#in 22/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
However, as you can see here is another trick have to be used: remember that extra result
set? It is so essentially empty that even an attempt to fetch from it will produce an error. So,
we cannot use just while ($stmt->nextRowset()) . Instead, we have to check also for
empty result. For which purpose PDOStatement::columnCount() is just excellent.
This feature is one of essential differences between old mysql ext and modern libraries: after
calling a stored procedure with mysql_query() there was no way to continue working with
the same connection, because there is no nextResult() function for mysql ext . One had
to close the connection and then open a new one again in order to run other queries after
calling a stored procedure.
Calling a stored procedure is a rare case where bindParam() use is justified, as it's the only
way to handle OUT and INOUT parameters. The example can be found in the corresponding
manual chapter (http://php.net/manual/en/pdo.prepared-statements.php#example-1009).
However, for mysql it doesn't work. You have to resort to an SQL variable and an extra call
(http://stackoverflow.com/a/23749445/285587).
Note that for the different databases the syntax could be different as well. For example, to
run a sored procedure against Microsoft SQL server, use the following format
where ? marks are placeholders. Note that no braces should be used in the call.
When in emulation mode, PDO can run mutiple queries in the same statement, either via
query() or prepare()/execute() . To access the result of consequent queries one has to use
PDOStatement::nextRowset() (http://php.net/manual/en/pdostatement.nextrowset.php):
Within this loop you'll be able to gather all the related information from the every query, like
affected rows, auto-generated id or errors occurred.
https://phpdelusions.net/pdo#in 23/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
It is important to understand that at the point of execute() PDO will report the error for the
first query only. But if error occurred at any of consequent queries, to get that error one has
to iterate over results. Despite some ignorant opinions (https://bugs.php.net/bug.php?
id=61613), PDO can not and should not report all the errors at once. Some people just
cannot grasp the problem at whole, and don't understand that error message is not the only
outcome from the query. There could be a dataset returned, or some metadata like insert id.
To get these, one has to iterate over resultsets, one by one. But to be able to throw an error
immediately, PDO would have to iterate automatically, and thus discard some results.
Which would be a clear nonsense.
Unlike mysqli_multi_query() PDO doesn't make an asynchronous call, so you can't "fire
and forget" - send bulk of queries to mysql and close connection, PHP will wait until last
query gets executed.
Both methods has their drawbacks and advantages but, and - I have to stress on it - both
being equally secure, if used properly. Despite rather appealing tone of the popular article
on Stack Overflow (http://stackoverflow.com/a/12202218/285587), in the end it says that if
you are using supported versions of PHP and MySQL properly, you are 100% safe. All
you have to do is to set encoding in the DSN, as it shown in the example above, and your
emulated prepared statements will be as secure as real ones.
Note that when native mode is used, the data is never appears in the query, which is
parsed by the engine as is, with all the placeholders in place. If you're looking into Mysql
query log for your prepared query, you have to understand that it's just an artificial query that
has been created solely for logging purpose, but not a real one that has been executed.
https://phpdelusions.net/pdo#in 24/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
one can use a handy feature of named prepared statements - a placeholder with same name
could be used any number of times in the same query, while corresponding variable have to
be bound only once. For some obscure reason this functionality is disabled when emulation
mode is off:
$stmt = $pdo->prepare("SELECT * FROM t WHERE foo LIKE :search OR bar LIKE :search");
$stmt->execute(['search'] => "%$search%");`
Also, when emulation is ON , PDO is able to run multiple queries in one prepared statement.
Also, as native prepared statements support only certain query types, you can run some
queries with prepared statements only when emulation is ON . The following code will return
table names in emulation mode and error otherwise:
One could bother not with parameter types, as mysql will sort all the types properly. Thus,
even string can be bound to LIMIT parameters, as it was noted in the corresponding chapter.
Also, this mode will allow to use the advantage of single prepare-multiple execute feature.
It's hard to decide which mode have to be preferred, but for usability sake I would rather turn
it OFF , to avoid a hassle with LIMIT clause. Other issues could be considered negligible in
comparison.
Recently all PHP extensions that work with mysql database were updated based on a low-
level library called mysqlnd , which replaced old libmysql client. Thus some changes in the
PDO behavior, mostly described above and one that follows:
When using libmysqlclient as library PHP's memory limit won't count the
memory used for result sets unless the data is fetched into PHP variables.
With mysqlnd the memory accounted for will include the full result set.
https://phpdelusions.net/pdo#in 25/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
The whole thing is about a resultset, which stands for all the data found by the query.
When your SELECT query gets executed, there are two ways to deliver the results in your
script: buffered and unbuffered one. When buffered method is used, all the data returned by
the query gets copied in the script's memory at once. While in unbuffered mode a
database server feeds the found rows one by one.
So you can tell that in buffered mode a resultset is always burdening up the memory on the
server even if fetching weren't started at all. Which is why it is not advisable to select huge
datasets if you don't need all the data from it.
Nonetheless, when old libmysql-based clients were used, this problem didn't bother PHP
uers too much, because the memory consumed by the resultset didn't count in the the
memory_get_usage() and memory_limit .
But with mysqlnd things got changed, and the resultset returned by the buffered query will be
count towards both memory_get_usage() and memory_limit , no matter which way you
choose to get the result:
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, FALSE);
$stmt = $pdo->query("SELECT * FROM Board");
$mem = memory_get_usage();
while($row = $stmt->fetch());
echo "Memory used: ".round((memory_get_usage() - $mem) / 1024 / 1024, 2)."M\n";
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, TRUE);
$stmt = $pdo->query("SELECT * FROM Board");
$mem = memory_get_usage();
while($row = $stmt->fetch());
echo "Memory used: ".round((memory_get_usage() - $mem) / 1024 / 1024, 2)."M\n";
which means that with buffered query the memory is consumed even if you're fetching
rows one by one!
So, keep in mind that if you are selecting a really huge amount of data, always set
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY to FALSE .
1. With unbuffered query you can't use rowCount() method (which is useless, as we
learned above)
2. Moving (seeking) the current resultset internal pointer back and forth (which is useless
as well).
https://phpdelusions.net/pdo#in 26/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
1. While an unbufferered query is active, you cannot execute any other query using the
same connection, so use this mode wisely.
RELATED ARTICLES:
> Simple yet efficient PDO wrapper (/pdo/pdo_wrapper)
GOT A QUESTION?
I am the only person to hold a gold badge in
(http://stackoverflow.com/help/badges/4220/pdo),
(http://stackoverflow.com/help/badges/6342/mysqli) and
(http://stackoverflow.com/help/badges/5981/sql-injection) on Stack Overflow and I am
eager to show the right way for PHP developers.
Besides, your questions let me make my articles even better, so you are more than
welcome to ask any question you got.
LATEST ARTICLE:
PDO Examples (/pdo_examples)
SEE ALSO:
> Top 10 PHP delusions (/top)
https://phpdelusions.net/pdo#in 27/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
LATEST COMMENTS:
12.11.18 20:26
Harachi for (The only proper) PDO tutorial: (/pdo#comment-583)
I'm answering Zeef's post for having done ANSI C development not too long ago. The
syntax is the...
read more (/pdo#comment-583)
12.11.18 20:12
Harachi for (The only proper) PDO tutorial: (/pdo#comment-582)
Oups, Sorry for not doing block of code
read more (/pdo#comment-582)
12.11.18 20:09
Harachi for (The only proper) PDO tutorial: (/pdo#comment-581)
Hello, thank you for this excellent articles that filled a lot of my shortcomings.
unfortunately...
read more (/pdo#comment-581)
11.11.18 23:57
John for Your first database wrapper's childhood diseases:
(/pdo/common_mistakes#comment-580)
Hey, sorry for being brief I actually thought it wasn't going through as last time I
couldn't...
read more (/pdo/common_mistakes#comment-580)
11.11.18 18:08
John for Your first database wrapper's childhood diseases:
(/pdo/common_mistakes#comment-579)
https://phpdelusions.net/pdo#in 28/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Nov 8, 2018
Add a comment
Your name
Message
https://phpdelusions.net/pdo#in 29/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Address
If you want to get a reply from admin, you may enter your E—mail address above
Send
Comments:
Harachi, 12.11.18 20:26
I'm answering Zeef's post for having done ANSI C development not too long ago. The
syntax is the same in CLI PDO that it is for MySQL or MariaDB! So there are no lines
of codes to modify for this to work! If it does not work, there are syntax errors!
Hello, thank you for this excellent articles that filled a lot of my shortcomings.
unfortunately there is still a lot and if you want to fill another ... :) I am stuck in my
development because I can not use the min and max functions in a select with a limit.
Visibly, 'limit' is incompatible with these 2 functions but there may be a trick ... here is
my code: $query = 'SELECT MAX(price) AS Pmax , MIN(price) AS Pmin FROM book
ORDER BY timestamp DESC LIMIT 0,10'; $sql = sql_pdo_query($connexion,
$query); $row = $sql->fetch(PDO::FETCH_ASSOC); $Pmax = $row['Pmax']; $Pmin =
https://phpdelusions.net/pdo#in 30/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
$row['Pmin']; echo $Pmin.' '. $Pmax; I also tested with this code: $Pmax =
(float)current($connexion->query("SELECT price FROM book ORDER BY timestamp
DESC LIMIT 0,10")->fetch()); $Pmin = (float)current($connexion->query("SELECT
price FROM book ORDER BY timestamp ASC LIMIT 0,10")->fetch()); echo 'Pmax =
'.$Pmax .' '. 'Pmin = '.$Pmin; with this code I would like the min and the max of the last
10 records of my database but obviously as soon as 'limit' is used the returned values
are not good! Do you have a way of doing that works please? Thank you for your help
Hello, certainly off topic but I start ... Already, very good article on PDO, I learned a lot.
My problem is that I have to resume a development written in C CLI that I have to
adapt to the PDO. Of course I can not find any documentation. Would you be so kind
as to indicate a URL or can I find this information?
int readconfig ()
{
MYSQL mysql;
mysql_init (& mysql);
mysql_options (& mysql, MYSQL_READ_DEFAULT_GROUP, "option");
if (mysql_real_connect (& mysql, mysql_host, mysql_login, mysql_pwd, mysql_db
, 0, NULL, 0)) {
mysql_query (& mysql, "SELECT * FROM Netwho");
MYSQL_RES * result = NULL;
MYSQL_ROW row;
result = mysql_use_result (& mysql);
while ((row = mysql_fetch_row (result))) {
Tconsign_p = (float) atof (row [0]);
Tconsign_c = (float) atof (row [1]);
}
mysql_free_result (result);
mysql_close (& mysql);
} else {
syslog (LOG_INFO, "ERROR");
}
return 0;
}
With my thanks
REPLY:
Hello Zeef!
That's a standard Mysql C API. To rewrite it to PHP/PDO, you basically need this:
https://phpdelusions.net/pdo#in 31/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
note that the function is called with a parameter $pdo, which should be a valid PDO
instance (https://phpdelusions.net/pdo_examples/connect_to_mysql) created before.
Please accept my gratitude for your kindness and willingness to take questions and
sacrifice your time for me and a host of others. My question: In the Catch Block,
following the $pdo instantiation, a backslash preceded the PDOException. What role
is the slash performing there?
REPLY:
Hello Adolf!
It does not affect your code if you don't use namespaces but prevents an
error/confusion if you do. Basically all PHP built-in classes are in the root namespace,
so just a single backslash means - the root namespace.
https://phpdelusions.net/pdo#in 32/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
REPLY:
Hello moose!
I am a novice. I want to build a scoring application for a 'cricket' sports match which
will involve a lot of real time inserting recalculation and updating and outputting. I've
set up WampServer, I know I can build the Database and tables. Because of all the
https://phpdelusions.net/pdo#in 33/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
good things I've heard about PDO and because I will need to be connected to Mysql
for about six hours straight, My question is, Do you think that this is the best or a good
way to approach such a project. (Software wise)?
REPLY:
Hello Stephen!
Please note that unless you are using websockets, there won't be a constant
connection to a database. By default, PHP processes are atomic, a request initiated
from a browser makes a php script run, return the requested data, and die. So it
connects to a database every time. A very good and simple model.
Great stuff, but... I've posted a question at stackexchange for "PHP 7.2.8 PDO Fails to
Connect to MySQL 8.0.12" that you may be interested in:
https://serverfault.com/questions/924367/php-7-2-8-pdo-fails-to-connect-to-mysql-8-0-
12-ga-in-aws-ec2-lemp-stack (https://serverfault.com/questions/924367/php-7-2-8-
pdo-fails-to-connect-to-mysql-8-0-12-ga-in-aws-ec2-lemp-stack)
In short, I used your advice above to write code more than a year ago, that has
worked great with PHP 7.0 and MySQL 5.7. But though all other connections that I
can think of to this MySQL work, PHP will not connect. Have tried many things so far.
Wondering if you will have better ideas.
Thanks much.
REPLY:
Hello Jeff!
Glad you got your case resolved. I had no idea of this issue and had nothing to offer
but now I see it was resolved.
This is tutorial is superb. I would advice that you compile this into a book.
https://phpdelusions.net/pdo#in 34/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
When is it OK NOT to use parameter binding, and when should you REALLY use it?
Use when there is user input, or data being passed from one page to another (PHP)?
Don't need to use when getting data within the same code page without any input
except for the code itself? Would appreciate our opinion. Thanks!
REPLY:
Hello Henry!
Thank you for the good question. There is indeed too much misunderstanding in the
matter. So let's make it straight:
for every query you run, if at least one variable is going to be used,
you have to substitute it with a placeholder, then prepare your
query, and then execute it, passing variables separately
Notice that there is not a single condition you mentioned in your comment like "user
input", "data within the same code page" and such. Simply because all these terms
are essentially vague and uncertain. There is no certain definition for "user input".
Different people take it differently, with catastrophic results. The moment you start
separating the seep from the goat, the "clean" data from "unclean", the moment you
are making your first step to disaster.
Remember, every application evolves over time. And something that has been
hardcoded for ten years, one day becomes dynamically changed and dangerous.
Why take chances? Why base your decision empty musings instead of following one
simple rule - any variable that goes into query should be substituted with a
placeholder, regardless of the source.
https://phpdelusions.net/pdo#in 35/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
<div class="col-md-3">
<form class="form-group" method="POST" enctype="multipart/data-form">
<label for="avatar">Upload Avatar</label>
<input type="file" name="avatar" class="form-control" required>
<p class="help-block">we accepted 2 mb file only!!</p>
<input type="submit" name="upload" value="Upload Avatar">
for transaction
if(isset($_POST['upload'])){
$file = $_FILES["avatar"]["tmp_name"];
$dir = "upload/";
$path = $dir.$file;
if(move_uploaded_file($path, $dir)){;
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$user_id = (int)$_SESSION['namauser'];
$insert_photo = $db->query("SELECT * FROM member WHERE namauser = '" . $_SESSI
ON['namauser'] . "'");
$sql = $db->query("INSERT INTO avatar VALUES ('0', '$path', '$user_id', NOW
())");
}else{
echo '<script type="text/javascript">alert("Sorry, the data cannot be processe
d");</script>';
}
}
</form>
<div class="col-md-6">
<?php
$ambilphoto = $db->query("SELECT * FROM avatar");
$photo = $ambilphoto->fetch(PDO::FETCH_ASSOC);
?>
<h2 class="text-center text-muted">Apakah ini photo anda?</h2>
<img src="<?=$photo['avatar']?>" class="img-responsive" style="width:5
0%; height:auto;">
</div>?>
I have already tried googling anywhere but has no result, when i remember i have
bookmarked your page, then I open it. I cannot upload my photo with this.
REPLY:
Hello Erik!
https://phpdelusions.net/pdo#in 36/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
There could be other issues as well. But it makes very little sense in posting a code
and asking what is wrong with it. Humans are not intended to run a code mentally, it's
a job for a computer. So you have to run your code, make sure that all possible error
reporting is on and start debugging. To tell you truth, debugging is the main
occupations for any programmer. So it is not writing a program that takes most time
but finding out why doesn't it work.
$id_user =(int)$_SESSION['id'];
$userdata = $db->prepare("SELECT * FROM member WHERE id = ?");
$userdata->execute([$_SESSION['id']]);
$usr = $userdata->fetch(PDO::FETCH_ASSOC);
<tr>
<td><?=$usr['id']?></td>
<td><?=$usr['usernama']?></td>
<td><?=$usr['nomorhp']?></td>
<td><?=$usr['email']?></td>
<td><?=$usr['password']?></td>
<td><?=$usr['kelas']?></td>
<td><?=$usr['jurusan']?></td>
<td><?=$usr['kabupaten']?></td>
</tr>?>
I've used to your code given, but I do beg your pardon, I cannot see the
data I need, I just see white screen whit table head data.
REPLY:
This code should work, given there is a closing PHP tag ?> between PHP and HTML
code:
https://phpdelusions.net/pdo#in 37/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
<?php
$id_user =(int)$_SESSION['id'];
$userdata = $db->prepare("SELECT * FROM member WHERE id = ?",'$id_user');
$userdata->binparam(':$_SESSION['id']',?);
$userdata->execute();
$usr = $userdata->fetch(PDO::FETCH_ASSOC);
?>
<tr>
<td>
<?=$usr['user data'] //how to fetch whole user (login) data here?'
?>
</td>
</tr>
</tbody>
</table>
</div>?>
REPLY:
Hello Erik!
$id_user =(int)$_SESSION['id'];
$userdata = $db->prepare("SELECT * FROM member WHERE id = ?");
$userdata->execute([$_SESSION['id']]);
$usr = $userdata->fetch(PDO::FETCH_ASSOC);
Then you can display all the user data that is already fetched. You need to know the
column names in order to display them though.
For example, if there is a name column, you can display it using $usr['name'] and so
on.
What a fantastic article. Very helpful, packed full of information! Many thanks.
https://phpdelusions.net/pdo#in 38/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Hi, I was wondering if you could do a simple client/server pdo api example? I have
been unable to find an examples/tutorials for a simple one. This should be geared
towards having an application log data and then submit it either php or json/js to a
remote DB site. I have made it work without an api, and with a simple php api .
However I fail to be able to do this with PDO. It is not as simple as one would think.
REPLY:
Hello Johnny!
You are probably mistaking PDO with something else. PDO is simply a database API,
it knows nothing of json/js or application log data. It belongs to that "remote DB site"
only. How this site is called is of no PDO's concern. In short, PDO is just a function to
run your SQL query against a database server, no more.
Such a client/server pdo api example simply doesn't exist. The only server PDO talks
to is a database server. Whereas any API is always implemented in PHP, which, in
turn, uses PDO to query a database.
This resource you have come up with is extremely helpful, especially the parts where
you explain where prepared statements can be used.
In the transaction section when you make mention of the rollback function, is it not
meant to be in camel case as shown in the documentation?
(http://php.net/manual/en/pdo.rollback.php
(http://php.net/manual/en/pdo.rollback.php))
Have you considered writing articles on design patterns and or generic articles
regarding writing smarter code in general?
REPLY:
Hello!
https://phpdelusions.net/pdo#in 39/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Although it could make sense to use a camel case to separate different words in the
method name, "rollback" is the established term by itself in a database word and
could be count as a whole word, thus making it unnecessary to use a camel case in it.
Does this statement prevent sql injection or does it turn emulation mode off??
setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
REPLY:
Hello Marvin!
It seems as though there are multiple ways to fix this, however I'm really curious what
YOU would recommend. There are so many differing opinions online, however yours
is the only one I actually trust, so I thought I would ask you directly.
Changing all fetch() instances to fetchAll() (which would require some additional
code re-writing)
If only using MySQL, enable MYSQL_ATTR_USE_BUFFERED_QUERY (it
seems like this fix might not work 100% of the time)
Using closeCursor after fetch() calls
https://phpdelusions.net/pdo#in 40/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Are there any of these methods that you prefer? Or NOT prefer?
REPLY:
Hello Greg!
Most of time you should never see such an error at all. The only plausible case could
be caused by a stored procedure, and should be treated as explained in the
corresponding chapter.
If you have a certain scenario in mind, please share, I'll try to sort it out
If value is 0, this code always do default action. How to select only one column a
check it when this column is 0?
REPLY:
Hello eFko!
If I get you right, instead of if($data) you can test for FALSE value with strict
comparison in the first condition:
That was it! I closed the <input> tag too soon. I also needed to remove id= for every
input field, including the submit button, which I changed from type="button" to
type="submit". Now everything works! I as able to insert into select fields in the table,
not all fields. Thanks so much for your help!
https://phpdelusions.net/pdo#in 41/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
You are not showing an INSERT example and I really need help using a FORM and
getting the info from the form into the db. AMPPS says I'm using php7 but my exports
say it's 5.6.37. Here's my info and using php tags:
include "localpdo.php";
if (isset($_POST['insert'])) {
// Prepare the SQL query
$stmt = $conn->prepare("INSERT INTO timesheet (vol_id, flag) VALUES(:vol_id,:f
lag)");
$stmt->bindParam('vol_id', $_POST['vol_id'], PDO::PARAM_INT);
$stmt->bindParam('flag', $_POST['flag'], PDO::PARAM_STR);
$stmt->execute();
} else { // }
- - -
<form name="form1" method="post" action="">
<input type="text" name="vol_id" id="vol_id">
<?php if (isset($_POST['vol_id']) === true) { echo 'value="', $_POST['vol_
id'], '"'; } ?>
<input type="text" name="flag" id="flag">
<?php if (isset($_POST['flag']) === true) { echo 'value="', $_POST['flag'
], '"'; } ?>
<input name="insert" id="insert" type="button" value="Start Time">
</form>?>
I have two TIMESTAMP fields for start/end times and an AI id field which auto-fill with
each new record so I don't insert into them. Can you see what I'm doing wrong and
help me? Thanks.
REPLY:
Hello1 tharring!
There is nothing wrong with your insert. Its HTML form which is at least wrong. You
are closing an HTML tag too early should be like
https://phpdelusions.net/pdo#in 42/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Hi there, firstly thanks a lot for the article. I have a question of my own.
Thanks.
REPLY:
Hello James!
Although there are ways to do that, I would advise against. There is no point in
stuffing as much queries in one statement as possible. Prepared statements are
exactly for that purpose. Just prepare your UPDATE statement once and then execute
it in a loop, like it shown in this article: https://phpdelusions.net/pdo#multiexec
(https://phpdelusions.net/pdo#multiexec)
If your updates will run unexpectedly slow, then it's your database settings to blame.
To fix that, wrap your updates in a single transaction.
Hello I wish to resume coding after more than 2 years without practice. now i want to
realize my personal projects with php mvc in a first so I want to understand how to
start! thank you!
REPLY:
Hello Armel!
https://phpdelusions.net/pdo#in 43/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Thanks for your reply. I followed your guide for beginners and implemented the
logging-errors advice. So far, the best approach :-)
How can i use procedures with FOREACH, how can i replace the while with foreach,
implementing the empty result prevention, or is more efficiente use while instead?,
because i like work with foreach
REPLY:
Hello Victor!
It is not very clear what you are asking, but in general there is no difference between
using foreach and while.
In both cases you can just iterate over the statement. In case there is no result, then
there simply will be no output:
https://phpdelusions.net/pdo#in 44/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
In case you want to know if there is any result beforehand, just fetch the resulting
dataset using fetchAll():
$data = $stmt->fetchAll();
if ($data){
foreach ($data as $row) {
echo $row['name'];
}
} else {
echo "No data";
}
Hi, Very good tutorial, thanks for sharing. My production database server went down
and when the app tried to connect to it, the error report threw back the database
connection details. Not nice. How would you recommend to handle this error? Thx
REPLY:
Hello Michael!
On a live server PHP must be configured to log errors instead of displaying them, so it
would never happen again to leak your database credentials outside. You may find
useful my article on the PHP error reporting configuration:
https://phpdelusions.net/articles/error_reporting
(https://phpdelusions.net/articles/error_reporting)
Feel free to drop a line if something is still unclear or you disagree with the proposed
solution
https://phpdelusions.net/pdo#in 45/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
it is safe ? thx
REPLY:
Hello Gio!
Yes, that's perfectly safe. PDO witll either correctly format a parameter for you (if
emulation mode is turned on) or even send it completely separated from the query,
and so there would be no way for it to interfere.
Beware that bitfields in MySQL suffer from the same problem that terms in the LIMIT
clause do when using prepared statements, i.e. in emulation mode, strings are not
correctly interpreted. So if you are converting code like mysql_query("create table foo
(bits bit(48))"); mysql_query("insert into foo (bits) values ($val)"; into $pdo-
>prepare("insert into foo (bits) values (?)")->execute([64]); you will actually get the
value 13876 inserted (0011 0110 0011 0100) which is the bytes of the ASCII
characters '6' and '4'.
To fix this, either turn off emulation, or use bindParam as described in the section on
LIMIT, or change your code to $pdo->prepare("insert into foo (bits) values (cast(? as
decimal))")->execute([64]); Something for the next version of this page perhaps?
https://phpdelusions.net/pdo#in 46/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
these statements seem contradictory to me. can you please explain how to properly
connect to a database with pdo? I am using oop php.
thanks, jeff
REPLY:
Hello Jeff!
When you assign an object to a variable, there remains just a single object, whereas
the second variable being just a reference to it. So when you are sending a PDO
instance into a class' constructor, then it's actually a reference, that points to the same
PDO instance. So the initial PDO object is not gets duplicated but remains essentially
a single instance.
So you can tell that the former statement doesn't violate the rule from the latter but
rater gets along with it
T G, 26.12.17 00:33
https://phpdelusions.net/pdo#in 47/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Great Column!
$data = ['name' => 'foo', 'submit' => 'submit']; // data for insert
$allowed = ["name", "surname", "email"]; // allowed fields
$values = [];
$set = ""; foreach ($allowed as $field) {
if (isset($data[$field])) {
$set.="".str_replace("", "`", $field)."". "=:$field, ";
$values[$field] = $data[$field];
}
}
$set = substr($set, 0, -2);
Why are you escaping the field names. Should the $allowed whitelist already handle
any SQL injection risk?
REPLY:
Hello T G!
Yeah, that's an awkward moment. On the one hand, you don't have to escape a
whitelisted value as nobody in their right mind would use a backtick in the field name.
On the other hand, however, if you take the formatting routine as an abstract one,
unaware of the data source, it would be natural to do the full processing, both quoting
and escaping - just like any good quoting function should do.
I think it would be better to move the full quoting routine into a distinct function that will
be doing the correct formatting regardless. I suppose it will arise less questions. What
do you think?
Hi and thanks for this great information. I have learnt a lot about PDO and - at least as
useful - about a consistent approach to error handling! I also like your style of writing.
Thanks mucho!
REPLY:
Hello Jayenn!
https://phpdelusions.net/pdo#in 48/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Thank you for your kind words and especially for mentioning the error handling stuff! I
am nor sort of crusade in changing the current state of error handling in PHP.
Feel free to check back if you have any problem though - any real life use case can
make my articles batter for everyone.
I have a pdo search engine using MYSQL database. How can I get the number of
search results per "MYSQL field"?
REPLY:
Hello!
Just run a
It appears that adding a code block does not work. I just tried submitting another
comment with code, but it did not appear as I expected.
I submitted a comment a few days ago about escaping operands for the LIKE
operator. Do you think it may have been sent to a spam folder?
REPLY:
Hello Jason!
There is no spam folder, but it could have been an error on the site.
https://phpdelusions.net/pdo#in 49/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Escaping special characters used for the LIKE operator is not specific to PDO, it's
rather related to the particular database. But probably I should add a mention at least
for MySQL.
I was talking with another programmer and hes was complaining about SQL query not
working (He has not the use to test his queries on the database before coding them
on PHP). And he said :
-- It would be usefull to be able to directely see the query made by PDO to the
database.
-- you mean with the values of binded ?
-- Yes !
-- For debugging ?
-- Of course, what kind of programmer do you take me for ?
I searched I stumbled on you (Your articles are wonderfully readable by the way).
REPLY:
Hello Vincent!
Although it is often useful to have a query from PDO with all the data interpolated, I
would rather use it for the profiling.
Whereas when a query is not working, all you need as an error message from a
database, that will tell you precisely, what is the problem. Note that you should be
using prepared statements to avoid silly syntax errors caused by the data - it goes
without saying.
That said, there are several code snippets and packages that can get you raw SQL
from a prepared query, just google for "pdo interpolated query" or "PDO raw query".
I have a simple loop that runs a small number of update statements if there's only one
in the loop it works fine more than one and only the first works, with no errors reported
I'm confused!!
https://phpdelusions.net/pdo#in 50/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
REPLY:
Hello Mike!
Dunno why did you cut off the part after WHERE but it's impossible to tell anything
without seeing the actual code, sorry.
How frustrating. I just tested with PostgreSQL, but it uses single quotes so it worked
there. One wonders why in so many years, such a basic and necessary feature is not
there (found a thread from 2008 with people complaining about this, sayng it's years
overdue). I just migrated from MDB2 because of it's unfinished state and difficult
install method and chose PDO. One of my tables matches one of MySQL's many
reserved words, so I must quote it and I was using quoteidentifier from MDB2. I see
Zend Engine has it, but that means a large dependency for a single quoting function.
Seems I'll have to roll up my sleeves. Thanks for the heads up, it could have gone
unnoticed otherwise!
Nice rundown, much better than overly complex tutorials. However, identifier quoting
does exist in PDO (didn't back then?).
https://phpdelusions.net/pdo#in 51/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
$pdo->quote('mytablename'); will take care of any strange quotes. Keep in mind only
MySQL uses backticks, so using this is better if you want to keep it a little more DB-
agnostic.
REPLY:
Hello Jorge!
Dear Colonel, I greatly appreciate this website and the common sense you share on
Stack Overflow. May I ask you, please, if you have time, to take a look at a
conundrum I have posted on SO?
(https://stackoverflow.com/questions/47278348/php-output-echoed-out-of-order
(https://stackoverflow.com/questions/47278348/php-output-echoed-out-of-order)). I
understand if that's not possible for you. Best wishes for the future
REPLY:
Hello Emma!
I see it has been happily resolved already. Glad you got it working and thank you for
your kind words.
Oh I typed in my second thought before I saw that you had responded to my question.
Looks like we came to the same conclusion,
https://phpdelusions.net/pdo#in 52/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Ty
REPLY:
Hello Tyler!
Thank you for the good question. I should have added it to the article. And especially
thank you for asking. It's always better to ask than devise something of your own.
Yes, there is a way to ask a database to count on its side in this case as well. Just
wrap your query into another query, like
SELECT count(*) FROM (SELECT 1 FROM ... here goes the rest of your query)
The idea is to wrap your query into another, and use it to count results. For this
purpose we are selecting just a constant value "1" instead of the actual fields in your
main query, just to make a database to do less job.
https://phpdelusions.net/pdo#in 53/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
In testing I notice that if you use sql statements containing LIKE in combination with
$_GET it is possible to inject % into the statement and retrieve all the data from a
table. e.g. if I do not escape the % this retrieves all users. http://example.php?
first_name (http://example.php?first_name)=%
but I can't find any mention of it in your more extensive documentation. I have to say
that I did not understand the code example you use to explain how to use 'like' with
pdo.
Thanks, John
REPLY:
Hello John.
1. if your code inside example.php is not intended for the substring search, then
you should NOT use LIKE in the first place.
2. if your code inside example.php is intended for the substring search, then it
means that the entire table contents is already exposed and you have nothing to
complain of. for example, if you are using something like
`'%'.$_GET['first_name'].'%', then I will have only run 28 requests with a different
letter each to dump your "entire database" without any special character used.
M, 06.11.17 12:04
Greetings. I have enjoyed reading your content, especially the part about the PDO
wrapper. Your site has become a bit of a reference on the topic. Helps people not to
reinvent the wheel every day. As a personal wish, if I may, I'd like to get your insights
on 'cache invalidation' an PDO. A bit off topic perhaps - it's just something I am
working on these days. An ambitious idea of having a "caching system" underlying the
app, i.e. transparent to the app who whouldn't know if data is coming from Memcache
or the database. Queries would be handled by the PDO wrapper (-ish) and cached
(using memcache), and also invalidated when 'interested' rows are updated/deleted.
Not an easy task to develop something like that. Also, it'd be nice to have the User
https://phpdelusions.net/pdo#in 54/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
REPLY:
Hello M!
Yes this question a bit off topic, but from my experience I would rather warn you
against such a generic all-embracing cache solution. In my opinion, your regular
queries should be fast by themselves, without the need of any cache. And it's only a
few exceptional cases should be handled using a cache, but such cases should be
carefully handpicked, considered using a regular optimization, and only then
optimized using a cache, with a carefully considered invalidation.
Regarding transactions, thank you for the suggestion, I will add a link to that
comment.
Hi, I again stuck at some point, Now I am trying to edit product details which I already
have added. I also have added image field when I click on edit button and then when I
add only some feature after clicking on update button I find Picture which I did not
touch have gone.... Now after analysing I have understood the Problem but I am
unable to resolve the issue if you can help me in this please. my code is:
if(isset($_POST['update_product'])){
$product_name = $_POST['product_name'];
$cat_name=$_POST['cat_name'];
$sub_cat_name=$_POST['sub_cat_name'];
$product_img1=$_FILES['product_img1']['name'];
move_uploaded_file($product_img1_tmp, "images/products_images/$product_img
1");
See in above When we use move_uploaded_file then what happen actually is our
database only get the path of image while image actually move or resides into the
folder we have mentioned into path. So, when I try to edit/update page when code
approached to database field product_img1 it only find path but not actual image. I
think there must be a way like move_uploaded_file which I don't know :(
Please could you help me in this? how to fix this issue that if I don't want to update
image, image should remain there?????
https://phpdelusions.net/pdo#in 55/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Hi David, Yeah, you are right, I am mad that since hours I was checking my code
madly, then took some rest and when again checked, I found my mistakes, both
foreign keys I had placed wrongly; I corrected in the way below and my code is now
working perfectly: into insert statement (cat_id, sub_cat_id) and into
values($cat_name, $sub_cat_name)
I also removed function NOW() and mysql is taking time itself. i might don't need to do
anything. I was so happy and wanted to let you know, when came here I found your
reply. I really appreciate you for a quick response, you are doing such a great job by
helping students. Great work! I will try to bound foreign keys as well as suggested.
Thanks a lot and Regards,
Hi, I am trying to learn PDO but stuck as receiving error message says:
"PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number:
parameter was not defined" here is code:
if(isset($_POST['add_product'])) {
$product_name = $_POST['product_name'];
$cat_id=$_POST['cat_name']; //Foreign Key from main_cat table
$sub_cat_id=$_POST['sub_cat_name']; //Foreign Key from sub_cat table
$product_price=$_POST['product_price'];
$product_warranty=$_POST['product_warranty'];
cat_id and sub_cat_id are foreign keys while I also want to get date and time into
Products table by function Now() Prepare statement and bindParam is as below:
Call to function has been made into form where I need this to be executed.
It looks like I have set bindParam wrongly, I don't understand how to make it perfectly
for cat_id, sub_cat_id and function NOW(); your help will be appreciated. regards,
https://phpdelusions.net/pdo#in 56/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
REPLY:
Hello Zohra!
All you need is to brace yourself and write a consistent code. Either in your editor or
here in comments.
I took your code and run it on my local server and there is no "Invalid parameter
number". Therefore I suppose that you are just doing some mistakes when running
your code and don't verify it, hence the error.
Note that you should use prepared statements for ALL variables. $cat_id, $sub_cat_id
should be bound as well.
Can you help me please. I am trying to perform the following PDO. All the values are
here are present. However I get an error message " Call to a member function
prepare() on null in "etc.
REPLY:
Hello David!
This error means that you are calling $pdo inside a function, where variable scope is
different. You need to make your PDO instance accessible. A short list of variants is
listed here: https://phpdelusions.net/pdo_examples/connect_to_mysql#access
(https://phpdelusions.net/pdo_examples/connect_to_mysql#access)
Please feel free to comment back if you need any help with any particular method
https://phpdelusions.net/pdo#in 57/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Thanks so much for your quick response. I think I just need to take a step back and
do some more reading. I think this code wont work anyway because the scope of the
$bdo variable is outside of the function. I need to work that one out next!
REPLY:
Hello Ed!
Whoops, I didn't notice the issue with variable scope, shame on me.
There are several ways you could get a PDO instance. You may check this page for
some suggestions: PDO wrapper (https://phpdelusions.net/pdo/pdo_wrapper)
Thanks for the great and extensive instructions. I havent got very far but I am having
a problem with passing the result of a function into a conditional statement. The
database connects ok but I think the problem is here:
$result = $stmt->fetch();
return ($result ==1)?true:false);
Any ideas?
https://phpdelusions.net/pdo#in 58/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
$host = 'localhost';
$db = 'mydb';
$user = 'dba';
$pass = '';
##$charset = 'utf8mb4';
$dsn = "mysql:host=$host;dbname=$db";
##var_dump($dsn);
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try{
$pdo = new PDO($dsn, $user, $pass, $opt);
##var_dump($pdo);
}catch(Exception $e){
echo 'Message: ' .$e->getMessage();
exit;
}
function user_exists($username) {
$stmt = $pdo->prepare('SELECT COUNT(uid) FROM users WHERE username = :username');
$stmt->execute(['username' => $username]);
$result = $stmt->fetch();
return ($result ==1)?true:false);
}
REPLY:
Hello Ed!
Yes, you spotted the problem perfectly. The value returned by fetch() method is not a
number but array, and therefore comparing it with a number won't give you any good
result.
If you want to fetch the number, you should use fetchColumn() instead. So your code
would be
https://phpdelusions.net/pdo#in 59/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
function user_exists($username) {
$stmt = $pdo->prepare('SELECT COUNT(uid) FROM users WHERE username = :usernam
e');
$stmt->execute(['username' => $username]);
$result = $stmt->fetchColumn();
return ($result == 1);
}
Also note that echong the error message right away is not advised. Consider the
information from the section related to PHP error reporting
(https://phpdelusions.net/articles/error_reporting)
Hi.
I have a small suggestion - if there are more than e.g. 10 comments, they could be
hidden under "show more comments" link, or something. IMHO it doesn't have to be
ajax, just a hidden <div> which you show upon clicking the link.
Right now people look at the scrollbar and get scared, because they think the article
is soooo long. While it is quite long, and comprehensive, it is not that huge :P
Plus, next to the 'address' field in the 'comments' section, you could add "not required"
and "will not be published"
Thank you very much for this article, I like pointing here ppl who want to start with
PDO :P
REPLY:
Hello Konrad!
Thank you for the suggestions! I'll definitely implement both of them soon.
https://phpdelusions.net/pdo#in 60/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Hi, please am trying to get a value from a field in a datatable, the scenario is like this:
I have a table with data about four columns, at a click of a button which is an action in
the table, a modal appears with four input boxes, I made the first input box disabled
so as to pick a value from a field in the table. But i can't seem to implement it. Please
can you help?
Thank you for your reply. I am a little confused though, but will try to solve my lack of
knowledge. Regards. David.
I would appreciate your help with a PDO issue. I am calling a PDO select function
which works except I only get one piece of data from a file that contains two pieces.
the code is
The field "Dn" should return "Firstname Surname" separted by a space. All I get
returned is the Firstname. Please can you help me ? Regards David.
REPLY:
Hello David!
Most likely your issue is not with PDO but with HTML. Check the HRML source of
your page and find your last name all right.
Then follow the HTML syntax and wrap Dn field value in quotes :)
Hey, I was linked to your site, and it seems to have some nice recommendations. That
said, I was looking at the section on rowCount, and it seems like you take quite the
position on 'it shouldn't be necessary'. I've run into this quite a bit when performing
pagination of results, and I'm curious as to what your recommendation is to do in this
case instead of getting a row count?
https://phpdelusions.net/pdo#in 61/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
REPLY:
Hello Tyler!
I should have probably emphasized it better, but using anything like row count for
pagination is not something "not necessary" but a big mistake.
For paginafion you need 2 queries: one that gets actual data for a particular page, for
this query you don't need no count at all.
Another query, which is used to get the total number of rows, should never be using
anything like row count. Instead of selecting actual rows and then discarding them,
only to get the count, one have to get just the count already, by using a select count(*)
query. Otherwise it will defeat the very purpose of pagination: selecting only a smaller
amount of data that will be actually processed. It's not a big deal for a personal home
page, but selecting all the big data only to count it may kill a mature site.
Hi! Whoever you are. I just wanted to let you know, that all your tips and best
practices are helping me a lot to code better and more efficient. I can't thank you
enough!
K, 28.09.17 22:48
So I understand PDO Prepared Statements should protect from SQL injection and '
escapes. But when I attempted the following...
https://phpdelusions.net/pdo#in 62/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Then the row with id=2 and name=entry2 would be updated. I should note, I only saw
this behavior within the "WHERE ..." part of the query. It did not escape in the
"SET...WHERE" part of the query, and I wasn't able to add any additional queries in
either. I observed the same behavior when I tried with mysqli as well.
That said, I didn't find anyone pointing this out anywhere, and when I asked on Stack
Overflow, no one believed me. I could probably just manually replace ' instances with
something else for storage and replace again for output, but the fact that I'm the only
one who seems to observe this has me seriously confused. Any insight would be
greatly appreciated.
REPLY:
Hello James!
It is quite strange that on Stack Overflow nobody believed you, as the behavior is
quite expected, given the way mysql (and PHP as well) treat input values. Well,
nothing strange on the other hand, given the average level of expertise there.
So, the issue is not actually an injection, neither it has anything to do with PDO. It's an
integral part of MySQL. By default, in the older versions of Mysql (pre 5.7. if I am not
mistaken), when casting input value to an integer, it takes first numeric characters,
cutting off everything else. You can see it with such a simple example:
As you can see, the value become just 25 when cast to an integer to perform an
arithmetic operation.
Exactly the same happened in your example. Value "2" has been taken out of the "2'
AND name='Entry2" string which made a legitimate query and thus the record got
updated.
https://phpdelusions.net/pdo#in 63/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
To prevent this behavior you can set mysql in strict mode, like
set SQL_MODE='STRICT_TRANS_TABLES';
it will start throwing errors for the malformed numeric values instead of silently casting
them.
I've reopened your question on Stack Overflow, you can now write an answer for it
and link to this comment, https://phpdelusions.net/pdo#comment-277
(https://phpdelusions.net/pdo#comment-277) as a proof
Beware that nothing will alert you about the lack of support for the
PDO::MYSQL_ATTR_FOUND_ROWS attribute.
Only way to check if your PHP is going to ignore this attribute is to call $pdo-
>getAttribute(PDO::MYSQL_ATTR_FOUND_ROWS) . Not sure why this isn't done when
setting the attribute (https://bugs.php.net/bug.php?id=62782). Go figure the reasoning
behind this.
To reiterate:
REPLY:
Hello Neighbour!
I thank you for your valuable info! Looks like an important case. I added the link to this
comment to the article.
https://phpdelusions.net/pdo#in 64/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
K, 20.09.17 22:45
Hi,
I'm sorry if my previous question was unclear, but I'd love an answer, even as to
whether this is possible, if you have the time.
$query = $dbc->prepare("UPDATE
customer_info
SET
customer_name = :customer_name,
billing_email = :billing_email
WHERE customer_id = :customer_id");
In order to do this would I have to write a separate query for each item? In my actual
code I have many other items that I'd like to be able to update without overwriting the
information in all of the database columns at once. It seems like using positional
parameters might be a solution, but I haven't been able to get it to work. I was hoping
you could tell me if this is possible, thanks!
REPLY:
Hello!
Sorry for the delayed answer, by the time you asked for the first time, I've been on
vacations and missed your comment.
What I would suggest is to create a query dynamically, from the array provided, as it's
explained in this article ( https://phpdelusions.net/pdo/sql_injection_example#solution)
So it should be like
https://phpdelusions.net/pdo#in 65/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Thank you for your reply. I will give more detail into my reasoning.
I have an online store i have built and i am afraid that a customer may be left on a
blank page or an abruptly ended script without reason during the checkout process, or
post checkout process receiving their order confirmation. I've encountered such an
error myself on another website from having an intermittent internet connection and
being left without any notification it leaves fear of having paid for something without
the order having being processed properly on their end.
I've been trying to handle every single type of error imaginable so my code is full of
try... catches as well as a billion if statements. I'm desperately trying make my coding
much more efficient.
My use of the try... catch operator would also be that if there was an error on a
product page I would have liked it to just keep the script running and display the 'Sorry
we could not find your product' situation, as a kind of diversion until i can fix what is
wrong.
Thank you
https://phpdelusions.net/pdo#in 66/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
REPLY:
Hello Jeremy!
I am eager to hear what are your thoughts after reading the article? Do you agree that
centralized error reporting is better than catching every exception separately?
At the very least you an create a global try..catch wrapping your whole code from start
to end. But To me, using a handler is more elegant solution
Please can you help me resolve an issue? I have a user input box which starts a
search of a PDO MySql table :- using this code snippet.<input type="text" name = "t1"
id="t1" placeholder="Enter an RD number"> <input type="button" name="button1"
value="GO" onClick="aa();"> </td> <td colspan="2" align="center"><div id="d1"></td>
via this code- <script type="text/javascript"> function aa() { var xmlhttp=new
XMLHttpRequest(); xmlhttp.open("GET","getrd.php?
Regno="+document.getElementById("t1").value,false); xmlhttp.send(null);
document.getElementById("d1").innerHTML=xmlhttp.responseText;<script> this
returns 3 data items. All this works. My issue is how can I put these 3 data items into
<input type="text" name = "Rd" id="ret1" placeholder="Rd No" > <input type="text"
name = "Dn" id="ret2" placeholder="DN"> <input type="text" name = "St" id="ret3"
placeholder="Status"> I have tried a number of variations of the above code without
success, I am sure there is a way to do it but I am unable to get there yet. Many
thanks. David.
So i understand that i shouldn't use try .. catch for error reporting. But is it fine to use
for displaying a custom notification to the user that an error has occurred?
Furthermore, if omitting rowCount as a boolean flag and using the $stmt variable to
see if any data was returned;
https://phpdelusions.net/pdo#in 67/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
If an exception has been thrown and caught the $data variable remains set and giving
a true boolean value even though there is no data present, and it doesn't seem
possible to globally unset($data) inside the catch as it's a function.
I would like to catch an error to notify the use 'Something went wrong' and to return a
false boolean flag to handle the rest of the script outside of the try... catch. How would
you recommend the best way i can achieve this?
REPLY:
Hello Jeremy!
Regarding the first question, it is extremely seldom when you need to provide a
custom error message. Given you are still learning, it is possible that your
understanding of the error reporting is rather wrong. Kindly read this article,
https://phpdelusions.net/articles/error_reporting
(https://phpdelusions.net/articles/error_reporting) and I hope you'll change your mind.
The second question is simpler to answer. If an exception has been thrown and
caught, the $data variable is not set, so there is no trouble with unsetting it
whatsoever.
If you want to continue the script execution even in case of error, it's a fair use of the
try..catch operator. But still, I cannot imagine the practical case. Could you please
comment back with a little more detailed description of the scenario you have in
mind?
Thank you for that. However, I'm now confused even more. I get: "Fatal error: Call to a
member function query() on null" and then the path to the document... Alternatively, I
get a blank page
REPLY:
Well, this one is simple. All you need is to read the article above, Particularly the error
reporting (https://phpdelusions.net/pdo#errors) part. You didn't tell a database to
report mysql errors and thus getting this cryptic one. Just make it the proper way and
https://phpdelusions.net/pdo#in 68/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
see.
Thanks very much (hope formatting is correct). Here it is. I created one class for
connecting to my database:
<?php
class Database {
private static $instance = null;
private function __construct(){}
public static function getInstance() {
if(is_null(self::$instance)){
self::$instance = new PDO("mysql:host=localhost;dbname=elektroakustika;", "ro
ot", "");
}
return self::$instance;
}
And now I'm making one that is supposed to handle queries (called it ActiveRecord as
that is the pattern I'm trying to implement):
Finally, I plan to create a class for each table in my db, that will extend the previous
class and use one of the methods I'll create in ActiveRecord. Something like this:
When I run the last code, I get the error, so obviously, my query is wrong. I realize this
is quite basic, but I can't seem to solve the problem. Thank you!
https://phpdelusions.net/pdo#in 69/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
REPLY:
Hello Adri!
Sorry I forgot to mention that a code should be padded by empty lines as well. But
nevermind, I already added them.
First and foremost: the "error" you are getting is not just a red card telling you that you
failed something. It usually explains what specific error you made. So it's always
worth to read what the error says or at least provide to a person you are asking for
help. There could be thousands errors, and its no use to stare at the code looking for
them if PHP can tell you that already. Especially because it can be caused not by the
code itself but by some other issue - a database, or server config or whatever.
Hi! I'm trying to implement PDO in my code (I'm a complete noob, by the way) and I
have a "why won't this work" type of issue. Would you mind checking it out (I'm not
sure if that is OK, which is why I'm not sharing the code now)? Thanks for your
explanations, by the way, they are incredibly useful!
REPLY:
Hello Adri!
Yes it's all right to ask a question here. Just make sure that your code is indented by 4
spaces, so it will be nicely formatted
K, 25.08.17 17:50
Hi, sorry if this is a double post! I wasn't sure if my other comment was in moderation
or disappeared.
https://phpdelusions.net/pdo#in 70/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Thanks for the great tutorial, it's one of the best I've come across for learning to use
PDO.
I was wondering if you could give an example of how to create a PDO update query
where you can have a variable number of inputs. So for example if I have a form
where I can update a customer's name, email, or phone number. But sometimes I
want to update only one at a time without overwriting the information already in the
database.
Awesome! I'm just learning PHP and this was the first tutorial that made any sense.
Most have code without going to the details which are the most confusing part at the
start
REPLY:
Hello Tomi!
Can you do me a favor? I am going to add a section called "PDO examples" with
practical examples of PDO usage. If you can think of any example case you'd like you
see, please drop a comment.
Thank You
$table = “myTable”;
$table = "`".str_replace("`","``",$table)."`";
$table = `myTable`;
or use
Thanks again.
REPLY:
Hello Christian!
If backticks are already added to the identifier, then there is no point to add them
again. So it should be your first variant.
Thank you for articles and your responses to questions. Can you please clarify the
section ‘Prepared statements and table names’?
$table = "`".str_replace("`","``",$table)."`"
I read that MySQL an identifier needs a (`) backtick if it contains special characters or
is a reserved word. In your example we concatenate (`) backticks on both side of the
identifier and replace single backticks with double backticks on original $table
variable. So the new $table variable would be ``identifier`` because extra backticks
are like an escape character(\) and that would remove one backtick? How does this
sanitize $table variable ? Does this have anything to do with PHP Execution
Operators? Thank you for your help!
REPLY:
https://phpdelusions.net/pdo#in 72/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Hello Christian!
Thank you for the good question. Look at this from the point of view of the regular
string formatting. To format a string we need to enclose it into delimiters and then
escape those delimiters that could happen to be inside. Exactly the same we are
doing here: We are adding delimiters (single backticks around) and then escaping
these that are could be already inside of the table name. But we don't double the
surrounding backticks, neither removing any. The above statement could be split into
two:
Imagine we have a table name coming from the user input and a hacker put a
backtick inside. Without doubling, with only surrounding backticks we'll get an
injection:
whereas with doubled backticks inside the whole statement will make a query like this
Where
is a single identifier (a-non existent of course, which will produce an error, but an error
is better than injection).
Thank you for articles and your responses to questions. Can you please clarify the
section ‘Prepared statements and table names’?
$table = "`".str_replace("`","``",$table)."`"
https://phpdelusions.net/pdo#in 73/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
There seems to be a bug in the code sample that describes the foreach functionality:
Alternatively, you could put ->fetchAll() at the end of the $stmt assignment instead of
in the foreach.
REPLY:
Hello, Bill!
Let me suggest you to try the first code snippet yourself, see it actually works and
then follow the link from the article explaining the trick :)
https://phpdelusions.net/pdo#in 74/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
REPLY:
Hello Christian!
If you mean filter_input() , it's superfluous for this code snippet. Using prepared
statement is secure enough. So, an extra sanitization won't make too much sense.
but it's only a matter of taste and you can keep with manual binding and named
parameters.
Thanks for your reply. When a user has logged in they presented with a menu screen
to choose where on the site they want to work. Then a menu second allows them to
select a sub area to work in according to their user rights. its function is to open
MySql tables via a PDO connection. this is the problem screen. it is taking too long to
appear. Thanks David E.
REPLY:
It's hard to tell what could be a problem. But at least try to change "localhost" to
127.0.0.1 in the DSN.
If it's not the case, you need to investigate more, what particular operator in your code
takes all the time.
https://phpdelusions.net/pdo#in 75/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Many thanks for your prompt reply. I was trying to avoid the "waiting for" messages.
Your answer is very much appreciated. Thanks again. David E.
REPLY:
Can you please elaborate on these "waiting for" messages? May be I can offer some
solution, but for the moment I am not quite getting what is it.
REPLY:
Hello David!
In case you are talking of a single script execution - yes, like it said in the article, the
connection should be established only once.
https://phpdelusions.net/pdo#in 76/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Many thanks for the updated code, it is working perfectly now. Very much
appreciated. Kind regards. David E.
Many thanks for rapid response to my PDO query and thanks for the conversion
coding. I have this line in my connection code already, but I did add your line to the
new script. PDO::ATTR_EMULATE_PREPARES => false, However I am still getting
error message SQLSTATE[42000]: Syntax error or access violation: 1064 You have
an error in your SQL syntax; check the manual that corresponds to your MySQL
server version for the right syntax to use near 'LIKE ? ORDER BY Friendship LIMIT
?,?' at line 1. I am stuck now. Hope you can help me. Many thanks.
REPLY:
Hello David!
First, I must confess that I made a terrible mistake. I overlooked the severe
vulnerability in your code. If $option is coming from the browser, then it's a disaster.
You should sanitize this variable, checking it against an array of predefined values.
And most likely this is the source of the error you are getting.
The first three lines are responsible for the $option santitization.
s/count(/COUNT(/g
https://phpdelusions.net/pdo#in 77/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
"Good ORMs are Doctrine, Eloquent, RedBean, and Yii::AR. Aura.SQL is a good
example of a PDO wrapper with many additional features."
Please provide a review of these or maybe a separate post. It is a huge qualifier when
you say "for real applications you'll need to use one of these bunch of libraries but for
now let me tell you all about PDO..."
REPLY:
Hello William!
Thank you for a good suggestion. Indeed it's a very good idea to outline at least
essential features of these libraries and to show why they actually to be preferred. I
cannot promise it will appear soon, as it' pretty much an undertaking, but I'll definitely
will start composing such an article
$option is user selected from dropdown list. $srch is user input search criteria.
REPLY:
Hello David!
There are two issues to keep in mind when converting such a query, both mentioned
in this article:
1. First, you have to make sure that the whole search string is set to a variable,
including wildcard characters
2. Second, make sure that emulation is turned off, to make LIMIT parameters
binding easier
So the final code would be
https://phpdelusions.net/pdo#in 78/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Hope it helps!
Very, very good website for someone starting with PDO. Thank You for making it!
Hi,
Trying to get last 25 row in ascending order but getting error any idea what could be
error
output
REPLY:
Hello Ganesh!
Thank you for your question. I never seen such an error message before. Looks like
you already posted a bug on the PHP site, https://bugs.php.net/bug.php?id=74769
(https://bugs.php.net/bug.php?id=74769)
what should i care about when i am migrating to postgresql from mysql ???
Thanks
in select,update and insert also should i use :dbprefix but not working
REPLY:
Hello Ganesh!
Unfortunately, PDO doesn't have a placeholder for the table name. So So all you can
do is to use a PHP variable and make sure it is not coming from user input:
https://phpdelusions.net/pdo#in 80/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Thank you for your advice. I would like you to be my mentor. I have still going through
your site and I am happy to tell you that I am really learning a lot.
Hi, I found out that your site is very useful to me as i am a beginner to coding. I
wanted to know whether to use mysqli or pdo. I searched and came to a point which i
decided to pay more attention to using pdo than mysqli. My question is, am i on the
right track with my decision or do i have to lern php with mysqli first?
REPLY:
Hello!
"In such a case explicit binding have to be used, for which you have a choice of two
functions, bindValue() and bindParam(). The former one has to be preferred,
because, unlike bindParam() it has no side effects to deal with."
You mention that bindParam has side effects, but there's no explanation as to what
they are. PHP The Right Way and some other sites recommend bindParam, so I'm
really curious why you recommend bindValue.
https://phpdelusions.net/pdo#in 81/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Thank you for providing a great resource. I want to reference your site in my
undergrad paper to Flidners University South Australia, on serving my first database
with WAMP, can i obtain your name ?
I'm trying to run a stored procedure on a Sybase 11.0.1.2596 database in a PHP file
using PDO and dblib as the driver. I can call a procedure with no parameters with no
problems using something like:
call custom.show_clocked_in_employees
However, if the procedure takes parameters, I get an error. If I have a procedure like
this that takes 2 parameters:
I have tried calling it many ways and received different errors including this:
SQL Anywhere Error -188: Not enough values for host variables [13638] (severity 16
) [(null)]
it works perfectly. The exact way I'm trying to call it is like this:
https://phpdelusions.net/pdo#in 82/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
I would like to know more, is there any pdf's available about this tutorial I'm eager to
learn
Hi! Another brief question: I was expecting to receive the exact same results using
this approaches, but it seems I got different results (results in terms of the output
results/data of the query)
And then this one: $stmt->execute(["Lim" => $L, "Lim2" => $L2, "Name" => $Name,
"Name2" => $Name2]);
Thanks!
REPLY:
Nope, as long as this code works, there should be no difference. The only case I can
think of is when you are comparing a numeric value with a string field, something like
name=1 in SQL. In this case results are indeed unpredictable.
Do you mind to provide more details on the difference you are talking about?
Hello, I am following closely your tutorial and I am really grateful for the useful
information. Now I need your help again:
As you said, there is a problem with the Limit clause.. and I'm stuck in the middle.
On one hand, I need the emulation mode turned ON as I have to query for multiple
fields having the same name, as in your example:
https://phpdelusions.net/pdo#in 83/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
$stmt = $pdo->prepare("SELECT * FROM t WHERE foo LIKE :search OR bar LIKE :search"
);
$stmt->execute(['search'] => "%$search%");`
$stmt = $pdo->prepare("SELECT * FROM t WHERE (((foo LIKE :search OR bar LIKE :sear
ch) AND (boo LIKE :search2")) OR ((foo2 LIKE :search OR bar2 LIKE :search) AND (b
oo LIKE: search2)) ... and so on ) LIMIT :Limit1, Limit2
And on the other hand I need the Limit parameters, for which the emulation needs to
be OFF in order to function.
Otherwise, Is there any way to bind only the values for Limit parameters and continue
executing: $stm ->execute(array_merge($params,${"in_params{$x}"}));
Thanks a lot!
REPLY:
Hello Alex!
Indeed there is no way to combine the execute and bindvalue methods. So in general
you just have to write all the binding by hand. I suppose that for an exceptional case it
is not a big deal.
However, I think there is a workaround, which is quite close to your requirements: you
can write a loop, binding all other parameters from array, and then make two distinct
bindvalue calls, something like this:
Hi,
https://phpdelusions.net/pdo#in 84/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
I have a piece of hardware that collects data and operates a machine and then you
can log into it to get statistics. A second operation it does (not very well) is send out
data to a web database which stores what it sends. Every tutorial i find with api for
php uses mysqli connect or other bad practices. Do you have to do a tutorial with the
right pdo framework?
"to cancel all the changes you made sihce transaction start"
REPLY:
It's c. 2017, seven years after mysql 5.5.3 was published, which supports utf8mb4 -
don't you think you should suggest this as encoding instead of utf-8?
you can use the method chaining and thus call execute() right along with
prepare():
However, if you want to get the number of affected rows, the code will
have to be the same boresome three lines:
https://phpdelusions.net/pdo#in 85/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
REPLY:
Hello Dave!
The method chaining returns the return value of the last method in the chain,
execute() here, and it's a boolean value. So it won't work.
So we can make it
Got caught out last night when upgrading our production server's PHP7 version and
modules (most likely php7-pdo?). PDO couldn't parse this previously working DSN:
mysql:host=localhost:13304;dbname=test;charset=utf8
Fixed with this (note the port has moved in the DSN):
mysql:host=localhost;dbname=test;port=13304;charset=utf8
I'm having a hard time gaining an understanding of best practices of real world
implementation of PDO. Especially file structuring - as in where my PDO connection
should be located and efficiently used throughout a php app. If this is not within the
scope of this site perhaps you can direct me to a good reference. Thanks!
REPLY:
Hello Jeff!
Thank you for the good question, it's perfectly within the scope of this site.
In general, you just create a separate file where PDO instance is created, like $pdo =
new PDO ... . And then just include/require that file into every script that needs a PDO
connection.
The main question here is how to use that instance. And this answer depends on your
coding style. If your code is procedural, then just use that $pdo variable. To make it
accessible inside functions, either make it global or use a static wrapper like one is
shown on the next page: https://phpdelusions.net/pdo/pdo_wrapper
(https://phpdelusions.net/pdo/pdo_wrapper)
If your code is OOP, that it's a different tory, too big to cover in a comment. You may
google for the Dependency injection and IoC containers.
I don't know if is due to the fact that there are 2 WHERE clauses (I've removed one).
Also as logic I don't know if it is intended to return what I expect.
So I need to retrieve all the details from Users (from those users that are active);
https://phpdelusions.net/pdo#in 87/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
then, for the users that have Ratings (and the ratings are also active), I need to
see the ratings, otherwise I'll just stick with the user's info and mention that there
is no rating for him.
Isn't the JOIN supposed to return only those that have ratings?
Thanx!
REPLY:
Hi Alex,
Sorry it was a copy paste error. There should be ON instead of first WHERE.
Regarding JOIN, the the original one indeed will get you only users with ratings. But
LEFT JOIN is a special join to give you all users regardless.
As of your other question, remember those u and r used in the query? they are called
aliases and intended to distinguish field names from different tables. So the final
query would be
Hi again, (it seems that my first comment wasn't posted - It was submitted several
minutes before this one: Alex, 21.02.17 01:36)
OK, thanks a lot for suggestions, I'll give it another shot during this night. Only one
more question here:
as my both selects contain "WHERE ACTIVE = 1" it refers one time at User
being active and the second time it refers the Rating being Active = 1 (so we
have to make sure that users is activated and that his rating has been
approved). Using your suggested code, could I make something like this? $sql =
"SELECT u.*, AVG(Rating) As AverageRating FROM users u WHERE Active = 1
(## Meaning User to be active ##) LEFT JOIN RatingsTable r WHERE
r.UserID=u.id WHERE Active = 1"; (## Meaning the Rating for the corresponding
User - to be active as well ##)
Thanks a lot!
https://phpdelusions.net/pdo#in 88/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
P.S. My first comment (really first comment that I cannot see it here / maybe it wasn't
even submitted successfully) was about your suggestion between PHP / Java /
Python for a good, robust, stable but flexible web application, based on your
experience. Maybe also suggesting a set of "knowledge toolkit".
Hello again, How do you see a better implementation for this case, as migrating it
from simple mySql to PDO is seems a lot slower: I have multiple users to retrieve
from DB and show their information, but some information is stored in another tables,
so then I have to run another query in the while loop, for each User ID
Example:
$i++;
}
so the point here is that I'm assigning the details from DB to arrays of First Names,
Last Names, User ID's, then Ratings and some other information that I need to
retrieve from other tables based on the specific UserID. Is there a better, cleaner,
faster way to do it, maybe not using so many arrays and using the
$row['anythingHere'] variables and using it / displaying in the loop (not just storing /
assigning in the loop and then use it later based on the array indexes).
Thanks a lot!
REPLY:
Hello Alex!
That's a really good question. Indeed, there are means for the improvement, both for
the performance and cleaner code.
https://phpdelusions.net/pdo#in 89/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Take out ORDER BY RAND() part. It doesn't seem to do any good, but can slow
down things considerably.
Take out the nested query as well, and use LEFT JOIN instead. You can join as
many tables as you wish
Get all records at once in a single array using fetchAll() method
So it should be something like this
Now you can loop over single $users array and get any column. Fname, for example:
foreach($users as $row) {
echo $row['Fname'];
}
If the code still be slow, make sure that you have an index for the UserID field in
AverageRating table (and in the all other linked tables as well).
Thanks it help me.thanks again if i had any inquiry regarding pdo i will back to this
web again.
how suppose im write using pdo style for user redirect their respective webpage after
login. example admin login will re direct to admin page and normal user redirect to
user page after login. please show some simple example.
REPLY:
Hello!
https://phpdelusions.net/pdo#in 90/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
this question doesn't really belong to PDO as you have to deal with the information
already fetched from the database. Assuming you have a variable $row with user
data where level field is responsible for the user level, the code would be
You can add as many such condition as you like. Hope it helps!
$dbfname = $row["fname"];
$dblname = $row["lname"];
$dbemail = $row["email"];
}
if($login_session != $dbemail){
}
thanks i advance
REPLY:
Hello!
$stmt = $pdo->prepare("SELECT fname, lname, email FROM register WHERE email =?");
$stmt->execute([$login_session]);
$row = $stmt->fetch();
if($row){
die("that username could not be found!");
}
$dbfname = $row["fname"];
$dblname = $row["lname"];
$dbemail = $row["email"];
https://phpdelusions.net/pdo#in 91/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Thank you! Great job. You helped me to understand better what I was just assuming
as best practice.
Thx!
REPLY:
Hello Andy!
For PDO there is no simple solution. But it can be done with a code like this
$pdo->prepare($sql)->execute($parameters);
Here we are using just a single $sql variable which is getting field names based on
your conditions. Also, we are adding all our variables into $parameters array.
https://phpdelusions.net/pdo#in 92/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Finally, we are adding VALUES part to the query, creating as many ? marks in the
query as many items in the $parameters array.
And all we need now is to prepare a query and execute it with parameters.
Thanks a lot for this. As a beginner with PDO this taught me a lot.
Cheers,
Thank you for this extraordinary work. I found your concise, well formed presentation
of material an easy read and the explanations, examples and insights on technique to
be extremely helpful. My gratitude to you for your work on and in, presenting this
document.
Lots of useful details on PDO! I use wizzyweb as it automates the code creation for
data entry and reports, even prepared statements for safer code.
Hello!
I was at the beginning of writing a complex tutorial on how to use PDO in polish,
because there's not even one good tutorial about it and while I was researching for
some resources other than PHP manual I encountered your The only proper PDO
tutorial and I got an idea to translate it into polish, of course give you a proper credit
and publish it on my blog (http://nastoletni.pl (http://nastoletni.pl)).
https://phpdelusions.net/pdo#in 93/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
I'd be very grateful and I think polish beginners would be really grateful too for such a
quality tutorial in such an important and trivial thing which is connecting to database in
PHP.
REPLY:
Hello Albert!
Thank you for giving this article such a credit! That would be a honor for me if you
translate it to your native language. Surely, I am giving you the permission to
translate.
Please send me a link when it will be done!
Also, feel free to ask for clarifications, whenever you find it necessary.
Thanks for the the articles. Very educative even for experienced programmers. Keep
up the good work bro.
REPLY:
Hello Kalule!
Thanks a lot for your feedback! May I ask, what you find especially interesting in this
article? and also, may be there are some parts that are not very clear or detailed?
Please share your opinion.
https://phpdelusions.net/pdo#in 94/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
REPLY:
Hello Danilcha!
Hi,
You seem to be very a skilled PHP developer, could you link me a good PHP tutorial
so I can start learning the language the right way? Right now I have a website that I
wrote from scratch using HTML, CSS of course and a bit of copy-pasted, slightly
edited JS. The only PHP I'm using are include statements so that my pages don't
become too hard to maintain. I'd like to be able to load images, table content and text
from a database, dynamically creating a page the same way it's also done on forums
and webshops. I understand that PHP 7 is relatively new so most of the tutorials out
there are written for PHP 5.6 or older. Is this a problem or is the conversion to 7 easy?
Thanks,
Viktor
REPLY:
As of the version, just don't worry: php7 is 99.9% the same as 5.6.
Just a note, if you are using an IN clause, and you need to ->execute more values
(besides those in the IN clause), this seems like a clean way to do it:
https://phpdelusions.net/pdo#in 95/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
$query->execute(array_merge(
array ($value1, $value2),
array_merge ($IN_CLAUSE_ARRAY,
array ($another_value, $another_value))));
REPLY:
Hello Tyler!
Indeed, last time I've answered the similar question on SO, I tought I should add such
an example but got busy and forgot it.
Yet I think that you made your example a bit overcomplicated. You need only one
array_merge() call:
$query->execute(array_merge(
array($value1, $value2),$IN_CLAUSE_ARRAY, array ($another1, $another2)));
M, 31.12.16 05:39
With the mysqlnd driver, what does that mean for fetch vs fetchall?
Will the database be "hit" multiple times using fetch and only once using
fetchall?
Thanks.
Hi, If I wanted to save a single row from my database as a variable for later use,
would this be the correct way to do it ?
https://phpdelusions.net/pdo#in 96/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Thanks :)
REPLY:
Hi Joe!
Depends on whether you're using a variable in your query or not, there will be either
one line or three lines, not five. Just check the code by the link and choose the variant
that suits you best.
REPLY:
Hello Joe!
This one is very easy to fix. 5 years ago a new syntax for arrays was introduced in
PHP - square braces instead of array() . You can change your code as follows. But
better consider upgrading your PHP to a more recent version
https://phpdelusions.net/pdo#in 97/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
$host = '127.0.0.1';
$db = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
);
$pdo = new PDO($dsn, $user, $pass, $opt);
Hi everyone!
If you you wish to be able to full Unicode in MySQL database, I would read here on
how to do that utf8mb4
$host = '127.0.0.1';
$db = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new PDO($dsn, $user, $pass, $opt);
REPLY:
https://phpdelusions.net/pdo#in 98/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
[ Thank you for your reply dated 12 Dec 16] Hello again. After using the code above:-
$host = '127.0.0.1';
$db = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new PDO($dsn, $user, $pass, $opt);
and inserting the server ip address, my database user name, password, database
name. I'm finding it don't work. Am I going wrong?
Would you write a sample code which will, Connect and open the database, create a
table and finally insert data and close? Thank you.
REPLY:
Hello Ian!
Well, this this is the exact code that should work, given you provides the correct
database credentials. To avoid a confusion, please keep in mind the following:
when something doesn't work, always provide the actual outcome - the error
message, the information shown, etc. You know it's hard to tell anything if you
can't see the actual result
this code have to be used with PDO only. whatever else functions like
mysql_query or mysqli_query won't work with it.
Taken by itself, this very code is not supposed to "work". It just connects to a
database, that's all. What is your expectations for this code, how it's supposed
to work?
https://phpdelusions.net/pdo#in 99/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Hello. Just a quick query, I'm currently teaching myself php, and started looking at
database. I'd just like to ask, how do I insert using mysqli as the data is been
displayed on the screen only, and not been inserted into the database? Secondly is
pdo better, if so, how do I get started with it?
Thank You
REPLY:
Hello Ian!
For the first question it's hard to tell, without a code. All I can tell is that you have to
use a prepared statement for your query because it helps you to avoid many
problems.
As of the PDO - you just landed on the exact page you need: this is a very good PDO
tutorial. Feel free to ask any question you have.
I really like your page and whole blog :) everybody should use code like you explain. If
you ever release a book i'll buy it :)
Oh wow didn't know about the trailing comma in the arrays, learn something new
every day :D Great tutorial, very comprehensive and useful !
REPLY:
You're welcome!
https://phpdelusions.net/pdo#in 100/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
You have a comma after your last key-value pair! I'm sure that's not right :)
REPLY:
Hello Sam!
try {
$pdo->prepare("INSERT INTO users VALUES (NULL,?,?,?,?)")->execute($data);
} catch (PDOException $e) {
if ($e->getCode() == 1062) {
did not work for me as the value was 23000 or some such on a duplicate. What did
work for me was
if ($e->errorInfo[1] == 1062) {
I'm running on a dell Inspiron 15 laptop under windows 10 using Chrome Version
54.0.2840.99 . PHP version: 5.6.21 MySQL 5.7
REPLY:
https://phpdelusions.net/pdo#in 101/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Although it's just to illustrate the handling process, I have to dig into these codes
deeper. I had the same issue before but no time to investigate and develop the
universal solution. Will add to my TODO list.
This is a great article, and I have bookmarked the site for future use. Having seen
your comment about inviting corrections to your English (which is excellent, by the
way), I have one that will improve readability in this article: in several places, you say
"one have to", which should be "one has to". The verb "to have" is conjugated like so:
I have; you (singular) have; he/she/it/one has; we have; you (plural) have; they have.
Hope this helps :)
REPLY:
Hello Ian!
Thank you for your kind words and a suggestion! Been busy recently, but finally got
time to fix that.
That code is vulnerable to sql injection. you obviously havent tested it thoroughly.
I would like more on how to INSERT INTO a MySQL database. I am having trouble
understanding some code I have gathered from the internet and the insert function of
the DB.php after assembling the data keys and values as fields and placeholders
calls the query function of the DB.php to do the insert as a Query. It then checks to
see if an error occurred using
if (!$this->query($sql, $fields)->error())
{
return true;
}
https://phpdelusions.net/pdo#in 102/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
This is returning without any error but no data is inserted. What is the correct way
using PDO to check for successful completion of the insert?
REPLY:
Hello Alan!
Thank you for the good question. Although the example you posted is not a genuine
PDO (there is no error() method), and thus I cannot comment on it, I can answer the
question regarding error handling in general.
Most of time, you don't have to check for the success. Just write your code out of the
assumption that everything goes well. So in your case you can just return true,
unconditionally.
As of the possible failure, you don't have to write any dedicated handling code either.
Let me explain. You see, an error message like "Sorry insert failed" exists only in
schoolbooks. While for the real life sites it is agreed upon, that on any unexpected
error your program should just halt. Given PHP can halt on error by itself, you don't
have to write any handling code by hand. However, this halting process consists of
several steps. I am still in the process of writing a whole article on the matter but in
short:
on a live site, a complete error message should go into error log. Just to let you
know what went wrong.
while in the browser a generic error page have to be shown.
Both actions (logging and showing an excuse page) can be set up in a centralized
way, written only once, somewhere else in your code. Means for the moment you can
keep your code as is, while by the time it goes live, you will be able to add whatever
centralized error handling code you wish.
To let PDO halt on errors, just set it in exception mode is it's shown in the article
above, and avoid catching exceptions right in place.
Please feel free to ask if something is still unclear to you or you have some particular
scenario in mind you have to handle.
I really don't see the benefit of PDO. I've written almost 100 e-stores from the ground
up with all SQL flavors. This just seems like a big headache and annoying. It's easy to
prepare SQL statements and send them to a server. I personally don't think 'oh wow
its gonna help my queries so much' ... I usually output the query in text to test it in a
shell window.... and PDO is the type of thing that causes sites to crash , be slow, and
be dumb.. because the developer put too much faith into it..! it just makes it harder to
debug later....
REPLY:
PDO doesn't cause sites to crash or be slow. You confused it with something else.
pdo o mysqli?
REPLY:
Hello robot! :)
I have a question regarding exception handling though: ok, it makes sense to let
exceptions bubble up the call stack and use an application-wide handler, but what do I
do if I need to show an error message in a certain place on the page?
For instance, I have a page that displays a menu at the top, and underneath that
menu is the place where I want success/error messages to appear. If the user tries to
perform an operation in that page, and the operation fails because of the db, how can
I display an error message in the proper place on the page if I use a generic
exception handler?
https://phpdelusions.net/pdo#in 104/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
REPLY:
Hello Ionut!
Speaking of database errors, you don't actually show error messages for them. For
the success message it's all right; For the validation errors it's all right; But for the
system errors it is not.
For such a system-wide error like a database failure (or whatever else PHP error), it's
better to show a static 500 error message. Because if your database failed, then most
likely you won't get your page properly anyway - there are a lot of other queries that
won't fire too. Just look at all these big boys - they don't show you anything like that
on page reload.
The only case when such an error message can be shown is AJAX call. But again, for
a server it's all the same: on failure it have to send an error HTTP status code and a
static error page. Then either the latter will be shown (if requested directly) or the
AJAX handler will consider the response status code (which is not 200) and show an
error message.
If you still have any doubts, please provide a particular error example you are thinking
of. It will make my answer more focused.
I was waiting for my comment to be approved by moderator as it has link of image file
: explaining my Question in detail.
I really need answer for the question, can you please look into it.
REPLY:
Sorry, but there was no comment with an image. Can you please try to re-post it? Or
you can reply to the notification email directly, with your image attached.
"if you need to know how many rows in the table, use SELECT COUNT(*) query."
https://phpdelusions.net/pdo#in 105/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Partially false. First, don't take every column into the counting, it's worthless wasting.
Second, make use of indexes, especially the primary key (or closest unique key of
same purpose if no primary exists) like this:
REPLY:
Hello Joni!
Thank you for your contribution. Unfortunately, both your statements are not true.
The first one is just irrelevant to the question. count(*) has nothing to do with selecting
columns. When counting, your database actually doesn't select anything, it just
counts.
The second statement is wrong too, just because your database is smart enough to
pick up the appropriate index by itself. You can test it yourself, by running query
and checking the KEY section. There will be a key, if you have one defined in your
table.
You can't seriously call this a "proper PDO tutorial" when you don't ever say what
"PDO" means, not even once. I had to google it to find out that it means "PHP Data
Objects", which doesn't appear anywhere on this page (except now in my comment).
REPLY:
Dunno what practical use you can make from knowing that acronym meaning, or
whether "PHP Data Objects" makes any better sense for you, but indeed you are right
- I didn't bother to put it here. I will fix it, special for you!
https://phpdelusions.net/pdo#in 106/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Your post covers every thing about PDO and its execution, but i have following
doubts:
REPLY:
The kind of SQL query is irrelevant to PDO: you can run any kind of query with PDO.
You can run joins, sub-queries, stored procedures; set variables - whatever. PDO has
nothing to do with your SQL - it will just relay it to database server.
$values[$field] = $source[$field];
should be
$values[$field] = $data[$field];
REPLY:
REPLY:
Neither I am :)
Hello
You say "This is the main and the only important reason why you were deprived from
your beloved mysql_query() function and thrown into the harsh world of Data Objects:
PDO has prepared statements support out of the box"
REPLY:
Thank you for catching that. Indeed the phrasing is not that clear. However, mysqli is
not mentioned not implied here. This article is not about mysqli vs PDO comparison.
It's solely about PDO, and it is expected that the reader is interested in PDO and this
is why it mentioned here.
If you're looking for the article explaining why one should prefer PDO over mysqli,
here is one: https://phpdelusions.net/pdo/mysqli_comparison
(https://phpdelusions.net/pdo/mysqli_comparison)
As requested, a correction to the last paragraph on your landing page. You can delete
this comment after using it.
https://phpdelusions.net/pdo#in 108/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Text with improved grammar: Disclaimer: I am not a native English speaker and have
never even attended an English class, so there are tons of mistakes in the text for
sure - I beg your pardon for them. Please, feel free to leave comments with
corrections.
REPLY:
This is the best PDO tutorial I have found yet and I have SCOURED the internet. Your
explanations are so much more logical than anyone else's and you have covered so
much. Thank You.
If one should never use the rowCount() to count rows in database, what is the best
way to handle pagination?
REPLY:
Hello Tyler!
Thank you for the good question. Pagination is exactly the case when one should
never ever use the rowCount() .
By using this function you will ask a database to send into your PHP script all the rows
found. It is highly inefficient and even may lead your server to failure.
Instead, just like it is said in the article, you have to ask a database to count the
number of rows, by sending a query
https://phpdelusions.net/pdo#in 109/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
This site has taught me more than 100 others. Thanks for the clear explanation of
these confusing points.
One comment, it might be helpful to absolute beginners like myself, if you showed
both email and status in your placeholders example. It was a little confusing for me to
make the leap from SQL to named placeholder substitution.
REPLY:
Hi there!
Thank you a lot for the kind words, and - especially - for the suggestion. Indeed it was
a stupid for me to omit status in the examples. Fixed now!
You're welcome to share any other suggestions or confusions - it will help me to make
the site better for other people!
iRobot
I have tried everything to get a piece of code to work. Can I pay you to give me a
solution that works, as well as review other pieces of code I have written. If you reply
to my email I will send the code. Unfortunately the time I have spent trying to learn
this stuff has been sucking up all my time when I should be doing other things that
make money.
"Recently all PHP extensions that work with mysql database were updated based on
a low-level library called mysqlnd, which replaced old libmysql client"
REPLY:
I will try to re-phrase it, but the problem is that replacement is not permanent. There
are distributions, where libmysql is used by default. Means one could still encounter
with outdated setup. So, for the end user the change is still recent, in the meaning
one cannot rely on mysqlnd for sure.
It did not work, beacause PDO::FETCH_UNIQUE does not work with PDO
statement::setFetchMode, it affect only if use in fetchAll method :(
REPLY:
I will not delete your comments because it may help someone else. We all learn from
our mistakes and sharing a mistake is as good as sharing an achievement.
About previous comment Sorry it was my mistake. I use ORM over PDO and it did not
work, but clean PDO is working. You can delete this and my previous comment
https://phpdelusions.net/pdo#in 111/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Thanks for great job But unfortunely PDO::FETCH_UNIQUE in mysql is not work as
wrote in article. This options tell PDO return unique value not row indexed by key :(
REPLY:
Such a use case (to get only unique values) would make a very little sense, as any
database can return you unique values already.
Besides, all PDO fetch modes are irrelevant to database backend, working exactly the
same way for all databases including mysql.
I tested this code many times and always get the result as shown in the example.
What is your setup? Do you have an unique value listed first in the field list in your
query?
Hi, thanks for the excellent article. It gives some really helpful advice even for a little
advanced developer.
REPLY:
Thank you for your kind feedback! This kind of response helps to keep the thing
going. Check back for updates!
Great article. It convinced me to switch from mysqli to PDO. There is just one detail
that bother me, about the prepared statements and LIKE clauses. I may be wrong, but
I'm pretty sure this don't work as intended:
$search2 = "%$search%";
$stmt = $pdo->prepare("SELECT * FROM table WHERE name LIKE ?");
$stmt->execute([$search2]);
$data = $stmt->fetchAll();
PDO will escape the two extra '%' in $search2 as it will escape every other '%' that
may have been already in $search. It's the same thing for this:
https://phpdelusions.net/pdo#in 112/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
REPLY:
This confusion is very easy to clear: pdo won't escape % symbol and has not a single
reason to do so. Therefore all the above codes would work.
Your variant is perfectly legitimate too, but to me it's just too lengthy.
REPLY:
I mean what do you think about using for simple task smth like
http://stackoverflow.com/a/27826114/1767461
(http://stackoverflow.com/a/27826114/1767461)
REPLY:
There is a serious flaw in the answer by the link provided, as it doesn't sanitize the
field names.
https://phpdelusions.net/pdo#in 113/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
You may find a correct solution in this article, in the following section:
https://phpdelusions.net/pdo#identifiers (https://phpdelusions.net/pdo#identifiers)
Please ask if you find anything unclear or if you have any further questions.
Hi! I would like you to put some simple active record function, to update and insert
assoc array
sorry, i wasn't clear. i was asking about what actually gets sent to mysql/maria
(hereafter, myria). your comment about the artificial query made me curious about
what PDO actually sends. i used wireshark to expose the conversation (WS is your
friend :) and it appears the completely prepared query is sent to myria.
so, now, i'm a bit more confused - can you illucidate? (i'm really NOT trying to make a
point here, it's just that inquiring minds need to know :)
REPLY:
Got you. It's ok with your confusion. I understand the feeling. You just need to put all
the tiles together. Either way, I don't mind.
If you are using PDO with default settings, then emulation mode is used, when PDO
is sending a regular SQL with all the values put i the query. It occurs when execute()
method is called.
If you are already turned emulation off and still observing SQL with data substituted -
then it is something unusual.
https://phpdelusions.net/pdo#in 114/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
last question (promise) - you say that the query in mysql log is "an artificial query that
has been created solely for logging purpose, but not a real one that has been
executed" - exactly what is sent to mysql?
REPLY:
Well, first of all please promise to ask more. When asking, you are helping me to
make this article better.
When in native mode (i.e. when emulation is turned off) the exact query with
placeholders is sent. I.e. if you wrote
then exactly the same query is sent to mysql and executed. This is how native
prepared statements are intended to work. You may read my answer with explanation
here: http://stackoverflow.com/a/8265319/285587
(http://stackoverflow.com/a/8265319/285587)
thanks - in practice, never. but i ran across this catch-22 behavior experimenting with
PDO in phpsh quite by accident. i thought there might be some way to recover, but, at
the time, i had to exit phpsh then re-connect.
i take it that, short of using buffered queries (or script exit), using
PDO::query('SELECT something') will get you into a position where you can't recover,
since there's no PDOStatement on which to do a fetch/fetchAll(). or did i miss
something? i assume the situation clears on script exit and/or connection loss.
tnx for the write up, both here and in SO - i refer to your thoughts frequently.
REPLY:
https://phpdelusions.net/pdo#in 115/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Yes, you are right. Everything that was associated with the connection (unfetched
resultsets, prepared statements, transactions etc.) will be cleared up on the
connection close (and PHP will close a connection on exit).
However, I am not quite getting your point, why would you run a query without
assigning the result to a variable.
You have an error in text: for all undefined properties get magic method will be
called if there is no get method in the class, then new property will be created
PDO doesn't execute get, instead it calls set to set values. Cheers ;)
REPLY:
Thank you so much for this brilliant document. It really covers most of the things in an
inspiring way.
REPLY:
If you have any questions, I'll be glad to answer, as answering questions lets me
make the article more clear and useful.
One other downside to unbuffered queries is you can't run any other queries until the
current query has returned all of its results. That's why it is more convenient for the
user to have PHP retrieve all the rows of a dataset before iterating over them - doing
so allows other queries to be run inside the while loop.
https://phpdelusions.net/pdo#in 116/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
REPLY:
Thanks a lot for the reminder. Surely it's a really important issue. Added it to the text.
Great article - love the gotchas at the end. I would love to see an addition dealing with
the INSERT... ON DUPLICATE UPDATE procedure (which I'm currently exploring)
and how the single statement would take x number of bound variables on the INSERT
with additional variables if the UPDATE procedure is sent.
REPLY:
Hello!
I am in doubts whether to add on duplicate case or not, as it's not directly related to
PDO. Either way the answer is sumple.
This kind of query has a neat feature exactly for your case. You can refer to an insert
part value using values() function (which is different from regular values()):
Insert into t (foo, bar) values (?,?) On duplicate key update bar= values(bar);
So you have to bind your variables only once. Hope you get the idea.
This tutorial is a hackers wet dream. The statement $pdo = new PDO($dsn, $user,
$pass, $opt);
https://phpdelusions.net/pdo#in 117/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
could possibly dump your complete login details (server, user, pw, etc) if there is any
issue with connecting to the db (timeout or whatever). This info is contained in the
exception. I stopped reading after that statement, I don't care if it is mentioned later
on, YOU SHOULD NEVER send that command naked. I know what you are going to
say later on but what I wrote is 100% true and I tested it.
REPLY:
PHP has its reputation due to users. Who is ignorant about most basic PHP settings.
display_errors for instance. Which have to be turned OFF on a live site - a PROPER
way for hiding error messages.
ALL messages, mind you, not only one you've accidentally became aware of.
Thanks for the great article! I've been modifiying my code already!
A few things:
"but most of time it's all right and won't cause any problem. E.g." There
is no example after that.
Although you can tell rowCount() Font change for ")" stops too soon ;p
Hi, I'm kinda new to PDO and would like some advice.
Right now I have three different tables in three different & separate databases (2
PGSQL, 1 MSSQL) that I have to INSERT into for user registration. This means I
have 3 PDO connections, now all three INSERT are a bit different:
SQL 1: INSERT into user (Name, Password, Age) SQL 2: INSERT into users
([LOGINNAME], [PASS], [OCCUPATION]) SQL 3: INSERT into users (Name,
Address, Position)
https://phpdelusions.net/pdo#in 118/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
Now what I want to do is to somehow rollback the INSERTs if one or more fails to
insert, and commit only if all three can be inserted? Can I rollback or commit outside
of the try catch? or do I put all of them in one huge try catch block? or is there a better
way that I can approach this?
REPLY:
Hi!
It's impossible to have a proper transaction for the separate connections. If you
commit outside of try catch, there could be an error during commit itself - yet you'll be
unable to rollback.
But that's better than nothing - so, I think that you have to commit outside of try catch.
You may also want to add another try catch to wrap commits, and try to recover
successful inserts somehow.
Please tell about PDO::FETCH_CLASSTYPE, it comes handy when you store objects
of different subclasses in a single table
Not covered topic with inserting batches. E.g. INSERT INTO MyTable (col) VALUES
(1),(2),(3); AND INSERT INTO MyTable (col) VALUES (1),(2),(3),(4); Is two separate
prepared needed? $q1 = $pdo->prepare("INSERT INTO MyTable (col) VALUES (?),
(?),(?),(?)"); $q1 = $pdo->prepare("INSERT INTO MyTable (col) VALUES (?),(?),(?)");
https://phpdelusions.net/pdo#in 119/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
REPLY:
@Ry nothing wrong with doing so, if you actually handle the error. If you are just
echo'ing out the error message, you are better off not catching anything at all, you will
get more information that way. And you can then turn off display_errors in production.
great article, but what's wrong with wrapping pdo code by try - catch? I thought it was
the way one handles exceptions in PHP?
REPLY:
Thank you
This article pretty much clarified the proper useage of PDO, but sometimes it got over
my head. old habits from mysqli_*
https://phpdelusions.net/pdo#in 120/121
12/11/2018 (The only proper) PDO tutorial - Treating PHP Delusions
I'm trying to go from old, raw mysql to abstraction. PDO looks like a good path.
Thanks !
I am robot
good
https://phpdelusions.net/pdo#in 121/121