How to handle PDO connection errors properly? - php

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().

Related

When should you prepare and execute using `try` and `catch` using PDO?

I have been using PDO for a couple of years now but I have never fully researched when you should prepare and execute using try and catch.
My understanding is that you should use try and catch when data may contain user input.
So this code for example is safe:
public function getDetails($filename, $what){
$query = $this->handler->prepare('SELECT * FROM videos WHERE v_fileName = :v_fileName');
try{
$query->execute([
':v_fileName' => $filename
]);
}catch(PDOException $e){
return $e->getMessage();
}
}
$filename in this example is something which comes from the URL.
When not getting anything from the URL for example like this it is also completely save:
$query = $this->handler->prepare('SELECT * FROM videos WHERE u_id = :u_id ORDER BY v_id LIMIT :climit,1');
$query->execute([
':u_id' => $this->user->getChannelId($userid),
':climit' => $optional[1]
]);
$fetch = $query->fetch(PDO::FETCH_ASSOC);
Is my understanding of preparing statements correct and if not, how should I do it?
Only when you have a very good reason to do so.
This doesn't apply to only PDO exceptions. The same goes for any exception. Only catch the exception if your code can recover from it and perform some other action instead.
Catching exceptions just to echo or return $e->getMessage(); is not a valid reason. Your code doesn't recover from the problem, you are just handicapping the exception.
A good example of when you might want to recover is if you are using database transactions and in case of failure, you want to rollback and do something else. You can call PDO::rollBack() in your catch and then make your code perform some alternative logic.
Try-catch is not a security measure. It has nothing to do with user input. It is used only in situations when you expect your code to fail, but you have a plan B to handle the situation.
For more information, you can read My PDO Statement doesn't work and the article PHP error reporting
Is my understanding of preparing statements correct and if not, how
should I do it?
You use prepared statement to avoid SQL INJECTION.
Prepared statements will quote the parameters to avoid it.
My understanding is that you should use try and catch when data may
contain user input
The try catch block is used to handle erros in your application, not really related to prepared statements.

die() or try/catch when interacting with MySql database in PHP?

A lot of tutorials and books I have been over and read have used the die() method to catch an exception when interacting with a local MySQL database
For example:
mysql_connect($dbhost, $dbuser, $dbpass)or die(mysql_error());
Would a try/catch block be more beneficial over the die() method or is that just the standard way that exception handling works with db connections?
or die() is an extremely primitive way to "handle" errors and only for examples or debugging at best. In practice, it depends on how you handle your errors. You may want to return false from a function call or you may want to throw your own exception instead; e.g.:
if (!$con = mysql_connect(..)) {
throw new DatabaseConnectionError(mysql_error());
}
try..catch will do exactly nothing with mysql, since mysql never throws any exceptions. It only ever returns false on failure.
You will have to have your own error handling strategy. You'll probably want to log errors and display a user friendly error page instead of cryptic error messages. mysql is not concerned with that part. It only gives you a way to check whether an operation was successful or not (check if it returns false); what you do with this information is up to you. die kills the entire application and at least doesn't allow the problem to propagate further; but it certainly does not display any user friendly error pages.
Having said all this, mysql is old and deprecated. If you'd use something newer like PDO instead, it can properly throw exceptions itself.
The mysql_connect method does not throw exceptions and thus die() is used by many applications to terminate when there is no connection available.
You can use the solution mentioned here: how to use throw exception in mysql database connect
Included for completeness:
try
{
if ($db = mysqli_connect($hostname_db, $username_db, $password_db))
{
//do something
}
else
{
throw new Exception('Unable to connect');
}
}
catch(Exception $e)
{
echo $e->getMessage();
}
Alternatively use the new and more OOP styled database access: http://php.net/manual/en/book.pdo.php
The reason a lot of applications uses die is from the fact that they are so reliant on the database that continuing without a connection is utterly fruitless.
Edit As mentioned in the comments, the code example above is for illustrational purposes. Catching right after throwing is pointless.

Handling errors in PDO when MySQL query fails

I've recently changed all my mysql.* to PDO. I've been having a few issues and trying to get used to it.
My question is how to properly call an error when there is an issue in the sql statement. Normally I wouldn't use try, catch but is there another alternative?
Suppose I have the following:
private function init()
{
$query = $this->_PDO->prepare("SELECT * FROM here WHR name='john'");
if($query->execute())
{
$this->_sql_rows = $query->rowCount();
}
else
{
print_r($query->errorInfo());
}
}
This checks whether the execute method worked and if not, output errors. In this case it should as there is a spelling mistake. Normally when I do this, I never see any errors come out and must always use the method above to output an error. Is this a reliable and appropriate way of handling such errors?
Nope, it is not.
Just like almost every PHP user, you have quite vague and uncertain idea on error handling. And the code mostly looks like a dummy code from sandbox example. Speaking of your current approach, frankly - it's just terrible. On a live site it will only scare an innocent user, while webmaster would have no idea what's going on.
So, first of all you have to make your mind what is error handling you are looking for.
There are different possible scenarios, but most convenient is just to follow the way PHP handles all other errors. To do that just set PDO in exception mode. This way you'll be always notified of all the errors occurred. So - just add this line right after connect.
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
and thus your function become short and clean
private function checkUser($user)
{
$stmt = $this->_PDO->prepare("SELECT 1 FROM users WHERE name=?");
$stmt->execute(array($user));
return $stmt->fetchColumn();
}

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.

Custom mysqli prepare function

I'm doing my first own database class at the moment and currently I'm doing the prepare function.
What this function does is to take in an SQL-query and then an array containing the variables for the statement. I'm having problems with binding the parameters to the statement.
This is how the function looks like now
public function prepare($query, $var) {
$types = '';
foreach($var as $a) {
$type = gettype($a);
switch($type) {
case 'integer':
$types .= 'i';
break;
case 'string':
$types .= 's';
break;
default:
exit('Invalid type: ' . $a .' ' . $type . '(' . count($a) . ')');
break;
}
}
$stmt = self::$connection->prepare($query);
$stmt->bind_param($types, $var); // How do I do here??
$stmt->execute();
$result = $stmt->get_result();
while($row = $result->fetch_assoc()) {
print_r($row);
}
}
Everything works as I want it to (I know this function could do some polishing but it does what it needs to do). I have commented the line where I'm having trouble figuring out what to do. $var is an array and if I recall things correctly the variables needs to be passed seperately seperated with a comma. This is where I'm clueless.
The very idea of your own database class is great.
Very few people here do share it, for some reason prefers raw api calls all over their code.
So, you're taking great step further.
However, here are some objections:
Don't use mysqli for your first own database class if you're going to use native prepared statements.
Use PDO instead. It will save you a ton of headaches.
Despite of the fact this function works all right for you, it makes not much sense:
switch($type) code block is useless. Mysql can understand every scalar value as a string - so you can bind every value as s with no problem.
most integers coming from the client side have string type anyway.
there are some legitimate types like float or NULL or object that can return a string. So, automation won't work here. If you want to distinguish different types, you have to implement type hinted placeholders instead.
Never use exit in your scripts. throw new Exception('put here your error message') instead.
This is not actually a prepare function as it does execute as well. So, give it more generic name
But now to your problem
It is direct consequence of using mysqli. It is a nightmare when dealing with prepared statements. Not even only with binding but with retrieving your data as well (because get_result() works not everywhere, and after creating your application locally you will find it doesn't work on the shared hosting). You can make yourself an idea looking at this bunch of code served for this very purpose - to bind dynamical number of variables.
So, just keep away from mysqli as far as as you could.
With PDO your function will be as simple as this
public function query($query, $var=array())
{
$stmt = self::$connection->prepare($query);
$stmt->execute($var);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// and then used
$data = $db->("SELECT 1");
print_r($data);
You can take a look at my class to get some ideas.
Feel free to ask any questions regarding database classes - it's great thing, and I am glad you're going this way.
To answer questions from the comments.
To let you know, you're not the only user of the site. There are also some innocent visitors. Unlike you, they don't need no error messages, and they get scared with some strange behavior and lack of familiar controls.
exit() with error message does many evil things
throws an error message out, revealing some system internals to a potential attacker
scaring innocent user with strange message. "What's that? Who is invalid? Is it mine fault or what? Or may be it's a virus? Better leave the site at all" - thinks them.
killing the script in the middle, so it may cause torn design (or no design at all) shown
killing the script irrecoverably. while thrown exception can be caught and gracefully handled
When connecting to PDO, no need to throw anything here as the exception already thrown by PDO. So, get rid of try ... catch and just leave it one line:
self::$connection = new PDO($dsn, $user, $pass);
then create a custom exception handler to work in 2 modes:
on a development server let it throw the message on the screen.
on a live server let it log the error while showing generic error page to the user
Use try ... catch only if you don't want to whole script die - i.e. to handle recoverable issue only.
By the way, PDO don't throw exception on connect by default. You have to set it manually:
$opt = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION );
self::$connection = new PDO($dsn, $user, $pass, $opt);

Categories