Problem:
I have read quite a number of articles and two books about PDO but I do not seem to find the answer to my question. The question is whether there is a way to include the database connection to PDO as a require_once() and still be able to use prepared statements without a try/catch block?
I currently have a file called settings.php containing the following code.
Code (settings.php):
<?php
// Declaration of database connection information
$settings = [
'host' => '127.0.0.1',
'name' => 'c9',
'port' => '3306',
'charset' => 'utf8',
'username' => 'admin',
'password' => 'root'
];
?>
I put this file outside the document root and include it with require_once() to the actual database connection file.
Code (db.php):
<?php
// Includes database connection information
require_once('../settings.php');
// Connects to a MySQL database
try {
$dbh = new PDO(
sprintf(
'mysql:host=%s;dbname=%s;port=%s;charset=%s',
$settings['host'],
$settings['name'],
$settings['port'],
$settings['charset']
),
$settings['username'],
$settings['password']
);
// Prevents PDO to use emulated prepares and activates error
// mode PDO::ERRMODE_EXCEPTION
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
// Catches errors raised by PDO
catch (PDOException $e) {
// Prints out errors to text file
file_put_contents('errors.txt', $e->getMessage(), FILE_APPEND);
// Shows generic error message to user
header('Location: 404.php');
exit;
}
?>
Questions:
Since I have the file db.php is it possible to include this in other files for prepared statements? Can prepared statements also be in try/catch block despite that the connection is in one? Appreciate any comments of how to include SELECT / INSERT / UPDATE / DELETE to the aforementioned code.
You've got a lot of unnecessary code there.
1) The try/catch is redundant because PHP will log your errors to the error log by default anyway. Besides, any thing that's going to be helpful will come from the execute() call.
2) Yes you can certainly include your database credentials wherever you want, but keep in mind, require_once() is expensive so make it count. You might consider including the whole block of code on all your other pages instead of just including the credentials and then rewriting the other code.
3) And example of a select statement from your above code would be:
$stmt = $dbh->prepare("SELECT * FROM `table` WHERE `id` = :id");
$stmt->execute(array(":id"=>$id));
$results = $stmt->fetch(PDO::FETCH_ASSOC);
Your question seems to be quite unclear, consists of highly irrelevant parts. Say, there is no connection between prepared statements and a try-catch block, both has nothing to do with include. And there is no particular guide on including SELECT / INSERT / UPDATE / DELETE queries in the code - you just run them wherever you wish.
I can make only some notes, hoping it will clear some of your confusions.
First of all, read my article on PDO. It is still incomplete but it may help you to sort things out.
Now to your questions.
whether there is a way to include the database connection to PDO as a require_once() and still be able to use prepared statements without a try/catch block?
Yes. In most cases you don't need a try catch block at all. However, every time you really need it, you can use a try catch block with no problem.
is it possible to include this in other files for prepared statements?
Yes. That's what include operator is for.
Can prepared statements also be in try/catch block despite that the connection is in one?
Yes. You can have as many such blocks in your code, as you need. However, in most cases you don't need them at all.
how to include SELECT / INSERT / UPDATE / DELETE to the aforementioned code.
There is no point in including these queries there. Just write them after the line where you included your db.php
Related
I am converting PHP code from mysql_ to mysqli_. My DB connection is in a separate file, named "conn.inc" in a parent folder of my regular code. The code in it is ::
function GetDBConn($host="localhost", $user="mydb", $pass="mypass", $db="mydb") {
return $dbconn = #mysqli_connect($host, $user, $pass, $db);
mysqli_close($dbconn);
}
In my code files, I have
include_once ("../conn.inc"); .
I have code like -
$AuditInsertQ = mysqli_query(GetDBConn(),"INSERT INTO audit (userid, notes) VALUES (\"".$userid."\", \"".$notes."\")") or die("Error inserting row to Audit: ".mysqli_error($dbconn));
When I run the code, I get a message that ::
PHP Notice: Undefined variable: dbconn in C:\...
All of the examples I have seen have the DB connection in the same file as the code it was referencing. How do I reference the DB connection when it is in a different file; I thought the "include_once" was the way...?
There is so much wrong in this code I don't even know where to start.
no need for mysqli_error() at all
no need for a function like GetDBConn()
mysqli_close() right after connect makes no sense. Thanks to return operator, it never gets called though
die() is harmful
# operator is harmful
the file extension for conn.inc is harmful
the way you are adding variables to your query is most harmful of them all
I know it's hard to find a good tutorial. Internet is full of crappy outdated information. I am writing good tutorials, but Google don't know they are good and don't show them to you. Well at least I can give it to you here in my answer.
Three things you must understand about modern mysqli
mysqli can report its errors automatically, no need for mysqli_error()
a connection must be made only once, it means there is no use for a function like this
no variable should be added to the query directly. You have to use prepared statements with placeholders for the purpose.
In order to fix your code,
please read this post. It doesn't explain why you should use prepared statements but take my word for it
then read my tutorial on how to connect with mysqi properly
rename your file to conn.php or anyone will be able to see your database credentals
Then rewrite your code to
include_once ("../conn.php");
$stmt = $mysqli->prepare("INSERT INTO audit (userid, notes) VALUES (?,?)");
$stmt->bind_param("ss", $userid,$notes);
$stmt->execute();
For the explanation on what is going on in this code please see my tutorial on how to run an INSERT query with mysqli
In you function GetDBConn it return a mysql resource, not define a $dbconn variable for you.
Use something like mysqli_query($dbconn = GetDBConn(),"INSERT INTO audit ....
Note
function GetDBConn($host="localhost", $user="mydb", $pass="mypass", $db="mydb") {
return $dbconn = #mysqli_connect($host, $user, $pass, $db);
mysqli_close($dbconn);
}
$dbconn is only avaliable in the function, it cannot be accessed outside the function, so define it here is useless.
mysqli_close($dbconn); will never reached.
I have my db connection parameters set in a single file which I include on all pages I need it. Connection files looks like so... called connect.php :
$db_host = '111.111.111.111';
$db_database = 'test';
$db_user = 'test';
$db_pass = 'test';
$db_port = '3306';
//db connection
try {
$db = new PDO("mysql:host=$db_host;port=$db_port;dbname=$db_database;charset=utf8", $db_user, $db_pass,
array(
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, //PDO::ERRMODE_SILENT is default setting
PDO::ATTR_PERSISTENT => false //when true
)
);
}
catch(PDOException $e) {
error_log("Failed to connect to database (/connect.php): ".$e->getMessage());
}
When I need to do things with the db I include this file and end up with something like this... called example.php :
require $_SERVER['DOCUMENT_ROOT'].'/assets/functions/connect.php';
$stmt = $db->prepare("
SELECT
accounts.account_id,
FROM accounts
WHERE accounts.account_key = :account_key
");
//bindings
$binding = array(
'account_key' => $_POST['account_key']
);
$stmt->execute($binding);
//result (can only be one or none)
$result = $stmt->fetch(PDO::FETCH_ASSOC);
//if result
if($result)
{
// result found so do something
}
Occasionally the database connection will fail (updating, I shut it down, its being hammered, whatever)... when that happens the PDOException I have in the try/catch works as it should and adds an entry into my error log saying so.
What I would also like to do is add a 'check' in my example.php so it doesn't attempt to do any database work if there is no connection (the include file with my connect script failed to get a connection). How would I go about this and what is the preferred method of doing so?
I'm not sure of the correct way to 'test' $db before my $stmt entry. Would there be a way to retry the connection if it was not set?
I realize I can leave it as it and there would be no problems, other than the database query fails and the code doesn't execute, but I want to have more options like adding another entry to the error log when this happens.
To stop further processings just add an exit() at the end of each catch block, unless you want to apply a finally block.
try {
//...
} catch(PDOException $e) {
// Display a message and log the exception.
exit();
}
Also, throwing exceptions and true/false/null validations must be applied through the whole connect/prepare/fetch/close operations involving data access. You may want to see a post of mine:
Applying PDO prepared statements and exception handling
Your idea with including db connection file I find good, too. But think about using require_once, so that a db connection is created only once, not on any include.
Note: In my example I implemented a solution which - somehow - emulates the fact that all exceptions/errors should be handled only on the entry point of an application. So it's more directed toward the MVC concept, where all user requests are sent through a single file: index.php. In this file should almost all try-catch situations be handled (log and display). Inside other pages exceptions would then be thwrown and rethrown to the higher levels, until they reach the entry point, e.g index.php.
As for reconnecting to db, How it should be correlated with try-catch I don't know yet. But anyway it should imply a max-3-steps-iteration.
I received max_user_connections error this days. and I was wondering if I am doing something wrong.
I have a config.php file with mysqli connection script:
$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'my_db');
so pages where I need to get something in mysqli I include config.php. here is an example:
example.php
<?php
include_once("config.php");
$stmt = $mysqli->prepare("select...");
$stmt->execute();
$stmt->bind_result(...,...);
while($stmt->fetch()) {
...
}
$stmt->close();
?>
some html <p> <img>...
<?php
$stmt = $mysqli->prepare("select...");
$stmt->execute();
$stmt->bind_result(...,...);
while($stmt->fetch()) {
...
}
$stmt->close();
?>
some html <p> <img>...
<?php
$stmt = $mysqli->prepare("select...");
$stmt->execute();
$stmt->bind_result(...,...);
while($stmt->fetch()) {
...
}
$stmt->close();
?>
So, my question is: is it the best practise to do selects like this? should I close mysqli connect after each select and open again? or do selects on the top together without separete than with some html in the middle?
the best practise to do selects like this?
I hate it when people use the term "best practice" it's usually a good indicator they don't know what they are talking about.
The advantage of your approach is that its nice and simple. But as you've discovered, it does not scale well.
In practice its quite hard to measure, but in most applications the MySQL connection is unused for most of the lifecycle of the script. Hence there are usually big wins to be made by delaying the opening of connection and closing it as soon as possible.
The former can be done by decorating the mysqli class, the connect method just stores the credentials while anything which needs to talk to the database should call the wrapped connect method when it needs access to the database. However typically the yeild of this approach is low unless your code creates a lot of database connections which are never used (in which case a cheaper solution would be to increase the connection limit).
It can take a long time after the last query is run before the connection is closed down. Explicitly closing the connection addresses this, but requires a lot of code changes.
do not open and close a connection for each query. Although it will result in a reduced number of connections to the databasee, the performance will suck
The biggest win usually comes from optimizing your queries - reduced concurrency and a better user experience.
I am trying to have the PHP code update an address in user table.
For starters, using mysqli, and tried both prepared statements as well as simpler queries. Never had much luck with prepared statements ever because I find them confusing, particularly bind_result().
I do use mysql testing at the command itself to make sure it works as it should. Updates as it should so it's not the mysql command itself. I even gave it a shot in phpMyAdmin locally on the server. However, once in PHP, it doesn't update data in the table.
Immediate thought that came to mind was to make sure the 'user' accessing the mysql tables had UPDATE rights, and it does. So it doesn't look like a permission issue. Even when I use the mysql root with all rights and privileges, the table will NOT update.
My original attempt was some thing quite simple:
$query = "UPDATE `UserTable` SET `Address`=\"". $address . "\" WHERE `id`=".$id;
$conn->query($query);
So, I tried prepared statement version of this and had the same effect. No error, but nothing changed in my table.
Today, I decided to go the PDO route.
try {
$dbh = new PDO("mysql:host=$hostname; dbname=DBDatabase", $db_user, $db_pass);
$query = "UPDATE UserTable SET 'Address'='".$address."' WHERE 'id'=".$id;
echo "Query: ". $query;
$count = $dbh->exec($query);
echo $count . " record changed.";
$dbh = null;
}
catch (PDOException $e) {
echo $e->getMessage();
}
I also tried changing other fields (maybe it was just happening to VARCHAR fields like address). So, I tried flipping the Registered flag and no changes register for that either.
You're not using PDO properly. This is how you will want to form your query.
try {
$dbh = new PDO('mysql:host=$hostname; dbname=DBDatabase', $db_user, $db_pass);
$stmt = $dbh->prepare('UPDATE UserTable SET `Address`=:address WHERE `id`=:id');
$stmt->bindParam(':address', $address, PDO::PARAM_STR);
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->execute();
}
catch (PDOException $e) {
echo $e->getMessage();
}
This is not your direct question, but prepared statements are super easy, they just require a learning curve - which, honestly, from personal experience is no steeper than learning PDO.
Traditional Query Steps:
DB query.
DB result.
Prepared Query:
Send DB query without parameters, to prepare.
Insert parameters.
Connect to results (bind a variable for each return column, in order).
Cycle through results, using variables rather than a $result array.
It is a few extra steps, but take the twenty minutes you need to to conceptualize it and it will all snap together. The advantage to PDO is that it works across different databases and it is great at prepared statements.
I'm a beginner programmer, and I've installed XAMPP with the intention of learning a bit of PHP. I have a working knowledge of SQL.
I've been following the PHP tutorial at w3schools. The problem I am currently having is this: I'm using the script here to create a database and a table within it. What I'm using is almost verbatim, except I've replaced the user with "root" and I've deleted the password.
After running the script through the browser, the database my_db appears in the datafile for mysql in XAMPP.
However, there is no sign of a table, and when I try to select the table, I get
Table 'my_db.persons' doesn't exist
What is going on? Is there something wrong with the code I took verbatim, or is it something with permissions?
It's weird that the database is created but not a table...
What has happenned is that your "CREATE TABLE" will have failed.
Get into the habit of checking for errors after EVERY mySQL query (in your code): there are some times you can ignore errors, but it's rare. So program alonghte lines of:
Create query: $sql =
Set parameters: $aParams = (or bind paramters)
Execute query
If errors
If debug: Show error and query
If live: Log error and query
I'm not giving the solution is code, as one problem with the tutorial you follow is that it uses mysql_() functions that are going to be depreciated shortly. You should use PDO (PHP Database Objects) or mysqli() functions otherwise your code will not work in a few releases time.
With PDO, you can set error handling to use exceptions, and you wrap every call in try {} catch {} and this makes the habit of catching and reporting errors very easy.
$sql = 'CREATE TABLE....';
$aParams = array(
':param_name' => $param_value,
':param_name2' => $param_value2
);
try {
$stmnt = $db->prepare($sql);
$stmnt->execute($aParams);
$stmnt = null;
} catch (Exception $e) {
// Error log here; $e contins line of error and the actual error, you have $sql and $aParams
LogDBError($e, $sql, $aParams);
}