Global php PDO handling errors - php

I use try/catch block in my classes methods, If a get an exception, I log the error. But I would like to tell the "User" that a database query/etc failed - and the problem should be fixed soon.
I could use a die() on the Exception in my methods, but that wouldn't be DRY, as I would have to retype it a lot, so any suggestions on how I can do this.
Example method:
public function login($username, $password) {
try {
$this->STH = $this->DBH->prepare("SELECT id, baned, activated FROM users WHERE username = ? AND password = ?");
$this->STH->setFetchMode(PDO::FETCH_OBJ);
$this->STH->execute(array($username, $password));
if (($row = $this->STH->fetch()) !== false)
return $row;
} catch (PDOException $e) {
//Log $e->getMessage();
die('A database error occoured, we are working on the problem, and it should work in a few...');
}
}

If you need a quick fix, you can set a global exception handler, like this:
function pdo_exception_handler($exception) {
if ($exception instanceof PDOException) {
// do something specific for PDO exceptions
} else {
// since the normal exception handler won't be called anymore, you
// should handle normal exceptions yourself too
}
}
set_exception_handler('pdo_exception_handler');

It's OK to repeat yourself in this case because as each instance of die() passes a unique message.

Related

When it comes to handle errors, in what sense, using Exceptions are better that utilize if...else...switch... etc.?

I perfectly understand the nuances of the (try/throw/catch) block.
What I don't understand is:
If we gonna use an IF (or any control structure) inside our try block anyway in order to test if a condition is met, only then, 'throw' an exception if the results of that test is false, then... in my opinion: throw/generate an exception is useles; because if a condition is not met, we can simply print an error message, call a function, instantiate a class, redirect to another location, etc.
Another story would be, if for instance, a variable was not initialized, we enclose that variable inside a try{} block, echo the variable, and from that point onward, everything will be handle by the catch() block because the try block raises an error; and since the try/catch blocks talk each other, the catch block will catch every error that was originated from his corresponding try block. However, you can set a custom error message inside yout try block (optional).
What I've read so far:
every results from searching: if vs. try
I do see the difference.
But I can not understand why some people choose try/throw/catch over if...else...switch...while... etc.
As far I can see, try/throw/catch can be used for debugging, though.
One benefit of exceptions over if/then is that you can wrap try/catch around a large block of code. It will be triggered if an error happens anywhere in the block.
try {
$db = db_open();
$statement = $db->prepare($sql);
$result = $statement->execute($params);
} catch (Exception $e) {
die($e->getMessage());
}
With if/then, you would have to perform a test at each step.
$db = db_open();
if (!$db) {
die(db_connect_error());
}
$statement = $db->prepare($sql);
if (!$statement) {
die(db_error($db));
}
$result = $statement->execute($params);
if (!$result) {
die(db_error($db));
}
As you said, it's a lot of overhead to throw an exception inside a try/catch block and catch it immediately.
try {
if (...) {
// good, do no throw
} else {
throw new Exception();
}
} catch ($e) {
// handle exception
}
This should be replaced by:
if (...) {
// good
} else {
// handle error, no exception
}
Exceptions are useful because they bubble up. So imagine if you have this code instead:
function bla() {
try {
tryToDoSomething();
} catch ($e) {
// handle error
}
}
function tryToDoSomething() {
if ($somethingNotAvailable) {
throw new Exception();
}
doSomething();
}
In this case, the function that defines the try/catch is NOT the one throwing the exception. tryToDoSomething() does not know how to handle the errors so it will let parent methods to take care of it. The exception can bubble up the call stack until someone catches it and handles the error. That's how exceptions can actually be useful :)

PHP try, catch, and try again (depending on output)?

I currently have the following PHP which uses a try/catch block checking for exceptions.
try {
$obj->login('User', 'Pass');
$obj->joinServer('Server');
} catch(ConnectionException $objException){
die();
}
...and this is the ConnectionException class:
class ConnectionException extends Exception {
public function __construct($strError, $intError, $mixOther = null){
$this->strError = $strError;
$this->intError = $intError;
echo $this->intError, ' - ', $this->strError, chr(10);
}
}
Now let's say if the "catch" part returns a specific error (if ConnectionException outputs a specific message), how can I "retry" the try/catch again?
I guess you can nest the try/catch, wrap it the code into a function to avoid code repetition:
function login($obj){
$obj->login('User', 'Pass');
$obj->joinServer('Server');
}
try {
login($obj);
} catch(ConnectionException $objException){
try {
login($obj);
} catch(ConnectionException $objException){
die();
}
}
If you want to do this at this level, instead of having it throw a ConnectionException every time, throw a different exception that's a subclass of ConnectionException that signifies when you would want to rerun the logic. Then, encapsulate the logic that you have within the try to a function, and call it again.
try {
doWork( $obj);
} catch( ConnectionTimeoutException $e) {
// Wait a bit, then retry
try {
sleep(10);
doWork( $obj);
} catch( ConnectionException $e) {
// Because of the subclass, this would catch both ConnectionException and ConnectionTimeoutException
die();
}
} catch( ConnectionException $e) {
die();
}
You can also change how you're calling this function, and call it twice depending on the result of ConnectionException. Although, in that manner, it may make more sense to base this decision on a return value instead of an Exception.
My suggestion would be to create a simple while loop that checks for a condition:
try_connection = true;
while(try_connection) {
try {
$obj->login('User', 'Pass');
$obj->joinServer('Server');
} catch(ConnectionException $objException){
if(!specific_message)
try_connection = false;
}
}

Exceptions in database model, how do I handle it?

I am creating a model for a small PHP application. This will utilize PDO to communicate with a MySQL-server. I have understood that the recommended error mode is the one which throws exceptions, as this allows for graceful error handling. But I don't understand how I should handle these exceptions?
Technically, it is easy, but let me give you an example:
class Model()
{
private $host = "localhost",
$user = "",
$pass = "",
$DBH;
function __construct()
{
try
{
$DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
}
catch(PDOException $e)
{
error_log($e->getMessage());
}
}
}
If I create an object Model in my controller, and it fails, I have no way of handling this in my controller, right? Or what happens when I create that object, "new Model" returns false?
Excuse me for being a newbie, but I want to be able to handle any exceptions also from other functions in the model. How should I go about this? I need to be able to know if something went wrong in my controller and be able to do the appropriate thing there.
If you want your controller to catch the exception as well, you can always rethrow it after logging.
class Model()
{
...
function __construct()
{
try
{
...
}
catch(PDOException $e)
{
error_log($e->getMessage());
throw $e;
}
}
}
Depends entirely on what the error_log function does.
You can return the exception.
You can simply die after logging the error (presumably if your application can't hit the DB then theres no graceful recovery).
You can return a custom exception via throw();
It's really just your preference.

PHP Exceptions in Classes

I'm writing a web application (PHP) for my friend and have decided to use my limited OOP training from Java.
My question is what is the best way to note in my class/application that specific critical things failed without actually breaking my page.
My problem is I have an Object "SummerCamper" which takes a camper_id as it's argument to load all of the necessary data into the object from the database. Say someone specifies a camper_id in the query string that does not exist, I pass it to my objects constructor and the load fails. I don't currently see a way for me to just return false from the constructor.
I have read I could possibly do this with Exceptions, throwing an exception if no records are found in the database or if some sort of validation fails on input of the camper_id from the application etc.
However, I have not really found a great way to alert my program that the Object Load has failed. I tried returning false from within the CATCH but the Object still persists in my php page. I do understand I could put a variable $is_valid = false if the load fails and then check the Object using a get method but I think there may be better ways.
What is the best way of achieving the essential termination of an object if a load fails? Should I load data into the object from outside the constructor? Is there some sort of design pattern that I should look into?
Any help would be appreciated.
function __construct($camper_id){
try{
$query = "SELECT * FROM campers WHERE camper_id = $camper_id";
$getResults = mysql_query($query);
$records = mysql_num_rows($getResults);
if ($records != 1) {
throw new Exception('Camper ID not Found.');
}
while($row = mysql_fetch_array($getResults))
{
$this->camper_id = $row['camper_id'];
$this->first_name = $row['first_name'];
$this->last_name = $row['last_name'];
$this->grade = $row['grade'];
$this->camper_age = $row['camper_age'];
$this->camper_gender = $row['gender'];
$this->return_camper = $row['return_camper'];
}
}
catch(Exception $e){
return false;
}
}
A constructor in PHP will always return void. This
public function __construct()
{
return FALSE;
}
will not work. Throwing an Exception in the constructor
public function __construct($camperId)
{
if($camperId === 1) {
throw new Exception('ID 1 is not in database');
}
}
would terminate script execution unless you catch it somewhere
try {
$camper = new SummerCamper(1);
} catch(Exception $e) {
$camper = FALSE;
}
You could move the above code into a static method of SummerCamper to create instances of it instead of using the new keyword (which is common in Java I heard)
class SummerCamper
{
protected function __construct($camperId)
{
if($camperId === 1) {
throw new Exception('ID 1 is not in database');
}
}
public static function create($camperId)
{
$camper = FALSE;
try {
$camper = new self($camperId);
} catch(Exception $e) {
// uncomment if you want PHP to raise a Notice about it
// trigger_error($e->getMessage(), E_USER_NOTICE);
}
return $camper;
}
}
This way you could do
$camper = SummerCamper::create(1);
and get FALSE in $camper when the $camper_id does not exist. Since statics are considered harmful, you might want to use a Factory instead.
Another option would be to decouple the database access from the SummerCamper altogether. Basically, SummerCamper is an Entity that should only be concerned about SummerCamper things. If you give it knowledge how to persist itself, you are effectively creating an ActiveRecord or RowDataGateway. You could go with a DataMapper approach:
class SummerCamperMapper
{
public function findById($id)
{
$camper = FALSE;
$data = $this->dbAdapter->query('SELECT id, name FROM campers where ?', $id);
if($data) {
$camper = new SummerCamper($data);
}
return $camper;
}
}
and your Entity
class SummerCamper
{
protected $id;
public function __construct(array $data)
{
$this->id = data['id'];
// other assignments
}
}
DataMapper is somewhat more complicated but it gives you decoupled code which is more maintainable and flexible in the end. Have a look around SO, there is a number of questions on these topics.
To add to the others' answers, keep in mind that you can throw different types of exceptions from a single method and handle them each differently:
try {
$camper = new SummerCamper($camper_id);
} catch (NoRecordsException $e) {
// handle no records
} catch (InvalidDataException $e) {
// handle invalid data
}
Throwing an exception from the constructor is probably the right approach. You can catch this in an appropriate place, and take the necessary action (e.g. display an error page). Since you didn't show any code, it's not clear where you were catching your exception or why that didn't seem to work.
try {
$camper = new SummerCamper($id);
$camper->display();
} catch (NonexistentCamper $ex) {
handleFailure($ex);
}

PHP Try and Catch for SQL Insert

I have a page on my website (high traffic) that does an insert on every page load.
I am curious of the fastest and safest way to (catch an error) and continue if the system is not able to do the insert into MySQL. Should I use try/catch or die or something else. I want to make sure the insert happens but if for some reason it can't I want the page to continue to load anyway.
...
$db = mysql_select_db('mobile', $conn);
mysql_query("INSERT INTO redirects SET ua_string = '$ua_string'") or die('Error #10');
mysql_close($conn);
...
Checking the documentation shows that its returns false on an error. So use the return status rather than or die(). It will return false if it fails, which you can log (or whatever you want to do) and then continue.
$rv = mysql_query("INSERT INTO redirects SET ua_string = '$ua_string'");
if ( $rv === false ){
//handle the error here
}
//page continues loading
This can do the trick,
function createLog($data){
$file = "Your path/incompletejobs.txt";
$fh = fopen($file, 'a') or die("can't open file");
fwrite($fh,$data);
fclose($fh);
}
$qry="INSERT INTO redirects SET ua_string = '$ua_string'"
$result=mysql_query($qry);
if(!$result){
createLog(mysql_error());
}
You can implement throwing exceptions on mysql query fail on your own. What you need is to write a wrapper for mysql_query function, e.g.:
// user defined. corresponding MySQL errno for duplicate key entry
const MYSQL_DUPLICATE_KEY_ENTRY = 1022;
// user defined MySQL exceptions
class MySQLException extends Exception {}
class MySQLDuplicateKeyException extends MySQLException {}
function my_mysql_query($query, $conn=false) {
$res = mysql_query($query, $conn);
if (!$res) {
$errno = mysql_errno($conn);
$error = mysql_error($conn);
switch ($errno) {
case MYSQL_DUPLICATE_KEY_ENTRY:
throw new MySQLDuplicateKeyException($error, $errno);
break;
default:
throw MySQLException($error, $errno);
break;
}
}
// ...
// doing something
// ...
if ($something_is_wrong) {
throw new Exception("Logic exception while performing query result processing");
}
}
try {
mysql_query("INSERT INTO redirects SET ua_string = '$ua_string'")
}
catch (MySQLDuplicateKeyException $e) {
// duplicate entry exception
$e->getMessage();
}
catch (MySQLException $e) {
// other mysql exception (not duplicate key entry)
$e->getMessage();
}
catch (Exception $e) {
// not a MySQL exception
$e->getMessage();
}
if you want to log the error etc you should use try/catch, if you dont; just put # before mysql_query
edit :
you can use try catch like this; so you can log the error and let the page continue to load
function throw_ex($er){
throw new Exception($er);
}
try {
mysql_connect(localhost,'user','pass');
mysql_select_db('test');
$q = mysql_query('select * from asdasda') or throw_ex(mysql_error());
}
catch(exception $e) {
echo "ex: ".$e;
}
Elaborating on yasaluyari's answer I would stick with something like this:
We can just modify our mysql_query as follows:
function mysql_catchquery($query,$emsg='Error submitting the query'){
if ($result=mysql_query($query)) return $result;
else throw new Exception($emsg);
}
Now we can simply use it like this, some good example:
try {
mysql_catchquery('CREATE TEMPORARY TABLE a (ID int(6))');
mysql_catchquery('insert into a values(666),(418),(93)');
mysql_catchquery('insert into b(ID, name) select a.ID, c.name from a join c on a.ID=c.ID');
$result=mysql_catchquery('select * from d where ID=7777777');
while ($tmp=mysql_fetch_assoc($result)) { ... }
} catch (Exception $e) {
echo $e->getMessage();
}
Note how beautiful it is. Whenever any of the qq fails we gtfo with our errors. And you can also note that we don't need now to store the state of the writing queries into a $result variable for verification, because our function now handles it by itself. And the same way it handles the selects, it just assigns the result to a variable as does the normal function, yet handles the errors within itself.
Also note, we don't need to show the actual errors since they bear huge security risk, especially so with this outdated extension. That is why our default will be just fine most of the time. Yet, if we do want to notify the user for some particular query error, we can always pass the second parameter to display our custom error message.
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
I am not sure if there is a mysql version of this but adding this line of code allows throwing mysqli_sql_exception.
I know, passed a lot of time and the question is already checked answered but I got a different answer and it may be helpful.
$sql = "INSERT INTO customer(FIELDS)VALUES(VALUES)";
mysql_query($sql);
if (mysql_errno())
{
echo "<script>alert('License already registered');location.replace('customerform.html');</script>";
}
To catch specific error in Mysqli
$conn = ...;
$q = "INSERT INTO redirects (ua_string) VALUES ('$ua_string')";
if (mysqli_query($conn, $q)) {
// Successful
}
else {
die('Mysqli Error: '.$conn->error); // Show Error Complete Description
}
mysqli_close($conn);
Use any method described in the previous post to somehow catch the mysql error.
Most common is:
$res = mysql_query('bla');
if ($res===false) {
//error
die();
}
//normal page
This would also work:
function error() {
//error
die()
}
$res = mysql_query('bla') or error();
//normal page
try { ... } catch {Exception $e) { .... } will not work!
Note: Not directly related to you question but I think it would much more better if you display something usefull to the user. I would never revisit a website that just displays a blank screen or any mysterious error message.
$new_user = new User($user);
$mapper = $this->spot->mapper("App\User");
try{
$id = $mapper->save($new_user);
}catch(Exception $exception){
$data["error"] = true;
$data["message"] = "Error while insertion. Erron in the query";
$data["data"] = $exception->getMessage();
return $response->withStatus(409)
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
}
if error occurs, you will get something like this->
{
"error": true,
"message": "Error while insertion. Erron in the query",
"data": "An exception occurred while executing 'INSERT INTO \"user\" (...) VALUES (...)' with params [...]:\n\nSQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for integer: \"default\"" }
with status code:409.

Categories