CodeIgniter not catching DB errors while using PDO as driver - php

I'm using CodeIgniter 2.1.3, and I'm using the active record class with PDO as my database driver.
I came across an issue where CodeIgniter isn't reporting errors with incorrect update statements. I can write a query which clearly violates duplicate keys or has incorrect column names, but CodeIgniter gives no error whatsoever with:
echo $this->db->_error_message(); //returns nothing
echo $this->db->_error_number(); //returns '0000'
However, as soon as I change over to using MySQL as the driver, I receive error messages.
I've even tried $this->db->query(.... //violating statement to bypass the active record class with no luck and more errors.
I have $db['default']['db_debug'] = TRUE;.
Am I out of luck using PDO, or are there some additional config settings that need to be changed when using PDO with CodeIgniter?

If you look up the PDO driver, you can see that the PDO connection is done with PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT which does not throw out any errors.
In general you want PDO to throw exceptions, in which case you pass the below with the connection options.
array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false
);
If you want to use PDO, use PDO direct. PDO is a good abstraction layer. If you are going to be using the CI DB Driver anyway, I don't see any real advantages of selecting this over mysql or vice-versa, because you will be writing your queries the CI way regardless.

Related

How to handle PDO connection errors properly?

I try to handle or catch possible errors with PHP/MySQL for security reasons and would like to know if I'm doing it right.
The first case: I use it as a function and call it always when I need a database connection.
The second case: I am not sure how to handle it. I put all prepared statements in if conditions but that's pretty awkward.
And what about $stmt->execute? This could also fail and to handle this also in an if condition can really get confusing.
I hope there is a better way to go.
First:
function pdo () {
try {
$pdo = new PDO('mysql:host=localhost;dbname=dbname', 'user', 'pw');
}
catch(PDOException $e) {
header("Location: handle_error.php");
exit;
}
return ($pdo);
}
Second:
if ($stmt = $pdo->prepare("SELECT a FROM b WHERE c = :c")) {
$stmt->execute(array(':c' => $c));
$result = $stmt->fetch();
echo 'All fine.';
}
else {
echo 'Now we have a problem.';
}
Your current code has some flaws. Let me give you few pointers.
You do not need the function you have created, at least not in the current form. Every time you call this function it creates a new PDO object, which can hinder your script's performance. Ideally you would want to have only one connection throughout the execution of your whole script.
When creating new PDO connection you need to remember 3 things: to set proper connection charset, enable error reporting, and disable emulated prepares.
Proper charset is important. Without it your data could get corrupted or even leave you vulnerable to SQL injection. The recommended charset is utf8mb4.
Enabling error reporting saves you the trouble of manually checking every single function call for failures. You just need to tell PHP to trigger exceptions when an error occurs and find a suitable way of logging the errors on your server (read more: My PDO Statement doesn't work)
Emulated prepares are enabled by default, but the truth is you are better off without them. If your database supports native prepared statements, and most do, then use them instead.
The full code should look something like this:
$options = [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new \PDO('mysql:host=localhost;dbname=dbname;charset=utf8mb4', 'user', 'pw', $options);
Don't catch the exceptions unless you know what to do with them and are positively sure you need to do so. Exceptions are best left be; let PHP handle them together with the other exceptions/errors/warnings. After all why make an exception just for PHP exceptions? All PHP errors should be handled the same. Whatever you do never print (including die, exit, echo, var_dump) the error message manually on the screen. This is a huge security issue if such code ever makes its way into production.
If your PDO connection is set to throw exceptions on errors, you never need to use if statements to check return code of prepare() or execute().

Can PDO methods fail and not throw PDOException?

I'm tweaking a legacy database class written for PHP/5.2 that was designed to connect to MySQL and hide all errors. I've configured the PDO instance to throw exceptions:
new PDO($dsn, $user, $pass, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION))
Now I need to adjust my code accordingly to handle the case where functions can throw an exception where they wouldn't before so I head to the manual.
In some cases the manual is explicit, e.g. PDO::prepare:
If the database server successfully prepares the statement,
PDO::prepare() returns a PDOStatement object. If the database server
cannot successfully prepare the statement, PDO::prepare() returns
FALSE or emits PDOException (depending on error handling).
In other cases it's kind of vague, e.g. PDO::commit:
Returns TRUE on success or FALSE on failure. Throws a PDOException
if there is no active transaction.
(Can it fail for some other reason and simply return false?)
And there're cases when exceptions are not even mentioned, e.g. PDO::bindValue:
Returns TRUE on success or FALSE on failure.
... even though it's clear to verify that it does throw PDOException on error (at least, on certain errors).
Do I still need to check the return value of methods that return false on error when the manual doesn't say otherwise?
No, when you set PDO::ERRMODE_EXCEPTION there will be always exception for any errors
When you have exceptions enabled and there is an error happening in your code, the code directly stops executing from that function, so it wont return anything.
So to be blund, you don't need to check the return values but if you don't you cannot error anything specific and you would rely fully on PDO to error correctly.
When I created my database system, I take advantage of both if I see an error upcoming, I throw it myself instead. For example $pdo->prepare('') is very much valid but will error upon binding.
Then there are other functions such as fetch that wont error if there are no results in the database, not checking the results from that would be silly.
Now for committing to fail, I believe there is 1 scenario that would cause it to return false without throwing an exception and that is when the connection to the server drops after connecting to the database and before calling PDO::commit, quite good to know if you have a remote database server.
So to answer your question, yes it can fail without throwing an exception, but its timing has to be very specific even more so if you have a local database.
Try this one
try {
$db = new PDO("mysql:host=localhost;dbname=dbname", "username", "password");
}catch( PDOException $Exception ) { $Exception->getMessage( ) ; }

How to set PDO options?

I need to use silent PDO mode without any exceptions or errors so i do it like this:
$this->db = new PDO($db_config['dsn'], $db_config['username'], $db_config['password'],
array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,
PDO::ATTR_TIMEOUT => 5,
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
)
);
But it still throws exception that i cant connect to db.
I need to use silent PDO mode
Nope, you don't.
You are asking this question out of some false assumption. Instead of following it further, you have to rethink your premises, set PDO to Exception mode and then fix something else. Most likely - an error handler.
If you cannot connect to the database, PHP can't create a PDO object. You can't have a PDO object which is not connected to the database. You can't return anything but a valid object instance when using the new operator. Hence an exception is the only thing how the PDO constructor can fail here.
In other words, you can't silence an exception happening at construction time, that setting only applies to all later errors PDO may produce. You just have to catch it.

PDO Update Statement Not Working

I've browsed the web quite a bit before asking here; I noticed that some people have the same problem as me, but none of the answers that were given to the others didn't solve my problem, so....
I have a basic PDO Update Statement inside a public function:
public function editRank($name, $rank){
$query = "UPDATE `chat_mod` SET `chat_mod_rank` = :rank WHERE `chat_mod_ign` = :username";
$prepare = $this->_db->prepare($query);
$array = array(
':rank' => $rank,
':username' => $name
);
try {
$prepare->execute($array);
} catch (PDOException $e){
echo 'Error: ' . $e->getMessage();
return false;
}
return true; // If no PDO Exception is thrown..
}
No exception is thrown, so the function always returns true; but the rows are not being updated. Yes, I've checked that the rows are named properly and the values are not nulls.
Thanks,
Tom.
P.S Other queries like Select, Add & Delete work fine.
You are catching PDO exceptions but did you tell PDO to throw them?
To make PDO throw exceptions you have to configure PDO errmode. Note that setting this mode as a connection option will let PDO throw exceptions on connection errors too, which is very important.
So, here is an example for creating a PDO connection right way:
$dsn = "mysql:host=$host;dbname=$db;charset=utf8";
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// other options
);
$pdo = new PDO($dsn, $user, $pass, $opt);
Connecting this way, you will be always notified of all database errors, occurred during query execution. Note that you have to be able to see PHP errors in general. On a live site you have to peek into error logs, so, settings have to be
error_reporting(E_ALL);
ini_set('display_errors',0);
ini_set('log_errors',1);
while on a local development server it's ok to make errors on screen:
error_reporting(E_ALL);
ini_set('display_errors',1);
and of course you should never ever use error suppression operator (#) in front of your PDO statements.
Also, due to many bad examples telling you to wrap every PDO statement into try..catch block, I have to make a distinct note:
DO NOT use try..catch operator just to echo an error message. Uncaught exception is already excellent for this purpose, as it will act just the same way as other PHP errors - so, you can define the behavior using site-wide settings - so, you will have your error message without this useless code. While unconditionally echoed error message may reveal some sensitive information to a potential attacker, yet confuse a honest visitor.
A custom exception handler could be added later, but not required. Especially for new users, it is recommended to use unhandled exceptions, as they are extremely informative, helpful and secure.
Use try..catch only if you are going to handle the error itself - say, to rollback a transaction.

How do I check the DB connection

I have host, database name, username and the password given as form inputs like:
$form['dbname']->getData()
I need to check if these data is correct for the mysql connection. So I chose to use mysql_connect() to check this:
$conn = mysql_connect($form['host']->getData(), $form['username']->getData(), $form['password']->getData(), $form['dbname']->getData());
if($conn) // ...
else // ...
But it displays some mysql_connect() warning with no other specific message...
What's wrong? Does the symfony 2 has any mechanism to check the connection?
Symfony uses repositories and entities to manage the database. The programmer doesn't manipulate the connection itself (Doctrine).
You could try to check a connection using PDO. Try to instance a PDO Object, and catch exceptions (PDOException for connection errors). However, you're "breaking" the framework's philosophy, trying to initiate an "extern" DB connection. If you need to work with several connections in Symfony, I suggest this reference :
http://symfony.com/doc/current/cookbook/doctrine/multiple_entity_managers.html
use the following format to receive the connection error if there is any.
$con = mysql_query() or die(mysql_error());

Categories