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.
Related
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().
We have been going through some old code of ours and we have found some code that looks something like:
try
{
$stmt = $db->prepare($query);
$stmt->bindvalue(1, $id, PDO:ARAM_INT);
$stmt->execute();
$row = $stmt->fetchColumn();
}
catch(PDOException $e)
{
echo "There was an issue with query: ";
print_r($db->errorInfo());
}
Which at first glance we thought looked fine (Even many answers on Stack Exchange give this as example code). Then we looked at the PHP documentation for the errorInfo function and it states that:
PDO::errorInfo() only retrieves error information for operations performed directly on
the database handle. If you create a PDOStatement object through
PDO:repare() or PDO::query() and invoke an error on the statement
handle, PDO::errorInfo() will not reflect the error from the statement
handle
Which, if we understand it correctly, means that if anything goes wrong in any of the statement operations we do, we will not actually print out the error code we are expecting after "There was an issue with the query: ". Is this correct?
In light of this, we started looking for the proper way to do this, we started by looking at the PDOException class documentation which suggests that we might do something like:
try
{
$stmt = $db->prepare($query);
$stmt->bindvalue(1, $id, PDO:ARAM_INT);
$stmt->execute();
$row = $stmt->fetchColumn();
}
catch(PDOException $e)
{
echo "There was an issue with query: ";
print_r($e->errorInfo());
}
My questions are:
Is the above way the proper way of doing this? If not, what IS the proper way of doing it?
Is there any more useful information avaliable by using $db->errorInfo() and $db->errorCode ( or $stmt->errorInfo and $stmt->errorCode ) beyond what you can see from PDOException?
If there IS anything more detailed available-and-useful from those detailed calls, then is there a way to differentiate, by examining the PDOException, whether it was thrown by PDO or by PDOStatement?
The exception may be thrown by either $db->prepare or any of the $stmt operations. You do not know whence the error originated, so you should not guess. The exception itself contains all the information about what went wrong, so yes, consulting it and only it is the only sensible thing to do.
Moreover, it's usually nonsense to try..catch directly around the database call (unless you have a clear plan about something you want to do if this particular database operation fails). In your example, you're merely outputting the error and are continuing as if nothing happened. That's not sane error handling. Exceptions explicitly exist to abort and jump ship in case of a severe error, which means the part of your code which should actually be catching the exception should live several layers up and not have access to $db or $stmt at all (because it's in a different scope). Perhaps you should't be catching the exception at all and have it terminate your entire script (again, unless you expected an error to occur and have a clear plan how to handle it and how to recover your application into a known state). So looking only at the information in the exception itself is, again, the only sensible thing to do.
If there IS anything more detailed available-and-useful from those detailed calls, then is there a way to differentiate, by examining the PDOException, whether it was thrown by PDO or by PDOStatement?
This is only useful if, again, you have any sort of plan for recovery and that plan differs depending on where the error occurred. First of all, I doubt that, but if that's indeed the case, then you'd do something like this:
try {
$stmt = $db->prepare($query);
} catch (PDOException $e) {
// do something to save the day
}
try {
$stmt->bindValue(...)
..
} catch (PDOException $e) {
// save the day another way
}
In other words, you isolate the try..catch statements to smaller parts of your code that you want to distinguish. But, really... if $db->prepare failed, what are you going to do? You can't continue with the rest of the code either way. And what are you going to do differently than if a $stmt method failed? As one atomic unit, you were unable to query the database, period.
The PDOException::$code will give you the more detailed SQLState error code, which may tell you something useful (e.g. unique constraint violation), which is useful information to work with on occasion. But when inspecting that it's pretty irrelevant which specific line threw the error.
I am working on a project with a friend, we are building our own login and registration system for our site which we are creating. I am questioning his skills at coding in PHP with this following code statement:
try {
$stmt = $db->prepare($query);
$results = $stmt->execute($params);
}
catch() {
trigger_error('The query has failed.');
}
I know that the SQL Query which we are going to perform is going to work for logging in the user, that is not the issue here and the reason why that part of the code is not being displayed within the code block above.
This is the very first time which I have saw someone use trigger_error() with PDOExeception $error statement, which was how I was taught to code to begin with.
Should we continue our core login, registration, and all SQL statements this way by using a Try, Catch, and Trigger_Error? Should I change it over to PDOExeception $error?
Neither.
Trigger error makes not a slightest sense here, as uncaught Exception already an error. So, to catch an error only to throw an error is a tautology. Moreover, uncaught Exception contains indispensable stack trace while just ordinary error doesn't.
Neither echo with catch is required. this is but a calloused delusion of PHP folks.
Neither writing four additional lines of code for the every query execution makes no sense as well. I even wrote a dedicated article on the matter - so, I wouldn't repeat myself
So, just rewrite this code to
$stmt = $db->prepare($query);
$results = $stmt->execute($params);
which is all you actually need
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();
}
Is try catch the only way to catch PDO errors to check if a sql performed? Early on, I used to do like below. I don't need the error message, error names etc. Just a true or false. If the sql worked or not. Can this be done in some other way other than using try catch.
Before
$createUser = (insert into table (someCol) values (someVal));
$exeCreateUser = mysql_query($createUser);
if($exeCreateUser)
{ //The SQL query worked well
echo 'All went on well';
}else{
//The SQL query failed!
echo 'Failed';
}
Now
$sql = 'insert into names (names) values (:what)';
$what = "This value";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':what', $what, PDO::PARAM_STR, 5);
$stmt->execute();
Can I do something like I used to do before, instead of using try catch?
From http://www.php.net/manual/en/pdostatement.execute.php
Return Values
Returns TRUE on success or FALSE on failure.
You can use PDO::setAttribute to set PDO::ATTR_ERRMODE to a value other than PDO::ERRMODE_EXCEPTION before calling methods.
If you do this, you need to then check for errors using either PDO::errorCode or PDOStatement::errorCode, whatever is appropriate for each operation.
PDO::errorCode() only retrieves error codes for operations performed
directly on the database handle. If you create a PDOStatement object
through PDO::prepare() or PDO::query() and invoke an error on the
statement handle, PDO::errorCode() will not reflect that error. You
must call PDOStatement::errorCode() to return the error code for an
operation performed on a particular statement handle.
You don't need neither try..catch nor anything else.
In general, you don't need to check if your query were successful or not. On a properly tuned system all queries run smoothly.
But if some query gone wild - it means something went wrong and whole appication have to be halted. So - there is no use for checking every separate query - you just have to catch a thrown exception at application level.
So, there is no use for such a condition at all. A failure on insert doesn't mean failure with CreateUser, but site-wide failure. And you don't need no local check, but just site-wide exception handler and a generic error 503 page