I'm new to PDO and I wanna do it right from the beginning - I'm going to replace my old mysql_ functions on a site.
Have I got it right?:
Should I put the connection code in a try/catch and save it to a file, and include it on top of the page. Then put the queries in try/catch as well.
Or:
Should I put the connection code in a file and include it in the in the top of the try/catch statement above the query?
ver1:
include('pdo.php'); // try/catch in file
try {
$stmt = $conn->prepare('SELECT * FROM myTable WHERE id = :id');
$stmt->execute(array('id' => $id));
while($row = $stmt->fetch()) {
print_r($row);
}
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
ver2:
try {
include('pdo.php'); // no try/catch in file
$stmt = $conn->prepare('SELECT * FROM myTable WHERE id = :id');
$stmt->execute(array('id' => $id));
while($row = $stmt->fetch()) {
print_r($row);
}
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
Or should I put the try/catch in both places?
You should do both and even more :
Set an error handler that displays a message to the user and stops the PHP (see set_error_handler)
When you initiate the connexion, add a try/catch and throw an error if you catch an exception. Your website won't run without database connection i guess.
Do a try/catch around your queries to recover gracefully from error (when you can)
Related
I am creating a recipe's database and after adding 'add' and 'delete' functionality, the system throws an error:
Notice: Undefined variable: pdo in C:\xampp\htdocs\COMP1321\recipes\index.php on line 52
Fatal error: Uncaught Error: Call to a member function query() on null in C:\xampp\htdocs\COMP1321\recipes\index.php:52 Stack trace: #0 {main} thrown in C:\xampp\htdocs\COMP1321\recipes\index.php on line 52
Index.php
try // selection block
{
$sql = 'SELECT * FROM recipe';
$result = $pdo->query($sql);
}
Databaseconnection.inc.php
<?php
try
{
//new PDO('mysql:host=mysql.cms.gre.ac.uk; dbname=mdb_', '', '');
$pdo = new PDO('mysql:host=localhost; dbname=mdb_recipes', 'root', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec('SET NAMES "utf8"');
} catch (PDOException $e) {
$error = 'Unable to connect to database server';
include 'error.html.php';
exit();
}
I have been trying to solve this problem for 3 hours already. Some help would be appreciated!
Full Index.php
<?php
if(isset($_GET['addrecipe'])) {
include 'form.html.php';
exit();
}
//insert block
if (isset($_POST['recipename'])) {
include 'admin/includes/db.inc.php';
try
{
//prepared statement
$sql = 'INSERT INTO recipe SET
recipename = :recipename,
recipedate = CURDATE()';
$s = $pdo->prepare($sql);
$s->bindValue(':recipename', $_POST['recipename']);
$s->execute();
} catch (PDOException $e) {
$error = 'Error adding submitted recipe' . $e->getMessage();
include 'error.html.php';
exit();
}
header('Location: .');
exit();
}
//delete block
if(isset($_GET['deleterecipe']))
{
include '../includes/db.inc.php';
try
{
$sql = 'DELETE FROM recipe WHERE id = :id';
$s = $pdo->prepare($sql);
$s->bindValue(':id', $_POST['id']);
$s->execute();
}
catch (PDOException $e)
{
$error = 'Error deleting recipe' . $e->getMessage();
include 'error.html.php';
exit();
}
header('Location: .');
exit();
}
try // selection block
{
$sql = 'SELECT * FROM recipe';
$result = $pdo->query($sql);
}
catch (PDOException $e) {
$error = 'Error fetching recipes' . $e->getMessage();
include 'error.html.php';
exit();
}
foreach ($result as $row) {
$recipes[] = array(
'id' => $row['id'],
'recipename' => $row['recipename'],
'time' => $row['time'],
'ingredients' => $row['ingredients'],
'recipetext' => $row['recipetext'],
'servings' => $row['servings'],
'nutritionfacts' => $row['nutritionfacts'],
'recipedate' => $row['recipedate'],
'image' => $row['image'],
);
}
include 'recipes.html.php';
Looks like you never include the database connection file.
If $_POST['recipename'] is not set, and $_GET['deleterecipe'] is not set, then when you get to that line, db.inc.php has never been included, so $pdo is not defined.
You need to move your include 'admin/includes/db.inc.php'; line to be right below the form.html.php include.
This will make it available for the entire page as it is required regardless of whether or not you are inserting or deleting a recipe since you need it for the selection block at the bottom.
You only need to include the file once per page and I would normally use require_once() at the top of the file for the DB and other global dependencies like this, rather than trying to include it each place you need it 3+ times per file and then end up unnecessarily including the file and possibly generating warnings or errors. Generally it just makes your code more complicated. It's a shared resource, let it be shared. Or create a singleton to creates or retrieve the PDO instance for you in a standard way so you only have 1 PDO connection instead of potentially multiple per request.
I am having problem with transactions in php script. I would like to make multiply queries and be able to recall them all, if at least one of them fails. Below you can find a simple example of the script I am using:
$tags_input = array(6,4,5);
$conn = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8',
DB_USER, DB_PASSW, array(
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));
$conn->beginTransaction();
$sql = "INSERT INTO projects (id, pr_id, enabled) VALUES ( :val0, :val1, :val2)";
$stmt = $conn->prepare($sql);
if(count($tags_input)>0){
for($i = 0;$i<count($tags_input);$i++){
$stmt->bindValue(':val0', 57);
$stmt->bindValue(':val1', $tags_input[$i]);
$stmt->bindValue(':val2', 'Y');
$result = $stmt->execute();
}
}
$res1 = $conn->commit();
$conn->rollBack();
Now, this example generates an error:
Uncaught exception 'PDOException' with message 'There is no active
transaction'
If I erase the line $conn->rollBack();, the error disappears. Therefore I cannot understand, why pdo object can't see open transaction (begintransaction and commit do not generate any errors). I also tried putting rollBack() inside the transaction, but made no difference. I was still getting an error 'There is no active transaction'.
I am running PHP 5.6 and Mysql tables on InnoDB.
Wrap your transaction code inside a try-catch statement.
//try {
$tags_input = array(6,4,5);
$conn = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8',
DB_USER, DB_PASSW, array(
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));
} catch (Exception $e) {
die("Unable to connect: " . $e->getMessage());
}
try {
$conn->beginTransaction();
$sql = "INSERT INTO projects (id, pr_id, enabled) VALUES ( :val0, :val1, :val2)";
$stmt = $conn->prepare($sql);
if(count($tags_input)>0){
for($i = 0;$i<count($tags_input);$i++){
$stmt->bindValue(':val0', 57);
$stmt->bindValue(':val1', $tags_input[$i]);
$stmt->bindValue(':val2', 'Y');
$result = $stmt->execute();
}
}
$res1 = $conn->commit();
} catch (Exception $e) {
$conn->rollBack();
echo "Failed: " . $e->getMessage();
}
EDIT
A really well-based and straight-forward explanation of the answer was provided by Richard as a comment.
The reason you got error is because you were trying to close a transaction when it was already closed. beginTransaction opens one, and EITHER rollBack OR commit closes it. You have to avoid doing BOTH actions, meaning commit/rollback, for a single beginTransaction statement, or you'll get an error. The above try/catch code ensures that only one closing statement is executed.
Peter and Richards answers are already correct, but there is one little mistake in the code from the transaction structure (and i can't add a comment).
The $connection->beginTransaction() must be outside of the try-catch block. When you're start the beginTransaction() in the try-block and your Database Operations throws an exception, the catch-block doesn't know something from an active transaction. So, you get the same error:
"There is no active transaction".
So the structure should be as well:
Get the Connection.
Start the Transaction with $connection->beginTransaction()
Open the try-catch block.
The try-block contains the $connection->commit() after DB Operations.
The catch-block contains the $connection->rollback() before a throw Exception.
So your code should look like this:
$tags_input = array(6,4,5);
$conn = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8',
DB_USER, DB_PASSW, array(
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));
} catch (Exception $e) {
die("Unable to connect: " . $e->getMessage());
}
//Begin Transaction
$conn->beginTransaction();
try {
$sql = "INSERT INTO projects (id, pr_id, enabled) VALUES ( :val0, :val1, :val2)";
$stmt = $conn->prepare($sql);
if(count($tags_input)>0){
for($i = 0;$i<count($tags_input);$i++){
$stmt->bindValue(':val0', 57);
$stmt->bindValue(':val1', $tags_input[$i]);
$stmt->bindValue(':val2', 'Y');
$result = $stmt->execute();
}
}
$res1 = $conn->commit();
} catch (Exception $e) {
$conn->rollBack();
echo "Failed: " . $e->getMessage();
}
I've got a website that is pulling data from my MSSQL Server. I am using functions to build tables for reports. Here's what I've got:
function BeginTable($rowCount,$headings,$searchValue,$ReportName,$OneButton,$NewSearch)
{
try{
$StateSelectSQL = "select distinct State from pmdb.MaterialTracking where State is not null";
var_dump($StateSelectSQL);echo " What!<br>";
$getSelect = $conn->query($StateSelectSQL);
var_dump($getSelect);echo " When!<br>";
$StateSelectNames = $getSelect->fetchALL(PDO::FETCH_ASSOC);
var_dump($StateSelectNames);echo " Where!<br>";
}
catch(Exception $e)
{
echo "Something went wrong";
die(print_r($e->getMessage()));
}
I tried this too:
try{
$StateSelectSQL = "select distinct State from pmdb.MaterialTracking where State is not null";
var_dump($StateSelectSQL);echo " What!<br>";
$getSelect = $conn->prepare($StateSelectSQL);
$getSelect->execute();
//$getSelect = $conn->query($StateSelectSQL);
//var_dump($getSelect);echo " When!<br>";
$StateSelectNames = $getSelect->fetchALL(PDO::FETCH_ASSOC);
var_dump($StateSelectNames);echo " Where!<br>";
}
catch(Exception $e)
{
echo "Something went wrong<br>";
die( print_r( $e->getMessage()));
}
The second and third var_dump's never show anything and the rest of the code (not shown here) doesn't get run. If I comment out the $getSelect and $StateSelectNames lines (with the var_dump's under them) then everything else works.
Here is my DBConn.php file that is included above the Function:
$conn = new PDO("sqlsrv:server=$servername;database=$dbname", $username,$password);
//set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$conn->setAttribute(PDO::SQLSRV_ATTR_QUERY_TIMEOUT, 10);
What is wrong with the line $getSelect = $conn->query($StateSelectSQL); I can't figure it out. I tried using it later in my foreach like this:
foreach($conn->query($StateSelectSQL) as $StateName)
But that doesn't work either. It again stops at this line and doesn't go any further. The only thing I can think of is that my SQL is messed up, but when I run it in SSMS it works fine!
What's going on?
Try preparing and executing your SQL before using fetchAll. Also consider enabling exception mode if you haven't already and wrapping your statement in a try catch - this should flag any issues (e.g. your applications database user not having permission to access the schema, or syntax error etc)
Exceptions:
See this stack overflow post for info about how to enable
And for your code:
try {
$sql = "
SELECT DISTINCT State
FROM pmdb.MaterialTracking
WHERE State IS NOT NULL
";
$sth = $conn->prepare($sql);
$sth->execute();
$rowset = $sth->fetchAll(PDO::FETCH_ASSOC);
print_r($rowset);
} catch PDOException($err) {
echo "Something went wrong".
echo $err;
}
I have figured it out after pulling my hair out all day! I had to include my DBConn.php inside the function. After this it worked. I don't know why that mattered since it is included at the beginning of the file. If there is anyone who can explain why that is i'd be grateful!
It now looks like this:
function BeginTable($rowCount,$headings,$searchValue,$ReportName,$OneButton,$NewSearch)
{
try{
include("DBConn.php");
$SelectSQL = "select distinct State from pmdb.MaterialTracking where State is not null order by State";
$getSelect = $conn->prepare($SelectSQL);
$getSelect->execute();
$StateSelectNames = $getSelect->fetchALL(PDO::FETCH_ASSOC);
}
catch(Exception $e)
{
echo "Something went wrong<br>";
die( print_r( $e->getMessage()));
}
Making the switch to PDO from MySQL in PHP. In the past, when a query was ran and wasn't executed for whatever reason, I was able to send myself an email with the mysql_error() message in the body like so:
$query = "SELECT dogs FROM animals";
$res = mysql_query($query);
if (!$res) {
$error = "Error Message: " . mysql_error();
mail("my#email.com","Database Error",$error);
}
From that I would be alerted by an emil when something was wrong with the database on a website.
My PDO setup is as follows:
setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
I have a try catch in the PDO connection itself but I would like to get error messages for prepare and execute as they happen like so:
$query = "SELECT cats FROM animals";
$sql = $pdo->prepare($query);
if (!$sql->execute()) {
$error = "Error Message: " . (pdo error message here);
mail("my#email.com","Database Error",$error);
}
Any ideas on how to assign PDO error messages in an email subject? I'm able to get a logical error message if a prepare fails be using errorInfo() but on execute errors(such as invalid parameter counts for arrays), I can't seem to get an error message.
Thanks for any and all help.
Use try/catch
try {
$sql->execute();
} catch (PDOException $e) {
$e->getMessage(); // This function returns the error message
}
You can use:
$query = "SELECT cats FROM animals";
$sql = $pdo->prepare($query);
if (!$sql->execute()) {
$arr = $sql->errorInfo();
$error = print_r($arr, true);
mail("my#email.com","Database Error",$error);
}
I am trying to list data from two specific columns in a table. Whenever I go to the file, it returns a server error. When I remove the while loop, it executes perfectly, so I have no idea what I am doing wrong.
Here's the error:
Server error The website encountered an error while retrieving
http://dayzlistings.com/reg-whitelist.php. It may be down for
maintenance or configured incorrectly. Here are some suggestions:
Reload this webpage later. HTTP Error 500 (Internal Server Error): An
unexpected condition was encountered while the server was attempting
to fulfill the request.
try {
$dbh = new PDO('mysql:host=localhost;dbname=dbname','dbuser','dbpass');
$sql = "SELECT * from wp_cimy_uef_data";
$q = $dbh->prepar($sql);
$q->execute();
while($row = $q->fetchall() {
echo $row['USER_ID'];
echo $row['VALUE'];
}
}
$dbh = null;
} catch (PDOException $e) {
print "Error from Dedicated Database!: " . $e->getMessage() . "<br/>";
die();
}
500 means something wrong when you interact with the server, e.g. access db.
And $row['USER_ID'] will never work, instead you should use $row[0]['USER_ID'].
isn't fetchAll() returns the whole table as an array?..
you don't need to while loop $row.. just do $row = $q->fetchAll() without while and print_r the whole array and see what you get..
and if you still wanna do while I think you may use
while($row = $q->fetch()){
// rest of the code here
}
Also you cannot put annything between try catch..
try{
//code
}
$dbh = null; //**This is not allowed by the way...**
catch(PDOException $e){
//code
}
Dins