I am currently developing a website which uses MySQLi for the database access. Currently the code that performs a database query looks something like this:
$query = "SELECT height, color FROM llamas WHERE name = ?";
if(!$stmt = $connection->prepare($query)) {
// Error handling here.
}
$stmt->bind_param("s", $name);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($height, $color);
$stmt->fetch();
$stmt->close();
The return values for most of these calls return FALSE to indicate a problem. From the PHP docs:
Returns TRUE on success or FALSE on failure.
If I attempt to check all of these function calls for false, I get something like this:
$query = "SELECT height, color FROM llamas WHERE name = ?";
if(!$stmt = $connection->prepare($query)) {
// Error handling here.
}
if(!$stmt->bind_param("s", $name)) {
// Error handling here.
}
if(!$stmt->execute()) {
// Error handling here.
}
if(!$stmt->store_result()) {
// Error handling here.
}
if(!$stmt->bind_result($height, $color)) {
// Error handling here.
}
if(!$stmt->fetch()) {
// Error handling here.
}
if(!$stmt->close()) {
// Error handling here.
}
My question is: Which of these function calls do I actually need to check for a return value of FALSE? And is there a tidy way of doing this?
I would use PDO and try - catch
try {
// your code here
} catch(PDOException $e){
error_log('ERROR: ' . $e->getMessage());
}
Edit: Here is the MySQLi version
try {
} catch (mysqli_sql_exception $e) {
}
Related
I am using php in a project without a framework. I am using PDO. Now, I am declaring lots of sql queries to be used all over the application, like this.
function f($parameters) {
try {
$query = "";
$stmt = $this->con->prepare($query);
$stmt->execute(); // might have parameters
// return true if not a select statement
return $stmt->fetchAll(); // only fetch if looking for only one row
} catch(exception $e) {
$this->errors[] = "SQL failed: " . $e->getMessage();
return false;
}
}
}
I am doing this lots of times, as a result the code is extremely long. So I decided to rapped the try-catch code into a function that takes a closure as an argument.
protected function exec_query($statement) {
try {
return $statement();
} catch (Exception $e) {
$this->errors[] = "SQL failed: " . $e->getMessage();
return false;
}
}
I am using it like this:
return parent::exec_query(function() use() {
// any sql statement
});
this one is for transactions.
protected function exec_transaction($statements) {
try {
$this->con->beginTransaction();
$statements();
// if we are still inside a transaction. some $statement might rollback the running transaction, so just return false in such a case.
if ($this->con->inTransaction()) {
$this->con->commit();
} else {
return false;
}
} catch (Exception $e) {
$this->con->rollback();
$this->errors[] = "SQL failed: " . $e->getMessage();
return false;
}
return true;
}
I'll use it same as with exec_query, but with more statements to be executed inside a transaction.
Regarding the inTransaction() check, consider this example: I have to execute three insert statements. Each of those insert statements belongs into separate functions. This is because they might be reused outside transactions. The thing is inside each function is all the validation required before the insert statements get executed (E.G. if user exist, throw an error). For example,
function add() {
if (exist) {
// if there is a running transaction, rollback it.
if ($this->con->inTransaction()) {
$this->con->rollback();
return false;
}
} else {
// insert
}
}
All of the three functions are like this, because they might be executed separately from each other.
Is what I am doing good practice? Is there some cases they might lead me in trubble in the future by doing this in all of my sql queries?
The following code updates the table correctly, but it also returns an Exception. Any idea what might be happening here?
public function updateThis($aaa){
try
{
$success = false;
$query = "
UPDATE this_table
SET thing = '0'
WHERE aaa = :aaa";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':aaa', $aaa);
$stmt->execute();
if($this->conn->commit())
$success = true;
return $success;
}
catch(Exception $e)
{
return $e;
}
}
When you are using PDO, Auto-Commit is on by default, unless you specifically turn it off using Begin Transaction. I can't see it in your connection, so are you perhaps trying to commit a transaction that has already been auto-commited?
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.
I am trying to capture database (MYSQL) errors in my PHP web application. Currently, I see that there are functions like mysqli_error(), mysqli_errno() for capturing the last occurred error. However, this still requires me to check for error occurrence using repeated if/else statements in my php code. You may check my code below to see what I mean. Is there a better approach to doing this? (or) Should I write my own code to raise exceptions and catch them in one single place? Also, does PDO raise exceptions? Thanks.
function db_userexists($name, $pwd, &$dbErr)
{
$bUserExists = false;
$uid = 0;
$dbErr = '';
$db = new mysqli(SERVER, USER, PASSWORD, DB);
if (!mysqli_connect_errno())
{
$query = "select uid from user where uname = ? and pwd = ?";
$stmt = $db->prepare($query);
if ($stmt)
{
if ($stmt->bind_param("ss", $name, $pwd))
{
if ($stmt->bind_result($uid))
{
if ($stmt->execute())
{
if ($stmt->fetch())
{
if ($uid)
$bUserExists = true;
}
}
}
}
if (!$bUserExists)
$dbErr = $db->error();
$stmt->close();
}
if (!$bUserExists)
$dbErr = $db->error();
$db->close();
}
else
{
$dbErr = mysqli_connect_error();
}
return $bUserExists;
}
I have created my own code to execute MySQL statements and raise exceptions if they fail, including specific exceptions for different causes of failure. One of the most helpful of these is when collisions occur, allowing me to use try..catch blocks and catch DatabaseCollisionExceptions, and handle those differently from other database exceptions.
What I found easiest for doing this was an MVC design pattern where every table was represented by a PHP class (models) which I could just assign member variables to and call a save method to save to the database, similar to:
try
{
$user = new User();
$user->username = 'bob';
$user->setPassword($_POST['password'); // Could do some SHA1 functions or whatever
$user->save
}
catch(DatabaseCollisionException $ex)
{
displayMessage("Sorry, that username is in use");
}
catch(DatabaseException $ex)
{
displayMessage("Sorry, a database error occured: ".$ex->message());
}
catch(Exception $ex)
{
displayMessage("Sorry, an error occured: ".$ex->message());
}
For more information on similar design patterns, see:
http://www.phpactiverecord.org/
http://book.cakephp.org/view/17/Model-Extensions-Behaviors
http://www.derivante.com/2009/05/14/php-activerecord-with-php-53/
Of course this isn't the only answer, it's just some ideas you might find helpful.
I think exceptions are the best approach. PDO does throw exceptions you just need to set PDO::ERRORMODE_EXCEPTION when you create the object.
this still requires me to check for
error occurrence using repeated
if/else statements
How's that?
You don't need to check for every possible error.
I'd make just final check for the returned value and nothing else.
For what purpose do you use these nested conditions?
You may rewrite it in the following manner:
$bUserExists = false;
$uid = false;
if (!mysqli_connect_errno())
{
if($stmt = $db->prepare("select uid from user where uname = ? and pwd = ?")
->bind_param("ss", $name, $pwd))
{
$stmt->execute();
$stmt->bind_result($uid);
$stmt->fetch();
$stmt->close();
}
if ($uid)
$bUserExists = true;
$db->close();
}
else
{
$dbErr = mysqli_connect_error();
}
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.