The doc for error handling for fetch() seems unclear and I haven't found an answer anywhere after a lot of searching. A common usage model for fetch as shown in the doc is:
while ( $row = $stmt->fetch() ) {
// do something with $row
}
The PHP doc at http://www.php.net/manual/en/pdostatement.fetch.php says:
In all cases, FALSE is returned on failure.
What does "failure" mean? An error? A failure to fetch more rows? My question is: when fetch() returns FALSE, what is best practice to detect errors? It seems like after the loop I need to distinguish between an error case and the "no more rows" case.
Should I call $stmt->errorCode() and see if it's '00000' ?
To handle query errors (sql errors not occurs on fetch) and more generaly PDO error you should try using PDO::ERRMODE_EXCEPTION.
Moreover the PdoStatement returned by query() implement Traversable , so you can skip the use of fetch() for simple query :
try {
$db = new PDO('sqlite:mydb.db');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Set Errorhandling to Exception
foreach($db->query("SELECT...") as $row)
{
//Do domething
}
$db = null; // "Disconnect"
}
catch (PDOException $err) {
//Handling query/error
}
An execption will be thrown if query encounter an error (not fetch()).
Fetch will always return false when it reach the end of your result set.
fetch() will return false when there's no more data to be fetched. There could also be other case where there truly is a failure, e.g. mysql died or the connection was lost while doing the fetch loop. But generally speaking, that sort of "real" failure is fairly rare, and you'll most often get "false because there's no more data available".
If you're truly paranoid about writing error handling, then by all means, put an sql error code check after the while loop to catch the cases when something really did blow up.
Related
I am using Medoo (a PHP DB framework using PDO) with PDO::ERRMODE_EXCEPTION enabled. I have some queries in a try and catch block and I want to throw an exception if any of those queries return an empty result.
PS: $database->get() is a simple PDO SELECT returning a single row. I don't think it's relevant and I think my example applies also to PDO without frameworks.
try {
$q1 = $database->get(..);
$q2 = $database->get(..);
$q3 = $database->get(..);
$q4 = $database->get(..);
} catch (PDOException $e) {
die ("There was an error in a query.");
}
Right now I get into the catch block only if there is an error in the query, like I try to select a table that doesn't exist. I want to find the optimal way to avoid checking if every single query doesn't return an empty result manually, like I don't want to do this:
if (!$q1) { echo "No result"; }
if (!$q2) { echo "No result"; }
...
Is there a more generic approach?
Your logic is wrong, an Exception is an event that occurs during program execution that disrupts normal flow.
Query that returns an empty result set is not disrupting normal flow of your program, because that query executed successfully.
If you think that it's a good idea to use exceptions in order to signal that an empty result is returned, you have to throw that exception.
Eloquent, which is an ORM used by Laravel contains a method called firstOrFail and findOrFail which perform what you're after.
Your option is to either create such a method for Medoo or use a proper ORM such as Doctrine or Eloquent that can help you achieve such behavior out of the box.
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( ) ; }
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.
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
I'm using mysqli prepared statements in an OOP manner. Everything is working as intended, but I can't seem to find any documentation on how to detect and then throw an exception during a while loop. This how my code stands at the moment and it works perfectly.
$rslt=array();
$data=array();
$stmt->bind_result($rslt['col1'], $rslt['col2'], $rslt['col3'], $rslt['col4']);
while($stmt->fetch()){
$data[]=$rslt;
}
However it contains no way of throwing an exception. This is how I throw an exception in a similar script which is limited to 1 result (therefore I don't need to loop through them on this example).
$rslt=array();
$data=array();
$stmt->bind_result($rslt['col1'], $rslt['col2'], $rslt['col3'], $rslt['col4']);
if(!$stmt->fetch()){
throw new Exception('Failed to fetch results', 1);
}
$data[]=$rslt;
I'm almost trying to do what is in the code below but it throws the following error:-
"Parse error: syntax error, unexpected T_THROW in xxx/xxx/includes/_xxxClass.php on line 95
while($stmt->fetch()){
$data[]=$rslt or throw new Exception('Failed to fetch results', 1);
}
What I'm looking for is a way to see if there is an error with fetching the results and then throw an exception inside my while loop.
Thanks in advance!
The test you are performing is flawed. mysqli::fetch() returns false on failure, so your while loop will exit as soon as it sees false. If no results are returned, then the loop will not execute and $data will be empty.
What you need to do instead is test if $data is populated after the while loop and then throw your exception if it is empty:-
while($stmt->fetch()){
$data[]=$rslt;
}
if(empty($data)) throw new Exception('Failed to fetch results', 1);
On reflection, you shouldn't be throwing an exception just because no results are returned, just return the empty array:-
Exceptions are for handling exceptional circumstances and an empty result set is not exceptional.
Considering $mysqli your mysqli handler you can try with:
if ($mysqli->errno) { // an error occurred, throw exception }