PDO Error Management

<?php 
$mysql = 'mysql:host=localhost;dbname:toolcula_apps';
try {
  $db  = new PDO($mysql,'toolcula_apps','##');
  $sql = 'SELECT car_id, make, yearmade, mileage, transmission,
  price, description FROM cars
  INNER JOIN makes USING (make_id)
  WHERE yearmade > 2008
  ORDER BY price';
  $result = $db->query($sql);
  if ($db->error) {
    $error = $db->error;
  }
  
} catch (PDOException $e) {
  $error = $e->getMessage();  
}

if (isset($error)) {
  echo $error;
}else{
  echo 'The DB connection is established';
}

I was going through a PDO course, and I encountered such a PDO system.
I have difficulty understanding one thing: If we are using Try Catch system for a PDO why we are using another error management system:

if (isset($error)) {
  echo $error;
}else{
  echo 'The DB connection is established';
}

Yeah that doesn’t make sense

} catch (PDOException $e) {
  echo 'The DB connection is established';
}

That would have worked as well, without the need for an additional variable

1 Like

But arent we missing this here:

$error = $e->getMessage();
This should have some additional error messages, in case there are some messages.

There’s no good reason for your application code to try to handle a database statement error unless the visitor to your site can do something about correcting the error. The only errors a visitor to your site can do something about are when submitting duplicate or out of range data values for an insert or update query. In all other cases, telling a visitor that something they did caused a database statement error will only confuse them and if the visitor happens to be a hacker, telling them that something they did causes a specific type of error will only encourage them to do more of the same. Unconditionally outputting the raw database statement error will give hackers even more useful information (a connection error contains the database host/ip address, the connection username, if you are using a password or not, and web server path information.)

When you, as the programmer/developer, visit your site, you do want to see the raw database statement errors. The simple way of doing this is to use exceptions for errors and in most cases let php catch and handle the exception, where php will use its error relate settings to control what happens with the actual error information, in the form of an uncaught exception error (database statement errors will ‘automatically’ get displayed/logged the same as php errors.) The exception to this rule are for cases where the visitor can do something about the error, as stated above. In this case, your code should catch the exception, test if the error number is for something your code is designed to handle, setup and display a message for the visitor telling them exactly what was wrong with the data that they submitted. For all other error numbers, just re-throw the exception and let php handle it.

One of the great points of using exceptions is your main code will only ‘see’ error free execution, since execution transfers to the nearest correct type of exception handler upon an error.

The PDO connection always uses an exception for a connection error. You should set the PDO error mode to exceptions so that all other database statement errors (query, prepare, execute, exec, …) will also use exceptions.

Short version: use exceptions for all database statement errors and only use a try/catch block when your application/visitor can do something to correct a specific error. Keep It Simple (KISS.)

BTW - when you make the PDO connection, in addition to setting the error mode to exceptions, you should set emulated prepared queries to false, and set the default fetch mode to assoc.

4 Likes

Ah yes, you’re correct. I still would not use that $error variable though. Way too confusing.

At any rate, @mabismad is completely correct that you should not show the exact error to the user, but rather log it somewhere.

3 Likes

Is it possible you misunderstood the course? PDO itself can be configured to throw exceptions or to use regular error handling. Perhaps you conflated the two approaches?

Keep in mind that when PDO was first introduced, PHP Exceptions were fairly new. Lots of code relied on conventional error checking. Old tutorials might still use them. Nowadays I think it is safe to say that Exceptions are standard.

And no, there is no reason to attempt to use both approaches. The code you posted might be trying to cover both ways of configuring PDO. But I can’t think of any reason why you would do such a thing.

1 Like

The only PDO exception I ever use is for checking for a duplicate username in the database table other than that I just do regular PHP error checking and never display what error(s) my website spits out on a normal server to the user.

I got help for the following script and commented so I remember exactly why I wrote it the way that I did.

    $this->pwd = password_hash($data['password'], PASSWORD_DEFAULT);
    unset($data['password']);
    try {
        $this->query = 'INSERT INTO users (fullName, username, status, password, security, email, date_added) VALUES (:fullName, :username, :status, :password, :security, :email, Now())';
        $this->stmt = static::pdo()->prepare($this->query);
        $this->result = $this->stmt->execute([':fullName' => $data['fullName'], ':username' => $data['username'], ':status' => $data['status'], ':password' => $this->pwd, ':security' => 'newuser', ':email' => $data['email']]);
    } catch (\PDOException $e) {

        /*
         * echo "unique index" . $e->errorInfo[1] . "<br>";
         * 
         * An error has occurred if the error number is for something that 
         * this code is designed to handle, i.e. a duplicate index, handle it
         * by telling the user that they can't use that username 
         * then simply
         * 
         * return false;
         * 
         * else the error is for something else, either due to a
         * programming mistake or not validating input data properly,
         * that the visitor cannot do anything about or needs to know about
         * 
         * throw $e;
         *  
         * re-throw the exception and let the next higher exception 
         * handler, php in this case, catch and handle it
         */

        if ($e->errorInfo[1] === 1062) {
            return false;
        } else {
            throw $e;
        }
    } catch (Exception $e) {
        echo 'Caught exception: ', $e->getMessage(), "\n"; // Not for a production server:
    }

    return true;

Just want to add just say to the user the username can’t be used not because it’s a duplicate. Then maybe give a suggestion. Though I have seen message boards accept duplicate usernames (I think Fox News Does), so even that really doesn’t need to be checked if you are really concerned about security.

1 Like

@mabismad,

Thanks, I know you tried to give me a good inisght, but I still could not understand each technical insight given by you, and its not your fault I still fail to understand some part in its entirety, but with the passage of time and some more coding experience on real life project I will be comfortable with what you said.

BTW: I am not yet coding any live project of any commercial imporrtance, but was learling along with an excercice based tutorials on LinkedIn Learning

In future I will avoid “Generic” discussion pertaining to some tutorial because I could not paste their copyright material, and in such absence it becomes difficlut for you guys to interpret things correctly.

1 Like

Going back to the original question you asked -

Because whoever wrote that code doesn’t know/care what they are doing and didn’t define what they were trying to get the code to do before writing it. It actually contains a mistake, in that the PDO class doesn’t have an error property, so that code will produce a php error at that point. There isn’t even a PDO method named error(), so, this is not even a case of some missing ().

To answer the specific question you asked there. That code is using a pdo exception for the connection (a connection always throws an exception upon an error), but is not using exceptions for all the other database statements - query(), prepare(), execute(), exec(), … that can fail. It is expecting you to write out conditional logic to test for and handle any errors from these statements. It is using two different error management systems.

The problem with writing out conditional logic for these other statements, in addition to not giving hackers useful information, is you must now handle all the different possibilities in your code. 1) When learning, developing, and debugging database statements, you would like to display the error information so that you get immediate feedback as to any problems. 2) When on a live/public server, you should always log the error information so that you have a indication of and can then find and fix what’s causing problems. 3) When handling errors that are caused by something the user to the site did (inserting/updating duplicate or out of range values), you need logic to test the error (number), then setup and display a helpful user message telling the visitor what they did that was wrong. 4) You don’t want to be added/removing code every time you move it from one environment to another.

If you instead use exceptions for ALL the database statements that can fail, by setting the error mode to exceptions when/after you make the PDO connection, you end up with one error management system, which also allows most of the special case logic to go away, except for dealing with an error that the visitor caused.

If you make use of what has been written here, for the code at the top of this thread, the following is all you end up with -

<?php

// this is the Data Source Name (DNS). PDO works with 12 different database types. naming this $mysql is not general purpose/reusable
$dsn = 'mysql:host=localhost;dbname:toolcula_apps';

// while it's true that $db is going to be a database connection, what is it actually, its a pdo object. naming it so would make it clearer to anyone reading the code what it is
$pdo  = new PDO($dsn,'toolcula_apps','##');
// set the error mode to exceptions, either by using the $options array parameter in the connection statement or by calling the set attribute method here...

// when using exceptions, your main code only sees error free execution. if you are here, the connection was successful
echo 'The DB connection is established';

// at the point of building and executing a non-prepared query
$sql = 'SELECT car_id, make, yearmade, mileage, transmission, price, description
	FROM cars
	INNER JOIN makes USING (make_id)
	WHERE yearmade > 2008
	ORDER BY price';

// the following returns a PDOStatement object. naming is so would make it clearer to anyone reading the code what it is
$stmt = $pdo->query($sql);
// when using exceptions, your main code only sees error free execution. if you are here, the query was successful and you can test/use the result from the query

Short version: only write code that adds value to what you are doing, i.e. creating a secure web page, that provides a good user experience, that uses simple, general-purpose, reusable code, and either works or tells (display/log) the reason why it doesn’t. If you are creating variables, writing logic, and calling statements that doesn’t add anything useful, all you are doing is working on your typing speed and typing accuracy.

1 Like

I have some more questions related to PDO. Please confirm if my understanding is correct.
I searched an open-source PDO discussion here → https://www.youtube.com/watch?v=4kQqC3M3QXs

It was fast, crunch, and concise.

$stmt = $db->prepare("SELECT * FROM quotes");
$stmt->execute();
while ($row = $stmt->fetch()) {
}

My Understanding:

$stmt = $db->prepare("SELECT * FROM quotes"); → This can be compared to as loading bullet in a gun.

$stmt->execute(); → Analogically this can be compared to firing a bullet from the gun.

$stmt->fetch() → This can be treated as actually now fetching records out of the database when the bullet has hit after execution.

Am I correct or missing something?

I still could not grasp then what does param binding does?

In this example, there are no variables in the query, so there is no need to prepare and execute. It could be shortened to:-

$stmt = $db->query("SELECT * FROM quotes");
while ($row = $stmt->fetch()) {
}

What is also curious about the example, there is no specific selection, but it uses fetch() as opposed to fetchAll().
The query itself could have several results, but fetch() will only get one. You would use fetchAll() to fetch an array of many results.

Back to prepare and execute.
Prepare will tell the database what the structure of the query will look like. It will typically contain placeholders in place of values. Eg:-

$stmt = $db->prepare("SELECT * FROM quotes WHERE id = ?");

Execute tells the database to actually run the query that you sent it in the prepare stage.

Parameter binding tells the database what actual value belongs to the placeholder(s).
But in PDO, there is no need for a separate parameter binding, it can be done at the execute stage by putting an array of values into the execute method. Eg:-

$stmt->execute([$id]);

Then fetch, or fetchAll, will retrieve the results of the query.
Going with your gun analogy, I suppose “fetch” would be sending your dog to pick up and bring back the birds you just shot.

2 Likes
<?php 
declare(strict_types=1);
error_reporting(E_ALL);
ini_set('display_errors', 'true');

$mysql = 'mysql:host=localhost;dbname=toolcula_pdo';
$con   = new PDO($mysql,'toolcula_pdo','##');

$user = $con->query("SELECT * FROM users");
$user = $user->fetch(PDO::FETCH_ASSOC);

echo "<pre>";
var_dump($user);
echo "</pre>";

I have created a simple database, and I was trying a couple of things.

So with the above I got this output:

array(6) {
  ["id"]=>
  string(1) "1"
  ["first_name"]=>
  string(5) "James"
  ["username"]=>
  string(4) "Bond"
  ["email"]=>
  string(24) "james.bond@jamesbond.com"
  ["active"]=>
  string(1) "0"
  ["password"]=>
  string(3) "123"
}

so that means that this will give the first row with the column as an index of an associative array:

fetch(PDO::FETCH_ASSOC);

Couple of suggestions. Tweak your connection code:

$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false,
];
$db = new PDO("mysql:host=localhost;dbname=play;charset=utf8", "user", "pass", $options);

The exceptions option makes it clear that you are using exception handling. The FETCH_ASSOC option means that you no longer have to specify the type of array in your fetch statements.

The emulate option is a bit more subtle. Notice how var_dump is classifying your id column as a string instead of integer? With the emulate option, integer columns will produce integer data which is a good thing.

It’s also clear from your pre markers that you are debugging by refreshing your browser. You can streamline your development efforts by getting comfortable with running php from a console window. Once configured, a simple “php query.php” will run your code. It will save you a bunch of time in the long run.

1 Like

I do not know this. I only so far know that JS can be run and debugged in console window.

Additionally, my previous posts still seem to have doubts. If this gives one row in form of an associative array → fetch(PDO::FETCH_ASSOC) then what will be the next step to fetch additional rows. we should change in its entirety or the loop can work.

I didn’t get this →

He means running PHP as a command-line executable in what’s known as the Interactive Shell. Same thing that happens when you start python or powershell without a script file; it puts you in an interactive ‘console’ that runs commands as you type them.

If it was registering as a number, that would say ["id"]=> int(1). So if you fed what you’ve got back into a Prepared query, it would treat it as a string, and put quotes around it. Which the database engine isnt going to like if it is expecting a number, not a string.

EDIT: My grammar teacher would have my wrists for that post. Get it together, Marc, and write some English.

Read again:-

<?php 
$dsn  = 'mysql:host=localhost;dbname=toolcula_pdo';
$user = 'toolcula_pdo';
$pass = '##';

try {
  $myPDO = new PDO($dsn,$user,$pass);
  $myPDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  var_dump($myPDO); 
  $sqlreference = $myPDO->query("SELECT * FROM notes WHERE id=?");
  $test = $sqlreference->execute([2]);
  // var_dump($test);

  while ($myRow = $sqlreference->fetch()) {
    echo "<h3>".$myRow['title']."</h3>";
    echo htmlspecialchars($myRow['content']). "<br />";
  }
  
} catch (PDOException $e) {
  echo 'Message: <br>'.$e->getMessage();  
}

I still couldn’t figure out the remedy even after searching every possible solution on that error around over the Internet.

Error:

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 '?' at line 1

You do not use the query method when you have parameters. You must prepare the query.

All you need to know can be found here.
https://phpdelusions.net/pdo

2 Likes

Thanks!