No database selected inside function, what the solution with PDO? - php

I'm attempting to perform a query inside a function but it return me "No database selected". I regularly have re-initialized the PDO object inside it
$db = "mysql:host=localhost;dbname=my_database";
$pdo = new PDO($db, 'dbname', 'dbpassword');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//some operations.. Works fine!
function sendMail($to)
{
$db = "mysql:host=localhost;dbname=my_database";
$pdo = new PDO($db, 'dbname', 'dbpassword');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$qryString="SELECT Codice FROM users WHERE Mail=:mail";
$qry = $pdo->prepare($qryString);
$params = array("mail" => $to);
$qry->execute($params); //won't work
}
Note that operations on the DB outside the function works fine.
The problem is that the code won't work neither passing the global $pdo object.
This is the actual code
$db = "mysql:host=localhost;dbname=my_database";
$pdo = new PDO($db, 'dbname', 'dbpassword');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
sendMail($mail, $pdo)
function sendMail($to, PDO $pdo)
{
$qryString="SELECT Codice FROM users WHERE Mail=:mail";
$qry = $pdo->prepare($qryString);
$params = array("mail" => $to);
$qry->execute($params);
}

Variable scope has absolutely nothing to do with database connection details.
If you have a connection where database selected, the same database will be selected if you are using this connection inside a function.
So, this is a clear case of too localized question, as the problem is obviously of typo-like - connecting to wrong database, misspelling variable name, database name and such.
Unfortunately, regular PHP user has very little knowledge on performing correct, reproduceable experiment to prove their assumption. Instead, they guess the reason by indirect consequences.
You just have to write the code you told us about and see that database is selected all right. And then turn to search for real reason. Full error reporting (E_ALL) often helps a lot.

Pass the PDO object to the function, because the scope of $pdo is outside the scope of sendMail().
function sendMail(PDO $pdo, $to) {
$queryString = "SELECT Codice FROM users WHERE Mail=:mail";
$statement = $pdo->prepare($queryString);
$params = array("mail" => $to);
$result = $statement->execute($params);
}
or
function sendMail($to) {
global $pdo;
$queryString = "SELECT Codice FROM users WHERE Mail=:mail";
$statement = $pdo->prepare($queryString);
$params = array("mail" => $to);
$result = $statement->execute($params);
}

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 ***/
}

mysqli prepare statement error "MySQL server has gone away"

I'm struggling to make the jump form Procedural to Object Orientated style so if my code is untidy or flawed please be nice - here I'm passing a couple of posts via jQuery to a class to update a record when the user checks a checkbox:
Here is the database connection
class db {
private $host ;
private $username;
private $password;
private $dbname;
protected function conn()
{
$this->host = "localhost";
$this->username = "root";
$this->password = "";
$this->dbname = "mytest";
$db = new mysqli($this->host, $this->username, $this->password, $this->dbname);
if($db->connect_errno > 0){
die('Unable to connect to database [' . $db->connect_error . ']');
}
return $db;
}
}
Here is the update class
class updOrders extends db {
public $pid;
public $proc;
public function __construct()
{
$this->pid = isset($_POST['pid']) ? $_POST['pid'] : 0;
$this->proc = isset($_POST['proc']) ? $_POST['proc'] : 1;
// $stmt = $this->conn()->query("UPDATE tblorderhdr SET completed = ".$this->proc." WHERE orderid = ".$this->pid);
$stmt = $this->conn()->prepare("UPDATE tblorderhdr SET completed = ? WHERE orderid = ?");
$stmt->bind_param('ii', $this->proc, $this->pid);
$stmt->execute();
if($stmt->error)
{
$err = $stmt->error ;
} else {
$err = 'ok';
}
/* close statement */
$stmt->close();
echo json_encode($err);
}
}
$test = new updOrders;
When I comment out the prepare statement and run the query directly (commented out) it updates, when I try and run it as a prepare statement it returns an error "MySQL server has gone away".
I have looked at your code and I found this.
$stmt = $this->conn()->prepare("UPDATE tblorderhdr SET completed = ? WHERE orderid = ?");
I wrote nearly the same. But I saw you separated the connection from the prepare function.
$db = $this->conn();
$stmt = $db->prepare("UPDATE tblorderhdr SET completed = ? WHERE orderid = ?");
I don't know either why, but it works now.
It would appear that the problem lies within the connection to the database. Here is the (relevant bit of the) updated code:
$db = $this->conn();
$stmt = $db->prepare("UPDATE tblorderhdr SET completed = ? WHERE orderid = ?");
$stmt->bind_param('ii', $this->proc, $this->pid);
$stmt->execute();
When you call $this->conn(), you are creating a new connection object of class mysqli. When no more variables point to the object, PHP will trigger its destructor. The destructor for mysqli will close the connection. This means that if you do not save the return value of this method into a variable, there will be no more references pointing to the object and the connection will be closed.
To fix this, simply save the object and reuse it. Don't connect each time.
$conn = $this->conn();
$stmt = $conn->prepare("UPDATE tblorderhdr SET completed = ? WHERE orderid = ?");
On a side note, creating a class like the one you have db is absolutely pointless. This class doesn't do anything useful. You haven't added any extra functionality to mysqli. You never need to save the credentials in properties. The whole class can be replaced with this code:
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli('localhost', 'user', 'password', 'test');
$mysqli->set_charset('utf8mb4'); // always set the charset
Then your classes will expect the mysqli object as a dependency.
class updOrders {
public $pid;
public $proc;
public function __construct(mysqli $conn) {
}

Error SQL Syntax

hi im rather new to SQL and im currently trying to save some data into a Sql database using a website. But everytime i run it i get some Syntax error and now after many hours of looking i was hoping somebody in here knew an answer, or could lead me in the right direction :)
this is the error i get when i hit my submit button :
0You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '[Campus one],[101],[2016-12-08],[test])' at line 1
this is the insert part of the script, connection part seems to be running fine
$sql = "INSERT INTO `formel`(`Område`, `Værelse`, `Dato`, `TV Fjernsyn Forlænger`) VALUES ([$area],[$filnavn],[$dato],[$Fjernsyn]) ";
SQL information:
Your values need to be changed from:
[$area],[$filnavn],[$dato],[$Fjernsyn]
to:
'{$area}','{$filnavn}','{$dato}','{$Fjernsyn}'
You should really be binding your params though. I assume you're using mysql_ which you shouldn't be using anymore therefore I also suggest you look into mysqli_ or PDO.
Edit:
You should move over to PDO in fairness, I did and I love it!
I work with classes a lot as it's a neater way to work so I'll give you my input:
Make yourself a dbConfig.php file:
class Database
{
private $host = "localhost";
private $db_name = "dbName";
private $username = "username";
private $password = "password";
public $conn;
public function dbConnection()
{
$this->conn = null;
try
{
$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $exception)
{
echo "Connection error: " . $exception->getMessage();
}
return $this->conn;
}
}
So because i am half lazy and why type code over and over?
Create yourself a dbCommon.php file.
class DBCommon
{
private $conn;
/** #var Common */
public $common;
public function __construct()
{
$database = new Database();
$db = $database->dbConnection();
$this->conn = $db;
}
public function runQuery($sql)
{
$stmt = $this->conn->prepare($sql);
return $stmt;
}
}
Then you need a class file for your PDO to go into such as:
class.update.php:
require_once('dbCommon.php');
class Update extends DBCommon
{
function __construct()
{
parent::__construct();
}
public function updateCode($area, $filnavn, $dato, $Fjernsyn)
{
$stmt = $this->runQuery("INSERT INTO `formel` (`Område`, `Værelse`, `Dato`, `TV Fjernsyn Forlænger`) VALUES (:area, :filnavn, :dato, :Fjernsyn)");
$stmt->bindParam(array(':area' => $area, ':filnavn' => $filnavn, ':dato' => $dato, ':Fjernsyn' => $Fjernsyn));
$stmt->execute();
echo "Your insert command has been completed!";
}
}
Then within your file.php where you would be calling the class you need to do:
require_once ('class.update.php');
$update = new Update();
if (isset($_POST['send']))
{
$update->updateCode($_POST['area'], $_POST['filnavn'], $_POST['dato'], $_POST['Fjernsyn']);
}
Note: Because you didn't post your $_POST['name']'s in I have given you a base example.
Binding your params is best practice as this prevents SQL Injection.
With binding params you dont have to run $stmt->bindParam(); You can simply run $stmt->execute(array(':paramshere' => $paramVar));
It totally depends on your preference but I always prefer binding before executing (personal preference). I hope this gives you some input and insight on how to move into PDO instead but it really is the way forward.
In order to prevent SQL injection, you should really use Prepared Statements, or correctly escape your strings. Please look at MySQLi or PDO.
Here's a basic tutorial using PDO and Prepared Statements inside of PHP:
// Connect to the database:
$db = new PDO( 'mysql:dbname=DB_NAME;host=localhost', 'DB_USER', 'DB_PASS' );
// Prepare the statement:
$ins = $db->prepare( 'INSERT INTO `formel` (`Område`, `Værelse`, `Dato`, `TV Fjernsyn Forlænger`) VALUES (:area, :filnavn, :dato, :Fjernsyn' );
// Execute with bindings:
$ins->execute(array(
':area' => $area,
':filnavn' => $filnavn,
':dato' => $dato,
':Fjernsyn' => $Fjernsyn
));
Try This,
$sql = "INSERT INTO `formel`(`Område`, `Værelse`, `Dato`, `TV Fjernsyn Forlænger`) VALUES ('$area','$filnavn','$dato','$Fjernsyn') ";
I think it should work.
Change your query to this
$sql = "INSERT INTO formel(Område, Værelse, Dato, TV Fjernsyn Forlænger) VALUES ('$area','$filnavn', '$dato', '$Fjernsyn') ";
But I would suggest you use prepared statement for security purposes

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.

PHP PDO config file include - global

I am making a transfer from using mysql query lines to PDO for a current project and i have an issue.
For this task i am not allowed to use any classes (stupid restriction if you ask me)
Basically i was getting a non object error because my main php file could not see the set variable $DBH.
I solved this problem by setting each function with a $DBH global; so it could be used, however ive been told this is bad coding practice. Is this the case? and if so how can i make my function see my config variable.
Config.php
try
{
$DBH = new PDO("mysql:host=host;dbname=db", "username", "Password");
$DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch (PDOException $e){
echo $e->getMessage();
}
a php file
function concName($concID)
{
global $DBH; //THIS is the area that im told is bad practice - can this be eliminated?
$stmt = $DBH->prepare("SELECT CONCAT(`Firstname`, ' ', `Surname`) AS 'Membername' FROM `members` WHERE `MemberID`= :MemberID");
$stmt->bindValue(":MemberID",$concID);
$stmt->execute();
while($row = $stmt->fetch())
{
return $row['Membername'];
}
}
Just pass $DBH as a parameter to any function that needs it:
function concName($concID, $DBH)
{
$stmt = $DBH->prepare("SELECT CONCAT(`Firstname`, ' ', `Surname`) AS 'Membername' FROM `members` WHERE `MemberID`= :MemberID");
$stmt->bindValue(":MemberID",$concID);
$stmt->execute();
while($row = $stmt->fetch())
{
return $row['Membername'];
}
}
Rather than the global keyword, you can also access it from the $GLOBALS[] array, which is more explicit about the variable's origins when used in the function. Passing a parameter is still preferable to this though.
function concName($concID)
{
// Better than `global` keyword, use `$GLOBALS['DBH']` every time you access it in outside global scope
// Still not preferred to passing a parameter though.
$stmt = $GLOBALS['DBH']->prepare("SELECT CONCAT(`Firstname`, ' ', `Surname`) AS 'Membername' FROM `members` WHERE `MemberID`= :MemberID");
$stmt->bindValue(":MemberID",$concID);
$stmt->execute();
while($row = $stmt->fetch())
{
return $row['Membername'];
}
}
If you have multiple globals defined in your configuration file, you can wrap them all in an array which you pass into functions needing them. That wraps them tidily into a package of config options made available to any function that needs them.
config.php
// Global array of config options
$config = array();
// various options
$config['option1'] = 'option 1';
$config['option2'] = 12345;
try
{
$config['DBH'] = new PDO("mysql:host=host;dbname=db", "username", "Password");
$config['DBH']->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch (PDOException $e){
echo $e->getMessage();
}
Then pass $config to function calls

Categories