Link to home
Create AccountLog in
Avatar of elepil
elepil

asked on

PHP PDO question on how to use try/catch block

I just learned that PDO and mysqli both do NOT throw exceptions by default. I learned that to make mysqli throw exceptions, one has to have this line of code:

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

Open in new window


and in PDO, one has to have this line of code:

$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Open in new window


There is one very significant difference between the two. In mysqli, that line of code is done BEFORE obtaining a database connection. In PDO, that line of code is done AFTER obtaining a database connection. In fact, PDO requires an already successful database connection before one can even tell it to throw exceptions.

So here is my question. In PDO, how can I use the try/catch block to catch an incorrect user/password combination or incorrect database name while connecting to the database, an error condition that happens BEFORE having obtained a database connection?
ASKER CERTIFIED SOLUTION
Avatar of Chris Stanyon
Chris Stanyon
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
Create an account to see this answer
Signing up is free. No credit card required.
Create Account
Oops - forget to explain why :)

The PDO::ATTR_ERRMODE tells PDO how to handle errors on the database connection.

The PDO constructor (new PDO(...) ) throws a PDOException when there's a problem. The setAttribute() method doesn't effect that at all.
Avatar of elepil
elepil

ASKER

Chris, your code snippet demonstrates exactly what I did originally and it didn't work. Notice you did your $dbh->setAttribute() AFTER the creation of the PDO object. To use the setAttribute method, you needed to have a valid PDO reference in $dbh to begin. But if the user/password is wrong, at that point, PDO doesn't know YET it was supposed to throw an exception because the line that tells it to do so is the next line.

This is what I get when my code tries to execute the $dbh->setAttribute() method:

Fatal error: Call to a member function setAttribute() on a non-object in C:\xampp\htdocs\newdimension\public\ndLogin.php on line 9

It's complaining that $dbh is a "non-object", and hence, does not have a member function called setAttribute().
I think you're confusing 2 different error handling routines. The constructor of PDO will throw an exception if something is wrong. That has nothing to do with setting the ATTR_ERRMODE attribute. It's just the normal way the constructor works - if it fails, it throws an exception!

This is why you should always wrap the PDO constructor in a try/catch block. If the constructor fails, you can catch the exception. In the code I posted, if there was a problem with creating the connection, such as a bad username or password, then a PDOException would be thrown - the code would jump into the catch block and the setAttribute line would never be executed.

Maybe this code will make things clearer:

try {
	$dbh = new PDO('mysql:host=localhost;dbname=someDB', 'username', 'wrongPassword');
} catch (PDOException $e) {
	// There was a problem creating the connection so lets kill the script with some feedback to the user
	die($e->getMessage());
}

// If we get here then we know that $dbh has been created successfully
// We can now explicitly set how database errors are handled for this connection
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

Open in new window

Avatar of elepil

ASKER

Oh sorry, Chris, my mistake. You're right. It was working on my side, just didn't do the code right to display the catch error message properly. The new PDO() was throwing an exception. Thanks for your help.
Maybe this will help.  Step through the code and watch it in action.
http://iconoun.com/demo/temp_elepil.php

<?php // demo/temp_elepil.php
ini_set('display_errors', TRUE);
error_reporting(E_ALL);
echo '<pre>';

// THE ABSOLUTE MINIMUM YOU MUST UNDERSTAND TO USE PHP AND PDO/MYSQL
// MAN PAGE: http://php.net/manual/en/book.pdo.php
// MAN PAGE: http://php.net/manual/en/class.pdo.php
// MAN PAGE: http://php.net/manual/en/class.pdoexception.php
// MAN PAGE: http://php.net/manual/en/class.pdostatement.php
// MAN PAGE: http://php.net/manual/en/pdo.construct.php
// MAN PAGE: http://php.net/manual/en/pdo.setattribute.php
// MAN PAGE: http://php.net/manual/en/pdo.query.php
// MAN PAGE: http://php.net/manual/en/pdo.prepare.php
// MAN PAGE: http://php.net/manual/en/pdo.prepare.php#97942 <-- NO CURSOR SCROLLING
// MAN PAGE: http://php.net/manual/en/pdostatement.execute.php
// MAN PAGE: http://php.net/manual/en/pdo.lastinsertid.php
// MAN PAGE: http://php.net/manual/en/pdostatement.bindparam.php
// MAN PAGE: http://php.net/manual/en/pdostatement.rowcount.php
// MAN PAGE: http://php.net/manual/en/pdostatement.fetchall.php
// MAN PAGE: http://php.net/manual/en/pdostatement.fetchobject.php


// DATABASE CONNECTION AND SELECTION VARIABLES ARE INCOMPLETE HERE
$db_host = "localhost";
$db_name = "foo";
$db_user = "bar";
$db_word = "baz";


// WARNING: AN UNCAUGHT CONNECT ERROR WILL BARK OUT THE DB CREDENTIALS!
// $pdo = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, 'foo');

echo '<h1>FAILURE</h1>';
// OPEN A CONNECTION TO THE DATA BASE SERVER AND SELECT THE DB
$dsn = "mysql:host=$db_host;dbname=$db_name";
try
{
    $pdo = new PDO($dsn, $db_user, $db_word);
}
catch(PDOException $exc)
{
    var_dump($exc);
    trigger_error($exc->getMessage(), E_USER_WARNING);
}

// SHOW THE (FAILED) PDO CONNECTION OBJECT
var_dump($pdo);
echo PHP_EOL;


// NOW TRY IT AGAIN WITH THE RIGHT CREDENTIALS
require_once('RAY_live_data.php');

echo '<h1>SUCCESS</h1>';
// OPEN A CONNECTION TO THE DATA BASE SERVER AND SELECT THE DB
$dsn = "mysql:host=$db_host;dbname=$db_name";
try
{
    $pdo = new PDO($dsn, $db_user, $db_word);
}
catch(PDOException $exc)
{
    var_dump($exc);
    trigger_error($exc->getMessage(), E_USER_WARNING);
}

// SHOW THE (HAPPY) PDO CONNECTION OBJECT (ENTIRELY EMPTY!)
var_dump($pdo);
echo PHP_EOL;

Open in new window