I'm facing some doubts in PHP database connections. Since I can't just put a large try/catch/finally block on my method (Java style), what's the best approach to properly closing all connections and prepared statements when size/logic tends to increase? Considering the next method, is everything done right?
public function createRegister($register) {
$this->openConnection();
$query = "INSERT INTO register (username, password, email, confirmationToken) VALUES (?, ?, ?, ?)";
$result = $this->mysqli->query($query);
if ($statement = $this->mysqli->prepare($query)) {
$statement->bind_param("ssss", $register->username, $register->passwordHash, $register->email, $register->confirmationToken);
if (!$statement->execute()) {
$this->closeConnection();
throw new DAOException("Failed to execute statement: " . $statement->error);
}
$statement->close();
} else {
$this->closeConnection();
throw new DAOException("Failed to prepare statement: " . $this->mysqli->error);
}
$this->closeConnection();
}
You can still use try/catch in PHP:
public function createRegister($register) {
$this->openConnection();
$query = "INSERT INTO register (username, password, email, confirmationToken) VALUES (?, ?, ?, ?)";
try {
// This line is not needed
// $result = $this->mysqli->query($query);
if ($statement = $this->mysqli->prepare($query)) {
$statement->bind_param("ssss", $register->username, $register->passwordHash, $register->email, $register->confirmationToken);
if (!$statement->execute()) {
throw new DAOException("Failed to execute statement: " . $statement->error);
}
$statement->close();
} else {
throw new DAOException("Failed to prepare statement: " . $this->mysqli->error);
}
} catch (Exception $e) {
if ((isset($statement)) && (is_callable(array($statement, 'close')))) {
$statment->close();
}
$this->closeConnection();
throw $e;
}
$this->closeConnection();
}
This works well for establishing a connection for one specific task, but what if you want to share the same connection for multiple tasks that also need access to the same schema? You may want to consider a more advanced solution using a singleton/factory pattern for creating and access database connections. I posted such an example as a solution to another question. It is a bit more advanced but once you get your head around it, it is more performant.
Related
I need to know how to get the result of a select statement that is executed after an insert statement as one execute in PDO.
My PDO connection parameters are as follows:
$opt = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => true];
$conn = new PDO($dsn, $user, $pass, $opt);
I have the following helper function that i use for my PDO statement Execution:
function databaseExecute($SQL, $BIND_P, &$BIND_R) {
global $conn;
$stmt = $conn->prepare($SQL);
if ($stmt->execute($BIND_P)) {
if ($BIND_R !== false) {
//Type testing is important here
$tmp = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if (!$tmp || count($tmp) == 0) {
return false;
}
$BIND_R = $tmp;
} else {
$stmt->closeCursor();
}
return true;
}
$stmt->closeCursor();
return false;
}
My function itself is:
/**
* Adds the current purchase object to the database table
* #return true if success
*/
public function pushToDB() {
global $tbl_purchases;
//We don't push purchaseID since that field is auto handled by the database
$sql = "INSERT INTO " . $tbl_purchases . " (ProductID, UID, TID, GenDate, KeyIDs, Total, Assigned) VALUES (?, ?, ?, ?, ?, ?, ?); SELECT LAST_INSERT_ID();";
$result = array();
if (databaseExecute($sql, array(
$this->getProductID(),
$this->getUID(),
$this->getTID(),
$this->getGenDate(),
$this->getKeyIDsJSON(),
$this->getTotal(),
$this->getAssigned(),
), $r)) {
var_dump($result);
$this->_setPurchaseID($result[0]);
return true;
}
trigger_error("Purchase::pushToDB - Could not push purchase to database", E_USER_ERROR);
return false;
}
But this throws a general error
Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error when i attempt to fetchAll
In this situation, how do i get the result of the SQL execution?
PS: Using Two executes is not acceptable here.
Using Two executes is not acceptable here.
This is but a delusion.
Use either second query or - better - a dedicated function PDO::LastInsertId(). But with your rather poorly designed function it could be a problem. So be it 2 queries.
So change your functions to
function databaseExecute($SQL, $BIND_P = array();) {
global $conn;
if (!$BIND_P)
{
return $conn->query($SQL);
}
$stmt = $conn->prepare($SQL);
$stmt->execute($BIND_P);
return $stmt;
}
and
public function pushToDB() {
global $tbl_purchases;
//We don't push purchaseID since that field is auto handled by the database
$sql = "INSERT INTO $tbl_purchases
(ProductID, UID, TID, GenDate, KeyIDs, Total, Assigned)
VALUES (?, ?, ?, ?, ?, ?, ?)";
databaseExecute($sql, array(
$this->getProductID(),
$this->getUID(),
$this->getTID(),
$this->getGenDate(),
$this->getKeyIDsJSON(),
$this->getTotal(),
$this->getAssigned(),
));
$id = databaseExecute("SELECT LAST_INSERT_ID()")->fetchColumn();
$this->_setPurchaseID($db);
return true;
}
}
You can alter your databaseExectute function to take an extra parameter of 'SecondResult' (for example), then change it to something like...
function databaseExecute($SQL, $BIND_P, &$BIND_R,$SecondResult) {
global $conn;
$stmt = $conn->prepare($SQL);
if ($stmt->execute($BIND_P)) {
if ($BIND_R !== false) {
//Type testing is important here
if ($SecondResult) $stmt->nextRowSet(); // this will ensure that the fetchAll will return the data from the 2nd query
$tmp = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if (!$tmp || count($tmp) == 0) {
return false;
}
$BIND_R = $tmp;
} else {
$stmt->closeCursor();
}
return true;
}
$stmt->closeCursor();
return false;
}
I just typed this in to here directly, I haven't tested it, but it should work.
Also, I'm not saying that the other comments are wrong, and there might be a better way of doing this, but you CAN run two queries within the same 'statement'.
I'm creating a php class what is able to handle my query's and db connections.
I've played around with it for a while and did some research, now I've created m When I use the class for something to handle it does not commit, but I'm not able to see why it doesn't
I don't get any error's and my mysqli error handling is correct.
does anyone have a idea what's wrong with my script?
sql class:
class sql{
function convertArrayReferences($arr){
$refs = array();
foreach($arr as $key => $value)
$refs[$key] = &$arr[$key];
return $refs;
}
function database($database, $query, $parameters){
$result;
$mysqli = new mysqli("****", "****", "****", $database);
if(mysqli_connect_errno()){
return "Connect failed: %s\n " . mysqli_connect_error();
}
if(!$stmt = $mysqli->prepare($query)){
return $mysqli->error;
}
if(!$stmt->bind_param($this->convertArrayReferences($parameters))){
return $mysqli->error;
}
if(!$stmt->bind_result($result)){
return $mysqli->error;
}
if(!$stmt->execute()){
return $mysqli->error;
}
$stmt->close();
return $mysqli->error;
}
}
the bit of code where the function is called:
$execute = $sql->database("survivalTmpReg",
"INSERT INTO USERS (name, surname, email, username, password) VALUES (?, ?, ?, ?, ?)",
array("sssss", $name, $surname, $email, $username, $password));
if($execute){
$message = REGISTER_MESSAGE;
}else{
$message = $execute;
}
Statements are not automatically committed unless autocommit is turned on. On the first statement a transaction is created. Unless the transaction is not explicitly committed its changes will not be visible and eventually the transaction will be aborted by the system. MySQLi supports committing transactions using mysqli::commit.
The Following code will display "12" on the screen. That is all. These echo numbers were added for debugging.
It should Display "123" and insert into a MySQL database the variables in the statement.
For some reason it just ends at the prepare statement. The fail() error check never getting called. Actually, nothing gets called after the prepare statement.
I have been all over the site and believe I am doing everything required properly, but it is more then likely something I did.
Can anyone tell me why the prepare statement is failing this way?
$query = "insert into member(mail, user, val) values (?, ?, ?)";
$uuu = blah#blah.com;
$hhh = Blah Williams;
$val = 0;
echo "1";
if($stmt = $this->conn)
{
echo "2";
$stmt->prepare($query) || $this->fail('MySQL prepare', $stmt->error);
echo "3";
$stmt->bind_param('ssi', $uuu, $hhh, $val)
|| $this->fail('MySQL bind_param', $stmt->error);
$stmt->execute();
if (!$stmt->execute())
{
if ($stmt->errno === 1062 /* ER_DUP_ENTRY */)
{
$this->fail('This username is already taken');
}
else
{
$this->fail('MySQL execute', $stmt->error);
}
}
}
else
{/*error check*/
$this->fail('MySQL insert prepare failed', $stmt->error);
return 0;
}
$stmt->close();
return true;
You should use as your assignment will always be true.
$stmt = $this->conn->prepare($query);
To check why it's failing, use:
var_dump($stmt->errorInfo());
Using PDO to CRUD with SQLITE3. When I insert a string 'didn't', the string goes into the table as 'didn\'t'.
So, later when I read the string back out, to ouput to HTML, I get didn\'t in my web page.
So, if PDO is escaping the single quote on the INSERT with the backslash, how do I strip out the escaping backslashes for presentation?
Does that make sense?
EDIT - Including code. $eventBody is the string in question.
try {
$db = new PDO('sqlite:../posts.sqlite');
$db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
}
catch(PDOException $e)
{
echo $e->getMessage();
die;
}
//using the sqlite functions to do date/time stuff
$query = 'INSERT INTO posts (eDay, eMonth, eYear, eTitle, eBody,author, eURL, eTime)
VALUES( strftime(\'%d\',\'now\') , strftime(\'%m\',\'now\') , strftime(\'%Y\',\'now\') ,"'. $eventTitle .'","'.$eventBody.'","' . $eventAuthor. '","' . $eventURL . '",time(\'now\',\'localtime\'));' ;
try
{
$result=$db->query($query);
if(!($result))
{
echo "INSERT FAILED.<br>";
echo "QUERY STRING: ".$query ." <br>";
die;
}
echo "Successfully Added Record";
$eventTitle = '';
$eventBody='';
$eventURL='';
$eventAuthor='';
// urlRedirect("Referback.php");
}
catch (PDOException $ex)
{
echo $ex->getMessage();
die;
}
catch (Exception $exc)
{
echo $exc->getMessage();
die;
}
}
PDO works the way it should, you should check how you do your insert (build the queries) and the data source (meaning what's coming from $_POST/$_GET).
And specially the magic quotes gpc. If you don't know what it is, check it out. It's a very recurrent problem. Before you start using stripslashes/addslashes and such.
You should also use the prepared statement, it's not only nicer, but a lot less work and safer.
$stmt = $db->prepare('INSERT INTO posts '.
'(eDay, eMonth, eYear, eTitle, eBody, author, eURL, eTime) '.
'VALUES (?, ?, ?, ?, ?, ?, ?, ?);');
$result = $stmt->execute(array(
date('d'),
date('m'),
date('Y'),
$eventTitle,
$eventBody,
$eventAuthor,
$eventURL,
time('now', 'localtime')
));
You could also print the data you give to ̀ execute` to make sure, it is what you want.
I am brand new to php and I ran into a problem that has already taken a few hours of poking around and researching and I could not find anything like it anywhere around the net.
Database:MyPHPAdmin winserver
Goal: Create a new row in table 'photo'. Take the last insert p_id for the current user and update the table accessible_to by creating a new row with that p_id.
I know I can create a trigger, and no it does not work either don't know why. Run out of ideas how.
What I found out by simply printing before-in-after the if statement
if ($stmt = $mysqli->prepare("insert into accessible_to values(?, ?, ?)"))
is that it just bypasses it.
Please post your suggestions.
P.S. The if statement above to which I am referring has been twisted in several ways and yet it does not work.
The connection is already imported.
Thank you a lot.
if(!isset($_SESSION["id"])) {
echo "You are not logged in. ";
echo "You will be returned to the homepage in 3 seconds or click here.\n";
header("refresh: 3; index.php");
}
else {
//if the user have uploaded a photo, insert it into database
if(isset($_POST["ext"])) {
//insert into database, note that p_id is auto_increment
if ($stmt = $mysqli->prepare("insert into photo (ext, owner_id) values (?,?)")) {
$stmt->bind_param("ss", $_POST["ext"], $_SESSION["id"]);
$stmt->execute();
$stmt->close();
$id = htmlspecialchars($_SESSION["id"]);
}
//The following function is fetching the last added p_id in PHOTO by the user with the current SESSION
//Do not simply get the last p_id in PHOTO because someone else might have just added another picture meanwhile
if ($stmt = $mysqli->prepare("select MAX(p_id) from photo where owner_id = ?")){
$stmt->bind_param("s", $id);
$stmt->execute();
$stmt->bind_result($p_id);
if ($stmt->fetch()){
$p_id = htmlspecialchars($p_id);
}
}
echo "BEFORE accessible_to insertion";
echo '<br />';
if ($stmt = $mysqli->prepare("insert into accessible_to values(?, ?, ?)")){
echo "Finally inside accessible_to insertion";
echo '<br />';
$stmt->bind_param("iss", $p_id, $id, 'T');
$stmt->execute();
$stmt->close();
}
echo "AFTER accessible_to insertion";
echo '<br />';
}
//if not then display the form for posting message
else {
echo "Something";
You can't boolean test an assignment and expect it to return a different result. What you want to test for is if $stmt->execute successfully executed or not.
$stmt = $mysql->prepare("insert into foo values (?,?)");
$stmt->bind_param(1,$f1);
$stmt->bind_param(2,$f2);
if ($stmt->execute()) {
... worked
} else {
... fubar
}
You have to start by calling mysqli::connect($server, $user, $pw, $db). The best way to do that is by constructing an object like:
$connection = new mysqli($server, $user, $password, $db);
if ($connection->errno)
{
echo "Connection failed";
echo $this->connection->error;
}
else
{
$stmt = $connection->prepare("insert into photo (ext, owner_id) values (?,?)")) {
$stmt->bind_param("ss", $_POST["ext"], $_SESSION["id"]);
$stmt->execute();
$stmt->close();
}