php PDO change connection during script via function - php

I have an app that has to use more than one database for reasons of client protection and archiving with a core one for administration - eg user detail storage. I appreciate the need to migrate from the mysql extension (soon to be deprecated) and I first tried mysqli but could not get away from the persistent connection, even using the change_user function, probably because the same user/pass combo is used so cross-database joins are possible.
I am implementing many stored procedures during the migration to reduce bottlenecks. MYSQL now assigns SPs to each specific database, so it is important to be connected to the right one when making a CALL. That means putting full database.table references in queries is not practical
So I have turned to PDO.
I created a test script:
$db_host="localhost";
$db_username='root';
$db_pass='';
$add='admin';
$db_name='host_base_name'.$add;
try{
$db= new PDO('mysql:host='.$db_host.';dbname='.$db_name,$db_username,$db_pass);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
$result= $db->query("SELECT firstname from centralusers WHERE usr_id='1'");
while($row=$result->fetch(PDO::FETCH_ASSOC)){echo $row['firstname']."<br>";}
}
catch(PDOException $e){echo $e->getMessage();}
$add='test';
$db_name='host_base_name_'.$add;
try{
$db= new PDO('mysql:host='.$db_host.';dbname='.$db_name,$db_username,$db_pass);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
$result= $db->query("SELECT stage_desc from action_stage WHERE stage_id='1'");
while($row=$result->fetch(PDO::FETCH_ASSOC)){echo $row['stage_desc']."<br>";}
}
catch(PDOException $e){echo $e->getMessage();}
And that successfully does the switch - I tried across three databases but have not shown the third for economy.
However, in the development code my script has a 'require' file which contains a function to handle the connection. it is sent the last part of the table name via the call (a process that has worked fine in the mysql extension. Here is that code;
function db_connect($add) {
$db=NULL;
$db_host="localhost";
$db_username='root';
$db_pass='';
$db_name='host_base_name_'.$add;
try{
$db= new PDO('mysql:host='.$db_host.';dbname='.$db_name,$db_username,$db_pass);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
}
catch(PDOException $e){echo $e->getMessage();}
return $db;
}
After the second call (to change the database) my next query produces an error that the table cannot be found - and gives the 'host_base_name_ 'part of the table reference as the original. I thought the NULL call would have destroyed the instance.
If I put the $db=NULL; in the calling script, just before the function I get a "Call to a member function query() on a non-object " Scream error.
Why does one work and not the other because they seem identical and how could I work round this?

Have separate PDOs for each database
$db_host="localhost";
$db_username='root';
$db_pass='';
$add='admin';
$db_name1='host_base_name'.$add1;One database
$db_name2='host_base_name'.$add2;Other database
try{
//PDO for one database
$db1= new PDO('mysql:host='.$db_host.';dbname='.$db_name1,$db_username,$db_pass);
$db1->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
//PDO for other database
$db2= new PDO('mysql:host='.$db_host.';dbname='.$db_name1,$db_username,$db_pass);
$db2->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);

For those coming across this in future, I found I could indeed replace the function call with a class and create a new connection to a different database. Along the way I discovered the tremendous speed improvement by replacing 'localhost' with '127.0.0.1' -halving the execution of a test script from 0.0060 seconds to 0.0029 which can be significant in complex scripts.
So here is my class - because it is included anyway at the top of the script, I put the main connection parameters at the head of the include so they could be used in the class. The $db_name is modified in the script so it refers to the correct database.
$db_host='127.0.0.1';
$db_user='root';
$db_pass='';
$db_name='host_base_name';
class databaseConnect{
public function connect($db_host, $db_user,$db_pass,$db_name){
try{
$db= new PDO('mysql:host='.$db_host.';dbname='.$db_name,$db_user,$db_pass, array(PDO::ATTR_PERSISTENT=>false));
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
}
catch(PDOException $e){echo "Error: ".$e->getMessage()."<br />"; die(); }
return $db;
}
}
I get the new database connection instance (in this case to 'host_base_nametest' using the code below I replace $con with another variable when I have to switch databases, essentially giving me totally flexible and transportable database naming within the connection:
$add='test'; // in production this comes from another variable
$db_inst=$db_name.$add; //builds the database name
$con=new databaseConnect; //calls the class
$db=$con->connect($db_host, $db_user,$db_pass,$db_inst); //gets the new instance

Related

mysqli_insert_id() is returning 0 but inserting the data

The Code is:
class anything_i
{
/*Every variable is defined and hidden fro privacy.*/
public function connect(){
return mysqli_connect(self::HOST,self::USERNAME,self::PASSWORD,self::DATBASE);
}
public function insertData($postData){
$sqli = 'INSERT INTO `payments`(`item_name`,`price`,`email`,`date`,`time`,`detail`,`volume`,`ip`,`payment_status`) VALUES ("'.$productData['name'].'","'.$productData['price'].'","'.$postData['email'].'","'.$date.'","'.$time.'","'.$postData['list'].'","'.$productData['volume'].'","'.$ip.'","On Hold")';
if(mysqli_query($this->connect(),$sqli)){
print_r(mysqli_insert_id($this->connect()));
}else{
print_r(mysqli_error($this->connect()));
}
}
}
It is inserting the data and working fine but does not return anything other than a 0 (zero) not getting any error.
Let me clear that my table contains auto_increment and my connection is fine because it is entering data just fine. Please don't disregard the question because you found similar once answered. I also found similar questions but most of them have connection problems, some of them the no AUTO_INCREMENT column and there may be some which included another query in between. So, please read it before mentioning another answer.
Here is the proof of AUTO_INCREMENT:
I suspect this code has been migrated from the legacy mysql extension where mysql_connect():
Opens or reuses a connection to a MySQL server.
This is no longer the case with mysqli_connect():
Opens a connection to the MySQL Server.
You really need to store the connection in a variable and reuse that same connection. Currently you're starting many different connections within the same script run.
When you call mysqli_connect() it will create a new connection each time. It does not reuse the previous connection. When a new connection is created all the properties of the old one are lost.
You are calling mysqli_connect() every time you call $this->connect() so you will never get the errors or the auto-generated ID.
mysqli connection should be global to your application. It does not make sense to keep recreating the same connection because it will lead to terrible performance issues and it will cause you problems like this one. If you are using dependency injection it should be easy to create the mysqli at the start of your application and then pass it as an argument to your class' constructor.
Connecting to the database using mysqli is always the same 3 lines of code. Only the details you pass as arguments are different.
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli('localhost', 'user', 'pass', 'db_name');
$mysqli->set_charset('utf8mb4'); // always set the charset
You can then pass this to your constructor whenever you create an object and store it in a private property.
Your fixed code would like this:
<?php
class anything_i {
private mysqli $db = null;
public function connect(mysqli $db) {
$this->db = $db;
}
public function insertData($postData) {
/*
...
*/
$stmt = $this->db->prepare('INSERT INTO `payments`(`item_name`,`price`,`email`,`date`,`time`,`detail`,`volume`,`ip`,`payment_status`) VALUES (?,?,?,?,?,?,?,"On Hold")');
$stmt->bind_param('ssssssss', $productData['name'], $productData['price'], $postData['email'], $date, $time, $postData['list'], $productData['volume'], $ip);
$stmt->execute();
}
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli('localhost', 'inet', '5432', 'test');
$mysqli->set_charset('utf8mb4'); // always set the charset
$obj = new anything_i($mysqli);

PHP Mysqli database connection pooling to avoid maximum user count

I surfed on Internet for past two days to create the DB pooling by PHP, but not yet achieved. I'm using PHP and MySQLi. We bought the mysqli db with 15 Maximum user connection. I want to pool the db connection to avoid the new connection. I used persistent as well as mysqli_connect. but I don't feel much different both are creating the new connection since other user already logged in. I was trying this function to get DB connection.
<?php
function getConnection(){
if ($connect){
return $connect;
}else{
$connect = new mysqli('p:xxxx','yyy','zzz','aaaa');
return $connect;
if(mysqli_connect_errno($connect))
{
echo "Server Busy";
}
}
}
?>
But above function only returns the else part. Please suggest me how to handle this. Thanks in advance. For now I'm killing the process which are in sleep mode to reduce the probability of increasing DB connection.
There is no connection pooling in PHP.
The persistent connection you are trying to use (that "p:" bit) is the most sure way to hit the maximum connection number, so get rid of it immediately.
15 concurrent connections is, actually, A LOT. Simply optimize your queries, so a typical connection would last 0.1 second, which would mean 150 concurrently executed PHP scripts which is like 1500 users on-line.
Also, you need to add
static $connect;
as the first line in your function, or is will create the new connection every time it is called, which is the actual reason why you are getting this error.
<?php
function getConnection(){
static $connect;
if (!$connect){
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$connect = new mysqli('xxxx','yyy','zzz','aaaa');
$connect->set_charset('utf8mb4');
}
return $connect;
}
Better yet, get rid of this function at all, have a separate file with the mysql connection code, and then use the $connect variable all around your code. See my article about mysqli connect for the details.
This is not how it works. See here. MySQLi will select one of available connections when you execute new mysqli('p:... The connection is persistent, not the PHP object.
function getConnection(){
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
return (new mysqli('p:xxxx','yyy','zzz','aaaa'));
}

What diffrence does a space between dbname and = does in PDO

This is the code when I try connecting to database. I have intentionally given wrong database name.
<?php
try
{
$pdo = new PDO('mysql:host=localhost;dbname=ehrp', 'root', '');
}
catch (PDOException $e)
{
echo $e->getMessage();
}
?>
This is the exception that I get, which is fine :
SQLSTATE[HY000] [1049] Unknown database 'ehrp'
But when I write this :
$pdo = new PDO('mysql:host=localhost;dbname =ehrp', 'root', '');
Notice a space between dbname and = I get nothing on screen.No error is shown.
Why is that?
The argument you're passing to the PDO constructor is in URI form, and a URI cannot contain arbitrary spaces, they all mean something. So what you've actually supplied the PDO constructor as far as it can tell is mysql:host=localhost; followed by an assigned property called "database " (with a space at the end). Since PDO does not know anything about a property called "database " there are no errors (it's a legal URI property value assignment), and as an unknown property, it just gets ignored. No errors, no warnings, you've done nothing wrong and PDO does what you ask it do to.
The PDO connection to your server on localhost succeeds, and you now have a PDO instance that is connected, and simply has not been tied to a specific database yet.
To verify this, prepare a SELECT DATABASE() statement with your PDO object, and then execute it: it should work just fine, and come back with a response indicating that you're not connected to a database yet.

How reconnect and retry a MySQL query in PHP when the database connection has been lost?

I have a PHP file which sometimes has a long execution and the MySQL connection (a mysqli object) goes stale. If I check the connection with mysqli_ping (and reconnect if mysqli_ping returns false), then I never have a problem. However, pinging before every query seems highly inefficient, so I'd like to only perform the ping check (and reconnect) upon the query failing. My problem is that I cannot seem to get my code to throw an exception upon the failure of the mysqli_query in order to trigger a reconnect.
Here is my "basic" code:
<?php
function query($query) {
$connection = $GLOBALS['global_connection'];
if(empty($connection) || mysqli_connect_errno()) $connection = connect();
try {
$result = mysqli_query($connection, $query);
} catch(Exception $e) {
if(!mysqli_ping($connection)) {
$connection = connect(); // reestablish the connection
$GLOBALS['global_connection'] = $connection; // update global connection value
$result = mysqli_query($connection, $query); // requery using the new connection
}
}
return $result;
}
The problem is that a bad connection in the mysqli_query does not cause an exception to be thrown, but it does cause an error to show up in my error log.
I have tried mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); (as suggested in other questions) before the mysqli_query but it does not change the outcome. I also have mysqli.reconnect enabled in my PHP config, but that doesn't seem to stop this.
So my question is either:
How do I throw an exception when a mysqli_query fails (due to a timed out connection)?
or
How do I automatically reconnect database in an efficient manner?
Per Barmar's help in the comments I realized all I had to do was this:
<?php
$result = mysqli_query($connection, $query);
if(empty($result)) {
if(!mysqli_ping($connection)) {
$connection = connect(); // re-establish the connection
$GLOBALS['global_connection'] = $connection; // update global connection value
$result = mysqli_query($connection, $query); // re-query using the new connection
}
}
No try{}catch needed at all! Clean and simple.
You will need to manage (throw) your own custom exceptions for this to work the way you need it too. I would consider making a database abstraction class to help handle this. The database class would have a member variable that is the connection reference and could use the mysqli built in error handling to throw custom exceptions relating to that instance of the mysqli connection object.
If you only need one database connection I might try a singleton pattern.

Access denied error when using mysql_real_escape_string()

i am trying escape some data before it goes into my database, but i keep getting this error:
Warning: mysql_real_escape_string(): Access denied for user
Now this would usually suggest that i have not connected to the database (it also states (using password: NO)).
I was a little confused by this because when connecting to a database i have a 'die' clause so if it fails to connect i get told about it. So i tested this theory by running a simple query in the same function that im trying to escape the data and it works just fine.
So why on earth won't the escape method work or get a connection to the database. I did notice that the user the error states is not the user i use to access the database its something like 'www-data#localhost'. Could it be trying to log in with a different user, if so why and how? Because i another area of my website the escape function works just fine and i didn't do anything special to make it work, just added the code into my web page.
thanks for the help.
Are there any other ways of sanitizing my code?
Okay, so here we go, when the user submits the form, i use AJAX to collect the data and put it into an obj to post(JSON encoding) it to the first PHP script which is here:
http://codepad.org/kGPljN4I
This script checks all the data is there and then calls a function to add it to the database
this Mysql class is called to escape the data and then add a new record to the database, when and instance of the class is made it makes a connection to the database:
http://codepad.org/wwGNrTJm
The third file is for constants, it holds the information for the database like pass, user and so on:
http://codepad.org/dl0QQbi9
any better?
thanks again for the help.
The problem is that you have established your connection using MySQLi, but are then calling mysql_real_escape_string(). You intend to be calling mysqli_real_escape_string() either in procedural context, or object oriented contex.
class Mysql
{
private $conn;
function __construct()
{
$this->conn = new mysqli(DB_SERVER, DB_USER, DB_PASSWORD, DB_NAME) or
die('No Connection to database!');
}
function add_non_member($data)
{
$email = $data->email;
// Procedural call
$san_email = mysqli_real_escape_string($this->conn, $email);
// Or OO call (recommended)
$san_email = $this->conn->real_escape_string($email);
// etc...
}
// etc...;
}
You're mixing ext/mysqli
$this->conn = new mysqli(DB_SERVER, DB_USER, DB_PASSWORD, DB_NAME
with ext/mysql functions:
$san_email = mysql_real_escape_string($email);
that last line should be
$san_email = $this->conn->real_escape_string($email);
I also got this access denied warning and I was able to find the solution. The problem is that I have not setup mysql db connection before calling mysql_real_escape_string function.
Solution:
Call mysql_connect($host, $user, $password) first (Or you can call your database connect function)
Then use mysql_real_escape_string($var)

Categories