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( ) ; }
Related
I have seen this question asked before on SO, and none of the answers seem to be complete. So please...
I have code using PDO and PDOStatement. It was originally written to work without exceptions, and I'm converting it to work with.
My simple questions are:
Are there any circumstances in which it is useful or necessary to continue to check the return value of functions for FALSE ( when this means "failure" ), or can I simply execute the method and assume that all kinds of failure will trigger an exception? I saw an example in an answer where someone was recommended to use BOTH try-catch AND to test the return value for FALSE - which, it is IS actually necessary, makes for some very ugly code.
Is there a proper list of which methods may, and which can never, throw an exception? I have seen answers which say "if you find we haven't documented an exception we throw, raise a bug", but that's not altogether helpful. I have seen statements that "the manual page says when an exception can be thrown", but the PDO::query and PDOStatement::execute pages make no mention of exceptions - which surely can't be true ... can it? Essentially I'd like to know whether prepare, bind[things], fetch[all], execute and a few others may or will never, throw stuff.
I don't feel I'm asking the earth, and if I have to I could look at the code, but surely the manual documentation on this should be rock solid.
[edit to add an example]
I now find myself with code blocks like this - I would like to simplify it by removing the test of the return value, but I cannot convince myself that it is correct. Other blocks use execute() and so on.
try {
/* I do not know whether beginTransaction throws an exception when it would otherwise return FALSE.
* If it does then checking the return value is spurious, and the code cam be simplified.
*/
if (FALSE == $customerDb->beginTransaction()) {
Log::add("Database begin transaction failed.",
Log::ERROR);
throw new Exception("Failed to begin a transaction.");
}
} catch (PDOException $e) {
/* The documentation does not mention the possibility of
* beginTransaction() throwing any exception,
* even when we have configured all the other functions to do so.
*/
Log::add("Database begin transaction threw an exception: " . $e->getMessage(),
Log::ERROR);
throw new Exception("Begin transaction threw an exception.");
}
There is three error handling strategies, ERRMODE_SILENT (the default one), ERRMODE_WARNING and ERRMODE_EXCEPTION. Only the last one make PDO throw exceptions if errors occurs, so except if you tell PDO explicitly to run in exception mode with:
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
It will not throw any exception except for PDO:__construct() which will throw an exception if the connection fails, whatever the value of PDO::ATTR_ERRMODE.
From the doc:
PDO::__construct() will always throw a PDOException if the connection
fails regardless of which PDO::ATTR_ERRMODE is currently set. Uncaught
Exceptions are fatal.
I suggest you to read the documentation - it's pretty clear how PDO handle errors.
Update
In your code sample, the exception will never be catched as you typed your catch block exception to PDO_Exception and you're throwing an Exception - which is the lower exception type level. If you want to catch all exceptions type, cast your catch parameter to Exception - currently your catch block capture only PDOException.
That being said, let's focus on the confusion you have about exceptions. There is no mysterious exceptions being thrown when in ERRMODE_SILENT or ERRMODE_WARNING, except the one from the PDO controller - which is documented. People telling otherwise are probably working in a PDO environment they don't really know / control - like a framework who use PDO but throw its own exceptions for queries, or changing the ATTR_ERRMODE in specific cases. I suggest you to focus less on the discussion under docs (as sometimes there could be interesting stuffs in it, most of the time it's comments from confused people) and focus more on the docs itself.
Let's be clear about what happen with errors in PDO.
An error occurs. If it happens in the PDO controller, an exception will be thrown and all next steps will be ignored.
The error will be standardized according to the SQL-92 standard (see this link for all the return codes that exists). From the doc :
PDO standardizes on using SQL-92 SQLSTATE error code strings; individual PDO drivers are responsible for mapping their native codes to the appropriate SQLSTATE codes.
PDO check for the ATTR_ERMODE attribute to know how it should handle this error.
ERRMODE_SILENT (the default mode)
The error will not be reported, so you'll need to check for the return of the PDO functions for false and check the PDO::errorCode(), PDO::errorInfo() or PDOStatement::errorCode(), PDOStatement::errorInfo() depending of the object you're using to get the error details.
ERRMODE_WARNING
Same as ERRMODE_SILENT but an E_WARNING PHP error will be thrown. Could be usefull for developpment, but you still need to check the function returns for false.
ERRMODE_EXCEPTION
A PDO_Exception will be thrown. There will be no need to check the function return for false as when an exception is thrown, the next lines will be skipped and PHP will go to the catch block, or if there is not any catch block it will throw a PHP error. So implementation you suggest is redundant when running in ERRMODE_EXCEPTION.
So yes, the doc doesn't said that x function throw an exception because it will be totally redundant. The error handling page explain everything you need to know.
If you're still not convinced, I suggest to try it by yourself: play with ATTR_ERRMODE and some various PDO functions, and see by yourself.
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.
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
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.
When establishing a new PDO db handler, I've got to wrap everything into a try-catch to prevent an error message that would print all db access data to the user.
But how about all the other methods like exec(), for example? Must I wrap all of these into a try-catch block? At which point is the PHP documentation telling that an method throws an exception?
First of all, you can set how errors are dealt with by PDO, using the PDO::setAttribute method, to set the PDO::ATTR_ERRMODE (error reporting) option.
In particular, it is possible to configure PDO so it throws exceptions when there's an error, instead of reporting an "error" -- that's what I generally do.
Then, when a method can throw an exception, it should be indicated in it's documentation -- generaly, it's in the "Return value" section.
For instance, PDO::prepare can throw an exception -- depending on error reporting (see what I wrote just before) :
If the database server cannot
successfully prepare the statement,
PDO::prepare() returns FALSE or
emits PDOException (depending on
error handling).
As a sidenote : if you find a function / method that throws an exception, and it's not indicated in its documentation, it might be a good idea to create a bug report (see http://bugs.php.net/ ), so that problem is corrected ;-)
(Errors / mistakes / missing informations in the documentation are treated via the bug-tracker, like any other bug)
You can see if the method throws exceptions by looking at the manual.
If you look at the manual for __construct you'll see at the bottom under Errors/Exceptions that it throws an exception.