PHP, PDO, and Exceptions - php

I'm currently in a bit of a dilemma regarding PDO. I've recently switched to using it from my own custom database class as I want to take advantage of transactions. The problem I'm facing is how to throw exceptions from inside a block of code that is already wrapped with try/catch for PDO. Here is an example...
try {
// PDO code
// Transaction start
// Throw manual exception here if error occurs (transaction rollback too)
// Transaction commit
} catch (PDOException $e) {
// Transaction rollback
// Code to handle the exception
}
Taking the above code example and bearing in mind that the PHP manual says; "You should not throw a PDOException from your own code". How would I handle my own exceptions and the PDO ones? Some kind of nesting?

try {
// PDO code
// Transaction start
// Throw manual exception here if error occurs (transaction rollback too)
throw new MyException("all went tits up");
// Transaction commit
} catch (PDOException $e) {
// Transaction rollback
// Code to handle the exception
} catch (MyException $e) {
// Transaction rollback
// Code to handle the exception
}
The thing is, you're going to have duplicate code which wont smell too nice. I would recommend just catching "Exception" e.g.:
try {
// PDO code
// Transaction start
// Throw manual exception here if error occurs (transaction rollback too)
throw new MyException("all went tits up");
// Transaction commit
} catch (Exception $e) {
// Transaction rollback
// Code to handle the exception
}

try{
//code here
}
catch(PDOException $e){
//handle PDO
throw $e; //to rethrow it upper if need
}
catch(Exception $e){
//handle any other
}

If something is going wrong PDO will generate exception. But if you make some changes in db and would like to revert all you can run
throw new PDOException(....);

Related

Why does the "catch" in try catch declare type of $e

try {
} catch (Exception $e) {
}
I thought PHP had type inference. Why is it neccesary to declare the type of the variable --$e-- ?
The code can throw different classes of exceptions. You can use that to your advantage to add proper code for error handling.
A try block can be followed by any number of catch blocks.
Example:
try
{
}
catch(\PDOException $e)
{
// Something bad happened while dealing with database
}
catch(\LengthException $e)
{
// Length exception occurred
}
catch(\Exception $e)
{
// The \Exception is the parent class for all exceptions, this handles anything not caught in above example
}
Using the above sample, you can take proper measures for handling errors depending on why they occurred. That means you can throw exceptions that you defined. It's the best if you don't overdo it and swap out entire error handling with exceptions. Exceptions occur when something abnormal in the code flow occurs, for example - a connection to MySQL broke mid-transaction.

What is the difference between DB::beginTransaction() and DB::transaction()?

I'm using Laravel 5.2.
I would like to know what are the differences between :
DB::beginTransaction() and DB::transaction()
DB::commitTransction() and DB::commit()
DB::rollbackTransction() and DB::rollback()
Any helps would be appreciated.
DB::beginTransaction() will only begin a transaction, while for DB::transaction() you must pass a Closure function that will be executed inside a transaction.
So this:
DB::transaction(function() {
// Do something and save to the db...
});
is the same as this:
// Open a try/catch block
try {
// Begin a transaction
DB::beginTransaction();
// Do something and save to the db...
// Commit the transaction
DB::commit();
} catch (\Exception $e) {
// An error occured; cancel the transaction...
DB::rollback();
// and throw the error again.
throw $e;
}
As you can see, DB::transaction() is a "helper" function to avoid writing code to catch errors, begin a transaction, commit the transaction, and optionally rollback (cancel the transaction) if an error occured.
If you have a more complex logic, or need an specific behaviour, you will manually build your transaction; if your logic is rather simple, DB::transaction() is the way to go.
As for DB::commitTransaction() and DB::rollbackTransaction(), I can't find information.
It's a good practice to check the source code of the things you use, because you will learn how they are written, as well as how to write. Here's the file with the source for these methods.
From Laravel 6 to use Transactions must be use the following helper functions.
use Illuminate\Support\Facades\DB;
try {
// For Begin a transaction
DB::beginTransaction();
// Do something
// Commit the transaction
DB:: commit();
} catch (\Throwable $e) {
// An error occured
DB::rollback();
// and throw the error again.
throw $e;
}
if you use the DB::commitTransaction(); function, get error undefined function.

PDO Transaction syntax with try catch

What is the syntax preferred while using PDO transaction and try catch and Why?
$dbh->beginTransaction();
try {
} catch (Exception $e) {
}
OR
try {
$dbh->beginTransaction();
} catch (Exception $e) {
}
The existent answers seem to suggest that since $dbh->beginTransaction() could throw a PDOException it should be in the same try block of the actual transaction code, but this means that the rollBack() code itself will be wrong, because it could invoke a rollBack() without there being a transaction, which could also throw another PDOException.
The right logical ordering of this is that you put the code you want executed in one transaction in one catch block after the transaction has been created. You could also check that the return of beginTransaction() is true before proceeding. You could even check that the database session is in a transaction before calling rollback().
if ($dbh->beginTransaction())
{
try
{
//your db code
$dbh->commit();
}
catch (Exception $ex)
{
if ($dbh->inTransaction())
{
$dbh->rollBack();
}
}
}
Keep in mind that you could still, at least in theory, get an exception from beginTransaction() and rollBack() so I would put this in a separate function and enclose the invocation in another try-catch block.
You could also bubble the exception you get up to catch it and log all Exceptions in one place. But remember that some exceptions could be data integrity errors such as duplicate keys or invalid foreign keys, which would not be a database fault as such, but most probably a bug in your code.
With this approach, the main thing to keep in mind here is that the two try-catch blocks have a slightly different purpose. The inner one is purely to ensure that multiple queries are executed and committed atomically in one transaction and if something happens they are rolled back. The external try-catch would be to detect erroneous situations and log it, or whatever you would want to do if you have a problem with your database.
try {
$dbh->beginTransaction();
} catch (Exception $e) {
}
Simply because an exception could be thrown as you attempt to begin the transaction.
Note that you can place another try catch block inside the initial try.
The second one usually makes most sense. Since you may not always know what will cause the transaction to fail, you would want the rollback (and possibly commit) logic available in the catch, so you'd want to put the beginTransaction() inside of the try.
In addition to the try/catch make sure you set the error mode attribute:
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
And if anything you should catch the PDOExecption just because you should not treat all exception in the same way.
try {
$dbh->beginTransaction();
//do stuff
$dbh->commit();
} catch (PDOException $e) {
$dbh->rollBack();
//...
throw $e;
}
But if you still want to catch other exception add more catch blocks:
try {
....
} catch (PDOException $e) {
//handle pdo exception
}catch (Exception $ex) {
//handle others differently
}

MySql PDO beginning a transaction and passing it to objects

Will this work? I'd test it but I don't know how to crash things half way though.
$db = DB::getDB();
try{
$db->begintransaction();
Invoice::saveInvoice($info, $db);
InvoiceDetails::saveDetails($moreInfo, $db);
$db->commit();
}catch(Exception $e){
$db->rollback();
}
And if it does work is there anything that could bite me in the butt besides doing something that causes a implicit commit?
The only thing I'd do is fix up your exception handling. For example
catch (Exception $e) {
$db->rollback();
throw $e;
}
Doing this lets you safely rollback the transaction as well as letting the error bubble up further in your application.
You could even wrap the inner exception (which will probably be a PDOException) with one of your choosing, eg
$db->rollback();
throw new RuntimeException('Error saving invoice details', 0, $e);
To "crash things half way though", simply throw an exception within one of your save* methods, eg
throw new Exception('KA-BLAM!');

Throwing exceptions in a PHP Try Catch block

I have a PHP function in a Drupal 6 .module file. I am attempting to run initial variable validations prior to executing more intensive tasks (such as database queries). In C#, I used to implement IF statements at the beginning of my Try block that threw new exceptions if a validation failed. The thrown exception would be caught in the Catch block. The following is my PHP code:
function _modulename_getData($field, $table) {
try {
if (empty($field)) {
throw new Exception("The field is undefined.");
}
// rest of code here...
}
catch (Exception $e) {
throw $e->getMessage();
}
}
However, when I try to run the code, it's telling me that objects can only be thrown within the Catch block.
function _modulename_getData($field, $table) {
try {
if (empty($field)) {
throw new Exception("The field is undefined.");
}
// rest of code here...
}
catch (Exception $e) {
/*
Here you can either echo the exception message like:
echo $e->getMessage();
Or you can throw the Exception Object $e like:
throw $e;
*/
}
}
To rethrow do
throw $e;
not the message.
Just remove the throw from the catch block — change it to an echo or otherwise handle the error.
It's not telling you that objects can only be thrown in the catch block, it's telling you that only objects can be thrown, and the location of the error is in the catch block — there is a difference.
In the catch block you are trying to throw something you just caught — which in this context makes little sense anyway — and the thing you are trying to throw is a string.
A real-world analogy of what you are doing is catching a ball, then trying to throw just the manufacturer's logo somewhere else. You can only throw a whole object, not a property of the object.
You tried to throw a string:
throw $e->getMessage();
You can only throw objects that implement \Throwable, e.g. \Exception.
As a sidenote: Exceptions are usually to define exceptional states of the application and not for error messages after validation. Its not an exception, when a user gives you invalid data
Throw needs an object instantiated by \Exception. Just the $e catched can play the trick.
throw $e

Categories