I'm trying to display a server status, based upon whether the database can be connected to or not. With the old school mysql_connect() and mysqli_connect() it was easy. I'm trying to stay modern, so I'm using PDO, but I can't figure out how-to suppress the default warning. From what I can tell, you need to use the getMessage() function for it to print the PDO warning, but I'm not using it.
Here's my code:
8 $dbstatus = 1;
9 try {
10 $db = new PDO($dbms . ':host=' . $dbhost . ';port=' . $dbport . ';dbname=' . $dbname, $dbuser, $dbpasswd);
11 $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
12 } catch(PDOException $e) {
13 $dbstatus = 0;
14 }
15 if($dbstatus == 1) {
16 echo '<span style="color: green">DB Up</span>';
17 } else {
18 echo '<span style="color: red">DB Down</span>';
19 exit;
20 }
All the connection variables are supplied and correct, except the $dbhost, which is intentionally broken to test this. Now, it produces the desired results, but is also prints a warning message too:
Warning: PDO::__construct(): php_network_getaddresses: getaddrinfo failed: No such host is known. in C:\xampp\htdocs\cd\includes\dbconnect.php on line 10
If I correct the $dbhost variable, it works fine, so I know the issue isn't with the PDO statement being usable.
Any ideas on what I'm missing?
Solution
I used a variation of what was supplied by jeroen:
if(filter_var(gethostbyname($dbhost), FILTER_VALIDATE_IP)) {
$dbstatus = 1;
try {
$db = new PDO($dbms . ':host=' . $dbhost . ';port=' . $dbport . ';dbname=' . $dbname, $dbuser, $dbpasswd, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
} catch(PDOException $e) {
$dbstatus = 0;
}
} else {
$dbstatus = 0;
}
if($dbstatus == 1) {
echo '<span style="color: green">DB Up</span>';
} else {
echo '<span style="color: red">DB Down</span>';
exit;
}
Thank you for the help and I hope this helps someone else! ^^
The only thing I can see here, is that you tell PDO to throw exceptions after you have tried to open the connection. That is most likely too late.
What you could do instead, is send that option to the constructor directly using the 4th parameter:
try {
$opts = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
$db = new PDO($dbms . ':host=' . $dbhost . ';port=' . $dbport . ';dbname=' . $dbname,
$dbuser, $dbpasswd, $opts);
} catch(PDOException $e) {
...
That will probably solve your problem.
Edit: If the host name is provided by the user, you could validate it before sending it to the PDO constructor.
For example using:
if (filter_var(gethostbyname($user_provided_host_name), FILTER_VALIDATE_IP)) {
// valid hostname / ip address
}
That will work for domain names, localhost and ip addresses.
Related
Forgive me, as I've not worked in php for years. I'm picking up some old code to get working again and I'm having a strange issue.
I'm writing in php with wordpress. As I am editing the code, I've noticed the php tag is closing after the following:
<?php
$databaseHost = "Localhost";
$databaseName = "testDB";
$databaseUser = "TESTUSER";
$databasePassword = "TESTPASS";
$coin_id = (isset($_POST['coin_id'])) ? $_POST['coin_id'] : '';
try {
$db = new PDO('mysql:host=' . $databaseHost . ';dbname=' . $databaseName . ';charset=utf8', $databaseUser, $databasePassword);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$request = "SELECT
_7UR_participants_database.city,
_7UR_participants_database.state,
_7UR_participants_database.country,
_7UR_participants_database.zip,
_7UR_participants_database.coin_id,
FROM _7UR_participants_database GROUP BY _7UR_participants_database.coin_id ASC";
$stmt = $db->query($request);
$item_info = $stmt->fetchAll();
} catch (PDOException $e) {
echo "Exception: " . $e->getMessage();
exit;
} // Try / catch end
?>
Everything after that > following $db- is not included in the php. The php tag is closing with that last >. Do I need to escape the character or something of that nature?
since your formatting is hard to undertand i rewrite it on my liking that it "migth" actually works
$dsn = "mysql:host=localhost;dbname=testDB;charset=utf8mb4"; // most cool kids use charset=utf8mb4
$options = [
PDO::ATTR_EMULATE_PREPARES => false, // turn off emulation mode for "real" prepared statements
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, //turn on errors in the form of exceptions
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, //make the default fetch be an associative array
];
try {
$dbh = new PDO($dsn, "TESTUSER", "TESTPASS", $options);
} catch (Exception $e) {
error_log($e->getMessage());
echo ("Error Code: " . $e->getCode() . "<br>"); // never use echo on public release build it would leak your database credential this is optional great for troubleshooting
echo ("Error Message: " . $e->getMessage() . "<br>");
exit('Something weird happened');//
}
$request = $dbh->prepare("SELECT
_7UR_participants_database.city,
_7UR_participants_database.state,
_7UR_participants_database.country,
_7UR_participants_database.zip,
_7UR_participants_database.coin_id,
FROM _7UR_participants_database GROUP BY _7UR_participants_database.coin_id ASC";
$request->execute([]); // you do not have something like this also i never put value since i don't know what your doing
$item_info = $request->fetchAll(); // store the fetched on $item_info also you need to indicate what data type you fetching by default it always PDO::FETCH_ASSOC
I'm trying to connect to a mysql via ssh in a php script.
I set a tunnel whit this command:
ssh -t myusername#ssh.studenti.math.unipd.it -L30080:tecweb:80 -L8443:tecweb:443 -L8022:tecweb:22
This is the php code:
$host = "localhost";
$port = "8022";
$dbname = "surbani";
$charset = 'utf8';
$user = "surbani";
$password = 'iequu2iJoo6eich7';
$dsn = 'mysql:host=' . $host . ';port=' . $port . ';dbname=' . $dbname . ';charset=' . $charset;
try {
$dbh = new PDO($dsn, $user, $password, array(
PDO::ATTR_EMULATE_PREPARES => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
));
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
When I run it, it display the error: "Packets out of order. Got: 45 Expected: 0." and "MySQL server has gone away". I have no access to server, but when i connect to phpmyadmin through a broswer (localhost:30080/phpmyadmin) it works.
Could anyone help me please?
Thank for all in advance.
EDIT: I tried to add a new tunnel (-L33306:tecweb:3306) and change the port in php script. Same error in client, but the server console displays: "channel 9: open failed: connect failed: Connection refused"
I'm doing an operation that inserts hundreds of records into a MySQL database.
After inserting exactly 176 records I get this error:
[PDOException] SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
Any ideas of how could I solve it?
The process is with PHP.
I would venture to say the problem is with wait_timeout. It is set to 30 seconds on my shared host and on my localhost is set for 28800.
I found that I can change it for the session, so you can issue the query: SET session wait_timeout=28800
UPDATE The OP determined that he also needed to change the variable interactive_timeout as well. This may or may not be needed for everyone.
The code below shows the setting before and after the change to verify that it has been changed.
So, set wait_timeout=28800 (and interactive_timeout = 28800) at the beginning of your query and see if it completes.
Remember to insert your own db credentials in place of DB_SERVER, DB_USER, DB_PASS, DB_NAME
UPDATE Also, if this does work, you want to be clear on what you are doing by setting wait_timeout higher. Setting it to 28800 is 8 hours and is a lot.
The following is from this site. It recommends setting wait_timeout to 300 - which I will try and report back with my results (after a few weeks).
wait_timeout variable represents the amount of time that MySQL will
wait before killing an idle connection. The default wait_timeout
variable is 28800 seconds, which is 8 hours. That's a lot.
I've read in different forums/blogs that putting wait_timeout too low
(e.g. 30, 60, 90) can result in MySQL has gone away error messages. So
you'll have to decide for your configuration.
<?php
$db = new db();
$results = $db->query("SHOW VARIABLES LIKE '%timeout%'", TRUE);
echo "<pre>";
var_dump($results);
echo "</pre>";
$results = $db->query("SET session wait_timeout=28800", FALSE);
// UPDATE - this is also needed
$results = $db->query("SET session interactive_timeout=28800", FALSE);
$results = $db->query("SHOW VARIABLES LIKE '%timeout%'", TRUE);
echo "<pre>";
var_dump($results);
echo "</pre>";
class db {
public $mysqli;
public function __construct() {
$this->mysqli = new mysqli(DB_SERVER, DB_USER, DB_PASS, DB_NAME);
if (mysqli_connect_errno()) {
exit();
}
}
public function __destruct() {
$this->disconnect();
unset($this->mysqli);
}
public function disconnect() {
$this->mysqli->close();
}
function query($q, $resultset) {
/* create a prepared statement */
if (!($stmt = $this->mysqli->prepare($q))) {
echo("Sql Error: " . $q . ' Sql error #: ' . $this->mysqli->errno . ' - ' . $this->mysqli->error);
return false;
}
/* execute query */
$stmt->execute();
if ($stmt->errno) {
echo("Sql Error: " . $q . ' Sql error #: ' . $stmt->errno . ' - ' . $stmt->error);
return false;
}
if ($resultset) {
$result = $stmt->get_result();
for ($set = array(); $row = $result->fetch_assoc();) {
$set[] = $row;
}
$stmt->close();
return $set;
}
}
}
Thanks #mseifert.
Your idea worked by doing the same with two variables.
interactive_timeout & wait_timeout
I copied the config from a local database:
SHOW VARIABLES LIKE '%timeout%'
Local db:
Remote db:
I did this inside the connect and disconnect and worked:
mysql_query("SET SESSION interactive_timeout = 28800;");
$result = mysql_query("SHOW VARIABLES LIKE 'interactive_timeout';");
$row = mysql_fetch_array($result);
$interactive_timeout = $row["Value"];
echo("interactive_timeout" . " = " . $interactive_timeout . "\n");
mysql_query("SET SESSION wait_timeout = 28800;");
$result = mysql_query("SHOW VARIABLES LIKE 'wait_timeout';");
$row = mysql_fetch_array($result);
$wait_timeout = $row["Value"];
echo("wait_timeout" . " = " . $wait_timeout . "\n");
Surprisingly it worked with GoDaddy.
I will accept your answer as valid #mseifert since you gave me the original idea.
Thanks a lot.
Let us hope this is useful in the future to solve the 2006 MySQL error for other developers.
In my case, when I got this error on the client side, the server side was
(Got a packet bigger than 'max_allowed_packet' bytes)
So I increase the value of the max_allowed_packet, and so far, no more issues.
On Google Cloud Platform, I edit the DB and add a Database flag and set the value to
max_allowed_packet=134217728
(which is 2^27 = 128M)
As you can only input numbers.
On regular instances, you can follow the doc here :
https://dev.mysql.com/doc/refman/8.0/en/packet-too-large.html
Assume your codes is:
// your codes
$pdo = db::connection()->getPdo();
$stmt = $pdo->prepare($sql);
$result = $stmt->execute($params);
So add below codes before your sql query:
$pdo = db::connection()->getPdo();
// Increase interactive_timeout
$prepend_sql = "SET SESSION interactive_timeout = 28800;";
$stmt = $pdo->prepare($prepend_sql);
$stmt->execute($params);
// Increase wait_timeout
$prepend_sql = "SET SESSION wait_timeout = 28800;";
$stmt = $pdo->prepare($prepend_sql);
$stmt->execute($params);
// your codes
/* $pdo = db::connection()->getPdo(); */
$stmt = $pdo->prepare($sql);
$result = $stmt->execute($params);
Another possible reason would be your client is trying to connect using SSL. while the MySQL/MariaDB server is not expecting that.
a solution is to check if the connection is active, if not re-establishing the connection, here it worked perfectly
<?php
require_once ('config.php');
class DB {
private static $instance;
private function __construct() {
;
}
public static function getInstance() {
if (!isset(self::$instance)) {
try {
self::$instance = new PDO('mysql:host=' . DB_HOST . ';dbname=' . DB_NAME, DB_USER, DB_PASS);
self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
self::$instance->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
try {
$testConn = self::$instance->prepare('SELECT 1');
$testConn->execute();
$testConn = $testConn->fetchAll(PDO::FETCH_ASSOC)[0];
} catch (Exception $ex) {
try {
self::$instance = new PDO('mysql:host=' . DB_HOST . ';dbname=' . DB_NAME, DB_USER, DB_PASS);
self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
self::$instance->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
} catch (PDOException $e) {
echo $e->getMessage();
}
}
return self::$instance;
}
public static function prepare($sql) {
return self::getInstance()->prepare($sql);
}
public static function lastInsertId() {
return self::getInstance()->lastInsertId();
}
}
I'm trying to display a server status, based upon whether the database can be connected to or not. With the old school mysql_connect() and mysqli_connect() it was easy. I'm trying to stay modern, so I'm using PDO, but I can't figure out how-to suppress the default warning. From what I can tell, you need to use the getMessage() function for it to print the PDO warning, but I'm not using it.
Here's my code:
8 $dbstatus = 1;
9 try {
10 $db = new PDO($dbms . ':host=' . $dbhost . ';port=' . $dbport . ';dbname=' . $dbname, $dbuser, $dbpasswd);
11 $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
12 } catch(PDOException $e) {
13 $dbstatus = 0;
14 }
15 if($dbstatus == 1) {
16 echo '<span style="color: green">DB Up</span>';
17 } else {
18 echo '<span style="color: red">DB Down</span>';
19 exit;
20 }
All the connection variables are supplied and correct, except the $dbhost, which is intentionally broken to test this. Now, it produces the desired results, but is also prints a warning message too:
Warning: PDO::__construct(): php_network_getaddresses: getaddrinfo failed: No such host is known. in C:\xampp\htdocs\cd\includes\dbconnect.php on line 10
If I correct the $dbhost variable, it works fine, so I know the issue isn't with the PDO statement being usable.
Any ideas on what I'm missing?
Solution
I used a variation of what was supplied by jeroen:
if(filter_var(gethostbyname($dbhost), FILTER_VALIDATE_IP)) {
$dbstatus = 1;
try {
$db = new PDO($dbms . ':host=' . $dbhost . ';port=' . $dbport . ';dbname=' . $dbname, $dbuser, $dbpasswd, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
} catch(PDOException $e) {
$dbstatus = 0;
}
} else {
$dbstatus = 0;
}
if($dbstatus == 1) {
echo '<span style="color: green">DB Up</span>';
} else {
echo '<span style="color: red">DB Down</span>';
exit;
}
Thank you for the help and I hope this helps someone else! ^^
The only thing I can see here, is that you tell PDO to throw exceptions after you have tried to open the connection. That is most likely too late.
What you could do instead, is send that option to the constructor directly using the 4th parameter:
try {
$opts = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
$db = new PDO($dbms . ':host=' . $dbhost . ';port=' . $dbport . ';dbname=' . $dbname,
$dbuser, $dbpasswd, $opts);
} catch(PDOException $e) {
...
That will probably solve your problem.
Edit: If the host name is provided by the user, you could validate it before sending it to the PDO constructor.
For example using:
if (filter_var(gethostbyname($user_provided_host_name), FILTER_VALIDATE_IP)) {
// valid hostname / ip address
}
That will work for domain names, localhost and ip addresses.
I have to pull data from at least 3 databases, is there anything wrong with reusing my PDO objects?
$dbh = new PDO('mysql:host=' . $host . ';dbname=' . $db_name, $user, $password);
$sth = $dbh->prepare($query1);
// do something
$dbh = new PDO('mysql:host=' . $host2 . ';dbname=' . $db_name2, $user2, $password2);
$sth = $dbh->prepare($query2);
//do something else
Sorry for the edit but here's another consideration. With each of these I obviously should check whether or not the connection was successful and throw an exception if it wasn't:
if (!$dbh) {
$err=$dbh->errorInfo();
throw new Exception('Could not connect: ' . $err[2]);
}
I don't suppose there's a way to avoid this, unless I create all the connections at the same time and do if (!dbh1|!dbh2) { ... }. Just something else to consider.
When you assign $dbh to a new PDO() you are not technically reusing your pdo object. You are creating a new PDO instance and assigning it to a variable that you have previously used. There is nothing wrong with doing this as long as you understand what is occurring in your program.
EDIT:
I'm editing my answer to address the new question you've added to your previous question.
With each of these I obviously should check whether or not the connection was successful and throw an exception if it wasn't:
You can surround your connection attempt in a try catch which is a typical strategy for dealing with connection errors:
<?php
try {
$dbh = new PDO('mysql:host=localhost;dbname=databaseName', $userName, $password);
foreach($dbh->query('SELECT * from TableName') as $row) {
print_r($row);
}
$dbh = null;
} catch (PDOException $ex) {
print "Error!: " . $ex->getMessage() . "<br />";
die();
}
?>
You can do that, but ... don't. Use another variable name (they are cheap) it will make your code much easier to understand. Heck you can (should) even use variable names that will clue you in on what database your object is connecting to, ie:
// connection to data warehouse
$dbh_dataWH = new PDO('mysql:host=' . $host . ';dbname=' . $db_name, $user, $password);
// connection to crm
$dbh_crm = new PDO('mysql:host=' . $host2 . ';dbname=' . $db_name2, $user2, $password2);
This way you will be able to re-use your connection objects, and the code in my very humble opinion will be much clearer for you or anyone else that has to maintain it.