How to prevent MySQLi to crash PHP script if database is offline - php

I have the following scenario:
I have a script that conencts to a remote database, and all works good unless the remote database is offline or the server is offline.
If database/server goes offline, the script uses long time to execute, what would be the best way to check if db connection was successfull before executing the SQL?
class remote_db{
public $db;
public function __construct(){
$this->db = new mysqli("127.0.0.1","usr","pw","database");
$this->db->set_charset("utf8");
}
}
$remote_db = new remote_db();
if($remote_db){ echo 'hello world';}

I suppose I finally managed to get what you mean under "crashing".
It seems your code structure is spaghetti, where HTML is intermixed with database stuff, and so on error it shows an incomplete, torn out design.
To prevent this, you have to structure your PHP application properly.
First of all, never output a single byte if not all database operations are not finished. To do so, split your PHP page into two parts: one is for the database interaction, that will collect all the data required to display; and another part, consists of HTML mostly, that is used to display the data.
After doing that, you will be able to show a dedicated error page, if error occurs during database stage.
Keep in mind that catching every single possible error is mission either highly inefficient and at the same time impossible: you simply cannot foresee and handle every error that may happen.
Instead, just make a simple code that will show a generalized error page in case of any error. To do so, setup an error handler like this (the code is taken from my article The (im)proper use of try..catch):
set_error_handler("myErrorHandler");
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
error_log("$errstr in $errfile:$errline");
header('HTTP/1.1 500 Internal Server Error', TRUE, 500);
echo "Server error. Please try again later");
exit;
}

There's no reason to check if it's online or not before running a query, since between the time you check, and the time you run the query, it might have gone offline. So simply run the query, and check for error afterwards, as you would normally do:
$r = $db->query('...');
if ($r === false) throw new Exception('error running query');
Also check for errors when you create the connection:
$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'my_db');
if ($mysqli->connect_error) {
die('Connect Error (' . $mysqli->connect_errno . ') '
. $mysqli->connect_error);
}
You can also change the timeout property to reduce the waiting time:
$db->options(MYSQLI_OPT_CONNECT_TIMEOUT, 5); // Wait for 5 seconds max

Related

Mysqli connection warning showing if connection details are bad

I have some code that allows a user to input details for their database on their server. After they submit the details, the code checks a connection to the database to see if valid details were entered. I want it to give outcomes of variables being true if the connection either works or does, like this:
$mysqli = new mysqli($_POST['dbHost'],$_POST['dbUser'],$_POST['dbPassword'],$_POST['dbName']);
if ($mysqli->connect_errno) { $badDetails = true; }
else { goodDetails = true; }
Problem is, if the details are indeed incorrect, it shows a PHP warning from the first line of the code above giving 'Unknown MySQL server host'.
What is the way around this? I don't want PHP throwing it's own visible error for this, I want to deal with the error myself.
You should not worry about visual errors. In a production environment, these should be turned off in the ini, and all errors should go to a log on the server instead of just to the screen.
This is configured with the display_errors setting and error_reporting()
Many frameworks override the PHP error handler with a custom implementation to display error in a pretty way, depending on their severity.
To achieve this, you can override the PHP error handler
As seen in the manual one can register custom handlers for regular errors and exceptions. And it is also possible to trigger an user defined error.
Just use a die()
$mysqli = new mysqli($_POST['dbHost'],$_POST['dbUser'],$_POST['dbPassword'],$_POST['dbName']) or die("Database Connection Failed");
A quick, dirty method would be error supression:
$con = #mysqli_connect( /* info */ );
Note that you should not keep this there, as this will suppress all errors, and mysqli can have multiple errors that you might need to know about.
Though I would check the host variable first, to see why the error is caused. You can also use die.
$con = mysqli_connect(/* info */) or die ("SQL Error!");
As far as where to look, try seeing that the host var is actually set and check it's value:
if (!isset($_POST['dbHost'])) {
echo "Host var not set!";
} else {
echo "Host var set: " . $_POST['dbHost']
}

Try & catch pdo close database connection

try{
//PDO CONNECT DB, $db
}catch(PDOException $e){die("ERROR"));}
I have a query user PDO connect to database.
I use try & catch, my question is if my query is error
Do i need to close conncetion before die();?
}catch(PDOException $e){$db="NULL"; die("ERROR"));}
As a matter of fact, you shouldn't die() at all
Until you learn how to use try and catch properly, you shouldn't use this statement. It is not intended for echoing "ERROR". It has totally different purpose.
If you want to echo silly "ERROR" in case of an erroneous query, you have to do it properly.
Namely,
send appropriape HTTP header
log the error to notify a developer of the problem
show whatever error message to the client
have all this stuff done in one place, not repeated for every query
to do this, you have to set up an exception handler:
set_exception_handler('myExceptionHandler');
function myExceptionHandler($e)
{
header('HTTP/1.1 500 Internal Server Error', TRUE, 500);
error_log($e->getMessage().". Trace: ".$e->getTraceAsString());
echo "ERROR";
exit;
}
put this code in your bootstrap/config file and quit wrapping every query into try-catch.
No, it is not necessary in php. When your php process finished, the connection will be closed too.

PDO error handling and storing errors in database

How would I go about getting PDO statements to generate a safe error message? I don't want the user to see the error message. I want them to get directed to a page that says a clean message, "Whoops something unexpected happened!". I would also like to log the errors in a database to review and catch errors others are generating.
I'm using PHP and MySQL.
I found that when you make your connection you can set your error handling like this.
$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Anyone do anything like this before?
So this is just a suggestion as I have never tried this but after thinking about it a bit I think it would be an interesting option to explore. As I am fairly new to PHP & PDO I'm sure there are other and better ways.
Perhaps you could try using the try function of PHP and then instead of echo'ing (if failed) the PDOException you could run another function that prints it to a text file. Something like.
<?php
try {
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
foreach($dbh->query('SELECT * from FOO') as $row) {
print_r($row);
}
$dbh = null;
} catch (PDOException $e) {
$strFileName = 'whatever.txt';
if(!is_writable($strFileName))
die('Change permisions to ' . $strFileName);
$handle = fopen($strFileName, 'a+');
fwrite($handle, "\r" . $e->getMessage() . "\r");
fclose($handle);
}
?>
This way you would avoid a DB connection (which is the problem I guess) but still save the error.
You would perhaps want to omit the echo'd text after die within the if statement.
I think it is better to write your logs to a file, instead of a database. Especially since you want to log PDO errors, which indicate something is wrong with your database connection.
You can show the user a nice error page by catching your errors. You can redirect your users to your error page then, in case something went wrong.
You have to understand that PDO do not generate a "safe" or "unsafe" error message. It does generate an error message. That's all. The rest is is the responsibility of site-wide PHP settings.
PDO is not the only source of errors. Why care of PDO errors only? Why not to handle ALL errors the same way?
Want errors logged? It's a matter of one PHP ini setting.
Want errors not to be displayed? It's a matter of one PHP ini setting.
Want generic error page to be shown? It's a matter of simple function that will handle all errors at once.
Everything can be done proper and straight way, without wrapping every statement into try catch. Without writing into log manually. Without even single additional line of code.
You need to set up PHP error handling, not PDO.
And of course, it makes absolutely no sense in trying to store a database error in the same database that failed you right now. Errors have to go into error log on a live server and on screen - on a local development PC.
Anyone do anything like this before?
Sure. Every single one of 1000000s sites in the world. The way described above.

What is proper way to handle mysql errors with php?

What is the "proper" way to deal with errors when manipulating a sql database with php?
What Im currently doing looks like this:
$connection = new mysqli('hostname', 'user', 'pass', 'database');
if ($connection->connect_errno) {
reportError("DB_CONNECTION_ERROR", $connection->connect_errno, $connection->connect_error);
displayError("DB_CONNECTION_ERROR");
}
$stmt = $connection->stmt_init();
$q = "query";
$stmt->prepare($q);
$stmt->bind_param('s', $username);
$stmt->execute();
reportError() is part of an error handling file I wrote and logs the error in a database
displayError() is part of the same file and tells the page what to display (as opposed to displaying the actual error).
However Im not sure of how to check for other errors, such as whether a statement was successfully prepared or whether a query was successful. Any recommendations appreciated!
Don't you find it quite odd to write database connection errors... into database?
I see also no point in having custom displayError() function. It should be generic _503() function, sending corresponding header along with general excuses.
I see no point in having custom logError() function either. PHP quite capable to log errors itself. trigger_error() serves me best.
Im not sure of how to check for other errors, such as whether a statement was successfully prepared
Ah, this one. Exceptions.
Mysqli should throw an exception if something went wrong. See mysqli_sql_exception for more details.
In your client code, you can then wrap your code in try/catch blocks:
try {
} catch (Exception $e) {
}
Sometimes, there are exceptions that can't be solved within a try/catch block, for example, the database server is down, and a site that is heavily reliant on the database would not be able to function anyway.
For those very critical problems, you can allow the exception to bubble upwards. You should then set an exception handler at the beginning of your script to catch those exceptions, notify the administrator and do some logging, then display an 500 error page to the user.

PHP Error handling in WordPress plugin

I am a newbie to both PHP and Wordpress (but do ok in C#), and am struggling to understand the error handling in a custom plugin I am attempting to write. The basics of the plugin is to query an exsiting MSSQL database (note its not the standard MYSQL db...) and return the rows back to the screen. This was working well, but the hosting provider has taken my database offline, which led me to the error handling problem (which I thought was ok).
The following code is failing to connect to the database (as expected), but puts an error onto the screen and stops the page processing. It does not even output the 'or die' error text.
QUESTION: How can I just output a simple "Cant load data" message, and continue on normally?
function generateData()
{
global $post;
if ("$post->post_title" == "Home")
{
try
{
$myServer = "<servername>";
$myUser = "<username>";
$myPass = "<password>";
$myDB = "<dbName>";
//connection to the database
$dbhandle = mssql_connect($myServer, $myUser, $myPass)
or die("Couldn't open database $myDB");
//... query processing here...
}
catch (Exception $e)
{
echo "Cannot load data<br />";
}
}
return $content;
}
Error being generated: (line 31 is $dbhandle = mssql_connect...)
Warning: mssql_connect() [function.mssql-connect]: Unable to connect to server: <servername> in <file path> on line 31
Fatal error: Maximum execution time of 30 seconds exceeded in <file path> on line 31
First of all, if mssql_connect raises a warning when there's a problem, there is not much you can do to avoid it : the only thing you could do is hide it, using the # operator :
if (($dbhandle = #mssql_connect($myServer, $myUser, $myPass)) === false) {
// connection failed
}
Note : you should not die() when a connection error occurs : it'll stop the execution of the whole application, which is most certainly not desired.
The Fatal Error is a second problem (which is probably a consequence of the first one).
Note that you cannot recover from a Fatal Error : it is Fatal. Which means you must avoid it, one way or another.
Here, the error is that your script is working for more than max_execution_time seconds ; as the error is reported on the mssql_connect line, I suppose the script is waiting for the connection to succeed, and it doesn't get etablished in less that 30 seconds.
I don't have an SQL Server database to test, but looking at the Runtime Configuration section of the manual for mssql, I'd say that these look interesting :
name Default value
mssql.connect_timeout "5"
mssql.timeout "60"
You could try changing those,
either in your php.ini file, if you can modify it
or using ini_set() before trying to connect.
In the second case, something like this might do the trick :
ini_set('mssql.connect_timeout', '3');
ini_set('mssql.timeout', '3');
You may also want to look at WP_Error Class for handling your errors in an elegant manner. Note that this is a generic approach & that you will have to handle the particular error detection logic separately. WP_Error will help you in gathering all the errors in one place.

Categories