Implementing Transactions in mysql via php - php

Below are my queries
$db= new mysqli('localhost','user','passcode','dbname');
try {
// First of all, let's begin a transaction
$db->beginTransaction();
// A set of queries; if one fails, an exception should be thrown
$query1="insert into table1(id,name) values('1','Dan')";
$query2="insert into table2(id,name) values('2','Anaba')";
// If we arrive here, it means that no exception was thrown
// i.e. no query has failed, and we can commit the transaction
$db->commit();
} catch (Exception $e) {
// An exception has been thrown
// We must rollback the transaction
$db->rollback();
}
Please I am trying to implement transaction in mysql queries through php, I would like to fail all queries when one of the queries fail. The above code throws an error("Fatal error: Call to undefined method mysqli::beginTransaction()").Please is there any better solution or idea to solve the problem.Thanks for your help

$db->begin_transaction();
not
$db->beginTransaction();
http://www.php.net/manual/en/mysqli.begin-transaction.php
If earlier than PHP 5.5, then use
$db->autocommit(true);
instead

According to the documentation, begin_transaction() is new in PHP 5.5. (just released a couple weeks ago)
If you're getting an error saying that it doesn't exist, it probably means you're using an older version of PHP.
If you think you are running PHP 5.5, please verify your PHP version by running this earlier in your same script:
echo(phpversion());
Sometimes there's multiple versions of PHP present on a machine, and your script may not actually be executing in the version that you think it is.

You could implement a $count, something like that:
$err_count = 0;
$query1 = "insert into table1(id,name) values('1','Dan')";
if(!$query1)
$err_count++;
if($err_count>0)
throw new Exception("error msg");

Related

How does MySQL transaction works and When to rollback?

i wonder how does transaction work.
Question is do I need to rollback if any error/exception occurs, where the Exception will be thrown and never reach the commit() call? If so why and how? and what rollback does exactly?
consider the followings PHP code (some Java code does similar things like so):
public function deleteAll():void{
$stmt = $this->con->prepare(
'DELETE FROM dd WHERE 1'
);
$this->con->begin_transaction();
if(!$stmt->execute()){
throw new Exception($stmt->error);
}
$this->con->commit();
}
public function insert(array $list):void{
$stmt = $this->con->prepare(
'INSERT INTO dd(cc) VALUES(?)'
);
$stmt->bind_param('s', $v);
$this->con->begin_transaction();
foreach($list as $v){
if(!$stmt->execute()){
throw new Exception($stmt->error);
}
}
$this->con->commit();
}
Note:
$this->con is the mysqli connection object (not using PDO, although different but PDO and mysqli will do similar thing like above).
database is MySQL, table using InnoDB engine.
Some people code shows they catch the Exception and do rollback. But I did not since the commit() is never executed if Exception is thrown. Then this let me wonder why does it need to rollback at all.
My assumption is if there are multiple queries to perform at once like the following:
public function replaceAllWith(array $list):void{
$this->con->begin_transaction();
try{
//do both previous queries at once
$this->deleteAll();
$this->insert($list);
$this->con->commit();
}catch(Exception $e){
//error
$this->con->rollback();
}
}
but then it still cannot rollback because each method/function is done and committed by different method and different transaction.
Second Question: Do I do each transaction in each method call, or only invoke transaction in the 'boss/parent' function which only call and coordinate 'subordinate/helper' functions to do work?
Not sure if I'm asking the right questions.
Database Transaction is confusing, many online resources show only the procedural process (commit only or then immediately rollback which make no differences to normal direct query) without OO concepts involved. Please any pros guide and give some advice. tq.

Catching Exceptions from library using Laravel 5.2

Pretty new to laravel, so I'm not exactly sure how it handles errors and how best to catch them.
I'm using a 3rd party game server connection library that can query game servers in order to pull data such as players, current map etc..
This library is called Steam Condenser : https://github.com/koraktor/steam-condenser
I have imported this using composer in my project and all seems to be working fine, however I'm having trouble with catching exceptions that are thrown by the library.
One example is where the game server you are querying is offline.
Here is my code:
public function show($server_name)
{
try{
SteamSocket::setTimeout(3000);
$server = server::associatedServer($server_name);
$server_info = new SourceServer($server->server_ip);
$server_info->rconAuth($server->server_rcon);
$players = $server_info->getPlayers();
$total_players = count($players);
$more_info = $server_info->getServerInfo();
$maps = $server_info->rconExec('maps *');
preg_match_all("/(?<=fs\)).*?(?=\.bsp)/", $maps, $map_list);
}catch(SocketException $e){
dd("error");
}
return view('server', compact('server', 'server_info', 'total_players', 'players', 'more_info', 'map_list'));
}
If the server is offline, it will throw a SocketException, which I try to catch, however this never seems to happen. I then get the error page with the trace.
This causes a bit of a problem as I wish to simply tell the end user that the server is offline, however I cannot do this if I can't catch this error.
Is there something wrong with my try/catch? Does laravel handle catching errors in this way? Is this an issue with the 3rd party library?
A couple things:
Does the trace lead to the SocketException or to a different error? It's possible that a different error is being caught before the SocketException can be thrown.
Your catch statement is catching SocketException. Are you importing the full namespace at the top of your PHP file? use SteamCondenser\Exceptions\SocketException;
Also for debugging purposes, you could do an exception "catch all" and dump the type of exception:
try {
...
}catch(\Exception $e){
dd(get_class($e));
}
If you still get the stack trace after trying the above code, then an error is being thrown before the try/catch block starts.

die() or try/catch when interacting with MySql database in PHP?

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.

PHP and MySQL ACID Program design

a while ago I completely recoded my application so that MySQL would perform in an ACID way.
At the very top level of all functions I do something like this:
try{
$db->begin();
dosomething($_SESSION['userid']);
$db->commit();
}catch(advException $e){
$eCode = $e->getCode();
$eMessage = $e->getMessage();
# Success
if ($eCode == 0){
$db->commit();
}else{
$db->rollback();
}
}
Within the function 'dosomething' I have the Exceptions thrown to users like:
throw new Exception('There was a problem.',1);
or
throw new Exception('You have successfully done that!', 0);
So that I can control the flow of the program. If theres a problem then roll back everything that happened and if everything was good then commit it. It's all worked quite great but there's just one flaw that I've come across so far.
I added Exception logging so I can see when there are issues that users face. But the problem is, if the table that logs the errors is InnoDB then it's also included in the transaction and will rollback if theres a problem so no errors are stored.
To get around this I basically just made the Error logging table MyISAM so when a rollback is done, the changes are still there.
Now I'm thinking of other bits I'd like to keep out of the transaction, like sending a mail within my application to the admin to help alert of problems.
Is there any sort of way for me to not include a database insert within the parent transaction? Have I taken a bad route in terms of Application/DB design and is there any other way I could have handled this?
Thanks, Dominic
It is not good idea to throw an Exception on success.
You have to do DB insert after previous rollback was called.
catch (Exception $e) {
$db->rollback();
Log::insert('Error: ' . $e->getMessage());
}
Try to use Logger to control your program. It is more flexible way.
Use return codes for successful operation, and Exceptions to specify exceptional situations (such as severe errors, etc.). As for your specific issue, I'd recommend having a separate database for logging if you choose to use the rollback strategy.

how to handle exceptions/errors in php?

when using 3rd part libraries they tend to throw exceptions to the browser and hence kill the script.
eg. if im using doctrine and insert a duplicate record to the database it will throw an exception.
i wonder, what is best practice for handling these exceptions.
should i always do a try...catch?
but doesn't that mean that i will have try...catch all over the script and for every single function/class i use? Or is it just for debugging?
i don't quite get the picture.
Cause if a record already exists in a database, i want to tell the user "Record already exists".
And if i code a library or a function, should i always use "throw new Expcetion($message, $code)" when i want to create an error?
Please shed a light on how one should create/handle exceptions/errors.
Thanks
The only way to catch these exceptions is to use a try catch block. Or if you don't want the exception to occur in the first place you need to do your due diligence and check if the record already exists before you try to insert the record.
If it feels like you're using this all over the place then maybe you need to create a method that takes care of this for you (Dont Repeat Yourself).
I don't know Doctrine, but regarding your concrete usage, maybe there is a way to determine if you are facing a duplicate entry, something like :
try {
/* Doctrine code here */
} catch (DuplicateEntryException $e) {
/* The record already exists */
} catch (Exception $e) {
/* Unexpected error handling */
}
Or maybe you have to check if the Exception code equals 1062, which is the MySQL error code for duplicate entries.
Any code that may throw an exception should be in a try/catch block. It is difficult in PHP because you cannot know which method throws an Exception.
You should also have a big try/catch block in your main PHP file that avoids displaying the stack trace to the user, and that logs the cause. Maybe you can use set_exception_handler for this.

Categories