This question already has answers here:
Reference: What is variable scope, which variables are accessible from where and what are "undefined variable" errors?
(3 answers)
Closed 2 years ago.
I am trying to create a PHP function to update the SQL table to keep from reusing code over and over. However, whenever I run the function, I always get 'SQL database error.' The odd part about this is I do not get any type of error whenever I use copy/paste this snippet of code and put it by itself, not as a function. What is wrong? Why is it behaving this way?
$servername = "localhost";
$dBUsername = "username";
$dBPassword = "password";
$dBName = "databasename";
$connection = mysqli_connect($servername, $dBUsername, $dBPassword, $dBName);
function updateSQL ($table, $row, $value, $where, $arg) {
$SQL = '
UPDATE `".$table."`
SET `".$row."`=?
WHERE `".$where."`=?';
$stmt = mysqli_stmt_init($connection);
if (!mysqli_stmt_prepare($stmt, $SQL)) {return "SQL database error."; exit();}
else {
mysqli_stmt_bind_param($stmt, "ss", $value, $arg);
if (mysqli_stmt_execute($stmt)) {return "success";}
else {return mysqli_error($connection);}
}
}
The $connection is not set in this scope (unless you're using it as a global variable, which we can't see in the code and it would be a bad practice anyway).
If the updateSQL() is a method of a object, you can set $this->connection property in the constructor (or any other suitable place) and then access it in the updateSQL() method.
Otherwise, pass it as an argument to the updateSQL() function the same way as you're passing the $table, $row and other arguments.
Related
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 ***/
}
This question already has answers here:
Why does this PDO statement silently fail?
(2 answers)
Closed 5 years ago.
I'm currently doing a populating/editing my DB using PHP on a webpage I'm going to build. I'm relatively new to PHP so please excuse my "greenhorn" knowledge. My DB connection works, however, I don't understand why my function "displaySQL" and then counting the quantity as an array is not generating anything on my webpage. There's no number displayed on the screen, I was going to have that value put inside an HTML table afterwards. My DB settings are also established for the parameter part (host, username, password). Thanks for any help.
class User{
var $conn;
function __construct($hostname,$username,$password){
try{
$conn = new PDO("mysql:host=$hostname;dbname=pjj5",
$username, $password);
echo "Connected successfully <br>";
}
catch(PDOException $e)
{
echo "Connection failed: " . $e->getMessage();
}
}
//display all users information
function displaySQL(){
$sql = "SELECT * FROM accounts";
$q = $conn->prepare($sql);
$q->execute();
$results = $q->fetchAll();
return $results;
}
$db = new User($hostname,$username,$password);
$res = $db->displaySQL();
echo count($res);
To access a variable of an object within the object you need to use the syntax $this->.
This line:
$conn = new PDO("mysql:host=$hostname;dbname=pjj5",
$username, $password);
and this line
$q = $conn->prepare($sql);
should have $this->conn instead of $conn. In the first function you create the connection to a new $conn variable that is local in scope to the function only, which gets discarded after the object is constructed. For the function displaySQL() $conn is undefined.
This question already has answers here:
Reference: What is variable scope, which variables are accessible from where and what are "undefined variable" errors?
(3 answers)
Closed 7 years ago.
connect_db.php:
<?php
$servername = "1.1.1.1";
$username = "root";
$password = "nope, not making this public :P";
$dbname = "seminarfach";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
//$conn = new mysqli(null, $username, $password, $dbname, null, '/cloudsql/seminarfach-abi-links:data');
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
echo "<p>Connected successfully</p>";
// Set charset to UTF-8
if (!$conn->set_charset("utf8")) {
printf("Error loading character set utf8: %s\n", $conn->error);
exit();
}
function TuckTheTorld() {
$conn->close();
}
?>
This is the file that connects to my database, and it is called with require "connect_db.php"; in the file that needs a connection. Now I wanted to create a function that is called to close the connection to the DB. That is the TuckTheTorld function. I named it that to make sure I don't use any keywords or overwirte any other functions.
The problem is that when I call TuckTheTorld() I get the following error:
Fatal error: Call to a member function close() on a non-object in path_to_file\connect_db.php on line 26
Why do I get that error?
You need to import the $conn object into the functions namespace by using the global keyword (there is nothing called $conn in the functions namespace because $conn resides in the global files namespace).
Just replace your function with:
function TuckTheTorld() {
global $conn;
$conn->close();
}
This should now work for you.
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
I am trying to pass an mysqli database connection to a php class. The code I have so far (cut down for simplicity) is as follows:
db.php
$db_host = 'localhost';
$db_name = 'dbname';
$db_user = 'username';
$db_password = 'password';
$db = array('db_host'=>$db_host,
'db_name'=>$db_name,
'db_user'=>$db_user,
'db_password'=>$db_password);
$dbCon = new mysqli( $db['db_host'],
$db['db_user'],
$db['db_password'],
$db['db_name']);
if (mysqli_connect_errno())
{
die(mysqli_connect_error()); //There was an error. Print it out and die
}
index.php
<?php
require_once($_SERVER["DOCUMENT_ROOT"] . "/db.php");
$sql = "SELECT id FROM usr_clients";
$stmt = $dbCon->prepare( $sql );
if ($stmt)
{
$stmt->execute();
$stmt->bind_result($id);
while($stmt->fetch())
{
$cl = new Client($id, $dbCon);
$cl->doIt();
}
$stmt->close();
}
?>
client.php
<?php
Class Client
{
private $con;
public static $clientCount = 0;
public function __construct( $id, $con )
{
$this->con = $con;
$sql = "SELECT id FROM usr_clients WHERE id = $id";
$stmt = $this->con->prepare( $sql );
if ($stmt)
{
echo "it worked!";
}
else
{
echo "it failed";
}
}
}
?>
Now the index.php page successfully recognises the database connection declared in db.php, and returns a list of all clients. It then loops through each client, and creates a "client" object, passing it the database connection.
It is here that the problem seems to start. In the client class, the database connection is not recognised. I get multiple errors on the page saying "it failed". In the logs, there is a line about calling prepare() on a non object.
Can anyone explain why the connection works in index.php, but not in the client class?
Thanks
Your main problem is assumptions.
You are assuming that there is no connection passed, judging by indirect consequence.
But a programmer should be always logically correct in their reasoning.
Talking of connection? Verify the very connection. var_dump($con) in the constructor. var_dump($this->con) in the method. If it fails - only now you can blame connection and start for the solution.
If not - there is no reason in looking for another connection passing method. Yet it's time to find the real problem.
If your query fails, you have to ask mysql, what's going on, using $this->con->error, as this function will provide you with a lot more useful information than simple "it fails". The right usage I've explained here: https://stackoverflow.com/a/15447204/285587