How to get mysqli instance recognised in functions? - php

I'm in the process of upgrading from mysql to mysqli.
All my mysql code was procedural, and I'd now like to convert to OOP, as most mysqli examples online are in OOP.
The problem I'm having is that, with mysql, once I had set up a connection, I never had to inject that connection into any functions as arguments for mysql to be accessible in the function.
Here is my old connection code:
$location = "localhost";
$user = "rogerRamjet";
$pass = "bestPassInTheWorld";
$dbName = "myDBName";
$link = mysql_connect($location, $user, $pass);
if (!$link) {
die("Could not connect to the database.");
}
mysql_select_db("$dbName") or die ("no database");
And an example function that has access to the mysql connection, without $link needing to be injected into the function:
function getUser($data)
{
$data=mysql_real_escape_string($data);
$error = array('status'=>false,'userID'=>-1);
$query = "SELECT `user_id`, `user_email` FROM `myTable` WHERE `data`='$data'";
if ($result = mysql_query($query))
{
$row = mysql_fetch_array($result, MYSQL_ASSOC);
if ($row['user_id']!="")
{
return array( 'status'=>true, 'userID'=>$row['user_id'], 'email'=>$row['user_email'] );
}
else return $error;
}
else return $error;
}
And here's my new mysqli connection:
$mysqli=new MySQLi($location, $user, $pass, $dbName);
So, to upgrade the first line in the above function, I'd need:
$data = $mysqli->real_escape_string($data);
But that throws the error:
Undefined variable: mysqli
Does this mean that for any function needing access to $mysqli, I need to inject $mysqli as an argument into it, or is there a way for it to be accessible the way mysql is without injection?
I know I need to move to prepared statements, but this is just so I can get my head around mysqli basics.

Making the variable global is bad practice. The singleton pattern solves the issue of needing to share one instance of an object throughout an application lifecycle. Consider using a Singleton.

The crude solution would be global $mysqli; as first line of your function. But as hsan wrote, read about PHP variable scope

Related

A template code to execute stored procedures

Usually when it is necessary to communicate with MySQL via PHP I use a template similar to the one below (which is similar to the ones available in beginners tutorials):
// Exception Handler
class customException extends Exception {}
// Database Link (include file in a private directory)
function db_connect()
{
$hostname = "localhost";
$username = "username";
$password = "password";
$database = "database";
$connection = mysqli_connect($hostname, $username, $password, $database);
return $connection;
}
// Template for calling common types of stored procedures:
// select a table row based on the primary key (pk)
function select_pk($connection, string $pk): array
{
// if other database is needed
mysqli_select_db($connection, "database1");
// query execution
$query = sprintf("CALL select__pk('%s')", mysqli_real_escape_string($connection, $pk));
$resource = mysqli_query($connection, $query);
$result = mysqli_fetch_assoc($resource);
// prepare for next query
mysqli_free_result($resource);
while(mysqli_more_results($connection)) mysqli_next_result($connection);
// use exception handling if necessary
if(!isset($result)) throw new customException('pk not found');
return $result;
}
// Typical execution
$connection = db_connect();
try
{
$result = select_pk($connection, $pk);
}
catch(customException $e)
{
/*** do something ***/
}
Although this template is, so far, working fine (single server), I have the impression that:
the preparation for next query is overcomplicated (mysqli_free_result, mysqli_more_results and mysqli_next_result)
it does not deal properly with errors
Question
Any comments or advice on how to improve this template?
Well, first I would give a generalized answer that likely would help other people stumbling upon this question and then review the particular case of yours.
I asked myself exactly the same question a long time ago and eventually came to a set of solutions that ease the database operations using mysqli.
Mysqli connection
I have doubts about storing a connection code in a function. It asks to be misused. A connection to a single database should be established strictly once during a single HTTP request/php instance. but a function's purpose to be called multiple times. It would be better to put the connection code in a file instead, and then just include this file in your code in a single place.
I've got a canonical mysqli connection code that deals with a whole lot of problems before they even appear. So, instead of function db_connect() let's create a file called mysqli.php and put the following code there
<?php
$host = '127.0.0.1';
$db = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
$conn = new mysqli($host, $user, $pass, $db);
$conn->set_charset($charset);
} catch (\mysqli_sql_exception $e) {
throw new \mysqli_sql_exception($e->getMessage(), $e->getCode());
}
unset($host, $db, $user, $pass, $charset); // we don't need them anymore
Among other solutions it will translate mysql errors into PHP exceptions which is, basically all you need in order to deal with errors.
Running prepared queries
The next problem is rather elaborate code required for the prepared queries in mysqli. To deal with it i wrote a mysqli helper function that eases the process dramatically.
note that although your current approach with mysqli_real_escape_string() is technically safe, it is frowned upon never the less, as it's a subject of human errors of all sorts. Better stick to prepared statements for all queries that involve a PHP variable as input.
So next solution would be a helper function like this
function prepared_query($mysqli, $sql, $params = [], $types = "")
{
if (!$params) {
return $mysqli->query($sql);
}
$types = $types ?: str_repeat("s", count($params));
$stmt = $mysqli->prepare($sql);
$stmt->bind_param($types, ...$params);
$stmt->execute();
return $stmt->get_result();
}
and you will get a tool that will make prepared statements as smooth as regular queries
Calling stored procedures with mysqli
Stored procedures are not easy because of a quirk: every call returns more than one resultset and therefore we need to loop over them. We cannot avoid it but at least we can automatize this process too. We can write a function that encapsulates all the resultset jiggery-pokery.
function prepared_call($mysqli, $sql, $params = [], $types = ""): array
{
$resource = prepared_query($mysqli, $sql, $params, $types);
$data = $resource->fetch_all(MYSQLI_ASSOC);
while(mysqli_more_results($mysqli)) mysqli_next_result($mysqli);
return $data;
}
A specifik function to call for a PK
And finally we can rewrite your select_pk() function
function select_pk($mysqli, string $pk): array
{
$data = prepared_call($mysqli, "CALL select__pk(?)", $pk);
return $data[0] ?? null;
}
I am not really sure we need an exception here though:
include 'mysqli.php';
$result = select_pk($mysqli, $pk);
if (!$result) {
/*** do something ***/
}

PHP: Can not use global variable in function to connect mysqli

I've been searching stack to get my answer, but nothing fixed my problem. So here's my shot:
$conn = mysqli_connect('localhost', 'username', 'pass', 'db');
function GetArticle() {
global $conn;
$sql = "sql query";
$getresult = mysqli_query($conn, $sql);
..
}
This doesn't seem to work. If I put $conn inside the function, it works fine.
Any ideas?
I don't know your exact situation, but in general I do not see the benefit to using $conn as global. Your function depends on a mysqli connection in order to work, so just make the connection a function parameter.
function GetArticle($conn) {
$sql = "sql query";
$getresult = mysqli_query($conn, $sql);
..
}
Then after you have established your connection, you can call the function with your connection object as its argument.
$conn = mysqli_connect('localhost', 'username', 'pass', 'db');
$article = GetArticle($conn);
I think this is a more manageable approach than trying to keep track of whether $conn is available in global scope before calling the function.

PHP function to open database using PDO

I realize this is probably super simple but i just started taking peoples advice and im converting a small program from mysql to PDO as an attempt to learn and switch to PDO.
The script is a script that shows you how to build a shopping cart, so keep in mind its focused on a learning audience like myself. Anyway i converted the old script here:
function db_connect()
{
$connection = mysql_pconnect('localhost', 'database_1', 'password');
if(!$connection)
{
return false;
}
if(!mysql_select_db('database_1'))
{
return false;
}
return $connection;
}
to this which does connect fine:
function db_connect() {
//Hostname
$hostname = 'xxx.com';
//username
$username = 'xxx';
//password
$password = 'xxx';
try {
$connection = new PDO("mysql:host=$hostname;dbname=database_1", $username, $password);
}
catch(PDOException $e){
echo $e->getMessage();
}
}
Now in other parts of the script before accessing the database it does this:
$connection = db_connect();
Now i have 2 questions. First is to help me understand better what is going on.
I understand in the original mysql function we connect to the database, if the connection is unsuccessful or the database doesnt exist it returns false. If it does connect to the database then it returns true.
With that i mind i dont understand this:
$connection = db_connect();
Isnt that just assigning true or false to the $connection variable, if so then whats going on in this part of the code.
$price = 0.00;
$connection = db_connect();
if (is_array($cart))
{
foreach($cart as $id => $qty)
{
$query = "SELECT price
FROM products
WHERE products.id = '$id' ";
$result = mysql_query($query);
if($result)
{
$item_price = mysql_result($result, 0, 'price');
$price += $item_price * $qty;
}
}
}
Instead couldn't i just create an include file with the PDO connection and no function and include that at the top of each page i run scripts on. I just don't understand where the $connection = db_connect comes in.
So the 2nd question if my above suggestion is not the answer is how do i return a boolean value from the connection function to return true or false (If i even need to)
There is one essential difference between old mysql and PDO: both these libraries require a resource variable to connect with. If you take a look at mysql_query() function definition, you will notice the second parameter, represents such a resource.
$connection variable returned by your old function by no means contain boolean value but such a resource variable. Which can be used in every mysql_query call.
But while for mysql ext this resource parameter being optional, and used automatically when not set, with PDO you have to address this resource variable explicitly. Means you cannot just call any PDO function anywhere in the code, but only as a method of existing PDO object. Means you have to make this variable available wherever you need PDO.
Thus, you need not a boolean but PDO object.
Here is the right code for the function:
function db_connect()
{
$dsn = "mysql:host=localhost;dbname=test;charset=utf8";
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
return new PDO($dsn,'root','', $opt);
}
now you can use it this way
$pdo = db_connect();
but note again - unlike with mysql_query(), you have to always use this $pdo variable for your queries.
Further reading is PDO tag wiki
As you guessed from the context, db_connect() is supposed to return the connection object. Your converted version doesn't return anything, which is a problem.
With the mysql module, you can run queries without using the connection object - this is not the case with PDO. You'll need to use the connection object to run any queries -
$result = $connection->query('SELECT * FROM foo');
First off, let me congratulate you for making the effort to learn PDO over mysql_*. You're ahead of the curve!
Now, a few things to understand:
PDO is OO, meaning the connection to the database is represented by a PDO Object.
Your db_connect() function should return the object that gets created.
Passing in the parameters required by PDO will give you more flexibility!
So what we have is:
function db_connect($dsn, $username, $password)
{
$conn = new PDO($dsn, $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //This makes sure that PDO will throw PDOException objects on errors, which makes it much easier enter code hereto debug.
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); //This disables emulated prepared statements by PHP, and switches to *true* prepared statements in MySQL.
return $conn; //Returns the connection object so that it may be used from the outside.
}
Now, you may have noticed we aren't checking for PDOExceptions inside of the function! That's because you can't handle the error from inside of the function correctly (becuase you don't know what you would want to do? Would you terminate the page? Redirect to an error message?). So you can only know it when you call the function.
So usage:
try {
$connection = db_connect("mysql:host=$hostname;dbname=database", "user", "pass");
}
catch (PDOException $e) {
echo "Database error! " . $e->getMessage();
}
Further Reading!
The PDO Manual entry - is super easy and super useful. I recommend you read all of it.

mysqli_real_escape_string: Non-Object in HelperFunction

I have a question regarding the following Code. It's a PHP-Class which I plan to use as a DB-Handler. For the INSERT I'm using a little helper function, mainly because I'm too lazy to sanitize the strings from hand. Here is the Code:
<?php
class Db{
/*
* Configure DB settings here, make sure php is in good health. Check phpinfo();
*/
private $MYSQL_HOST = 'localhost';
private $MYSQL_USER = 'root';
private $MYSQL_PASS = '******';
private $db;
function select($selected_db, $sql){
//Create new object out of connection to db
$this->db = #new mysqli($this->MYSQL_HOST, $this->MYSQL_USER, $this->MYSQL_PASS, $selected_db);
//If there aren't any errors
if(mysqli_connect_errno() == 0){
$result = $this->db->query($sql);
if($result){
$result = $result->fetch_array(MYSQLI_ASSOC);
}else{
echo "There is a problem with the query";
}
} else { //If you couldn't connect to DB at all
die("No connection possible: " . mysqli_connect_error());
}
//Close connection
$this->db->close();
return $result;
}
function dirtyLittleHelper($string){
//Change each character into its HTML equivalent
$string = htmlentities($string);
//Create a legal SQL string from input
$string = $this->db->mysqli_real_escape_string($string);
return $string;
}
}
?>
Now for the error I get:
Fatal error: Call to a member function mysqli_real_escape_string() on a non-object in /path/to/file/db_class.php on line 35
The Question is simply why? I don't want to use the strings unsanitized and I don't want to use mysql_real_escape_string since it's deprecated.
Since this is my first time working with SQL, I'm taking the risk to post a duplicate of a question. It is important for me to learn it proper and not just use a version that works, leaving ways in to the database open.
Thanks in advance,
stiller_leser
EDIT
Thanks, I guess I'm going with elusive's hint. I did not know, that it prepare does that. Anyway just to check, a proper insert-function could look like that right?
function insert($selected_db, $sql){
//Create new object out of connection to db
$this->db = #new mysqli($this->MYSQL_HOST, $this->MYSQL_USER, $this->MYSQL_PASS, $selected_db);
//If there aren't any errors
if(mysqli_connect_errno() == 0){
//If you could prepare query
if($result = $db->prepare( $sql )){
//Execute query
$result->execute();
} else { //If you couldn't prepare query
echo "There is a problem with the query";
}
} else { //If you couldn't connect to DB at all
die("No connection possible: " . mysqli_connect_error());
}
//Close connection
$this->db->close();
The call should instead be like this
$string = $this->db->real_escape_string($string);
Please check the documentation.
Alternative solution would be:
$string = mysqli_real_escape_string($this->db, $string);
Your class seems inconsistent and unusable to me.
You are passing $selected_db to your functions, but never use it
instead of that you are creating a new connection for the every query you run(!)
there is no use for that little helper function can be seen.
and I suspect it's still unusable and unsafe anyway
calling prepare/execute doesn't protect your queries by itself. it have to be fed with variables to be bound to do any good
binding variables in mysqli is A PAIN
that's why you've been told to use PDO
Also, with adequately implemented placeholders, you won't need no special functions like delete(). update() and such, using conventional query() method to run them all queries:
$data = array('name' => 'John','surname'=>'doe', ...);
$db->query("INSERT INTO ?n SET ?u", $table, $data);
So, if you need PHP-Class to use as a DB-Handler, here is a SafeMysql which is written with some knowledge and experience

PHP Database Class and new() Function

I have, what I think/hope, is a very simple PHP question. I have made a class to create database connections and issue common queries. I am trying to open two different database connections by creating two objects from the same database class. My code is as follows:
//connect to DB
$dbh = new DB('localhost', 'db1', 'user', 'pass');
//check connection
if(!$dbh->getStatus()) {
echo($dbh->getErrorMsg());
die;
}//if
//connect to DB 2
$dbh2 = new DB('localhost', 'db2', 'user', 'pass');
//check connection
if(!$dbh2->getStatus()) {
echo($dbh2->getErrorMsg());
die;
}//if
However, when I call a method for $dbh to query the database, it attempts to query with the credentials for $dbh2.
My DB constructor is below:
class DB {
function __construct($host, $db, $user, $pass) {
$dbh = mysql_connect($host, $user, $pass);
mysql_select_db($db, $dbh);
if(!$dbh) {
$this->status = false;
$this->error_msg = 'Error connecting to database: '.mysql_error();
return(false);
}//if
$this->dbh = $dbh;
$this->resetStatusAndErrors();
return($dbh);
}//_construct
Simple solution: Use PDO instead. It does exactly what you want, probably has better syntax and implementation and abstracts the interface for DB access.
You're not showing the full class, but the most probable reason is that you are not passing the current connection to the mysql_query() command.
Save $dbh as a property of your class, and add the connection parameter to each mysql_ function that accepts it:
mysql_query("SELECT * from.......", $this->dbh);
That said, if you are building this from scratch at the moment, take a look whether you don't want to use PDO instead. It is better, safer and more flexible than the old style mySQL library.
If you are using the mysql extension (using either mysqli or PDO_MySQL would give both superior performance, more features, etc., so check that for new code), you'll have to store the database handle, and use that on every mysql_* call:
class db {
....
function query($query){
return mysql_query($query, $this->dbh);//notice the second parameter.
}
}

Categories