I usually used PDO with new PDO('sqlite:test.log'); to write in Sqlite3 databases with PHP.
Now, for a performance comparison, I'd like to try using the SQLite3 PHP class :
$db = new SQLite3('test.db');
$db->query("CREATE TABLE IF NOT EXISTS log (a, b, d);");
$stmt = $db->prepare("INSERT INTO log VALUES (?, ?, ?);");
$stmt->execute(array("a", date("Y-m-d\TH:i:sO"), 123));
Unfortunately, after this code is executed, nothing seems to be written: SELECT * from log gives no row.
What's wrong in my code?
I also tried $db->commit(); or $db->query('COMMIT;') without success.
Context: I usually use PDO, but I noticed it takes ~ 50 ms for a simple 1) open of the DB, 2) add a row, 3) commit and close. So I was curious if it was any better with Sqlite3 class instead of PDO. Result: it's the same: ~ 50 ms.
Unlike PDO, you can't provide parameters in the arguments to $stmt->execute(), you have to call $stmt->bindParam() or $stmt->bindValue().
$stmt = $db->prepare("INSERT INTO log VALUES (?, ?, ?);");
$stmt->bindValue(1, "a");
$stmt->bindValue(2, date("Y-m-d\TH:i:sO"));
$stmt->bindValue(3, 123);
$stmt->execute();
If you have an array of values, you can call bindValue() in a loop:
foreach (array("a", date("Y-m-d\TH:i:sO"), 123) AS $i => $value) {
$stmt->bindValue($i+1, $value);
}
Related
So this is my code:
public function cadastrar(Family $family){
var_dump($family); //just to verify
$objDb = new db_con();
$conn = $objDb->getConn();
//REGISTER FAMILY INTO DATABASE
$sql = "insert into family(family_declaracao_renda,
familia_renda_mensal,
family_assinatura_local,
family_assinatura_dia,
family_assinatura_mes,
family_assinatura_ano,
family_deficiente_presente,
family_adaptacao_necessaria_imovel)
values( '$family->getDeclaracaoRendaFamiliar()',
'$family->getRendaBrutaMensal()',
'$family->getAssinaturaLocal()',
'$family->getAssinaturaDia()',
'$family->getAssinaturaMes()',
'$family->getAssinaturaAno()',
'$family->getDeficientePresente()',
'$family->getAdaptacaoImovelNecessaria()')";
//Executar query
if (mysqli_query($conn, $sql)){
echo 'SUCCESS!';
} else {
echo 'ERROR!';
}
}
So I know that you can put variables in between VALUES('variable1', 'variable2'). I'm calling class methods from this object, what am I missing?
Prepare your query, Its the best way to secure your DB against SQLInjection type attacks. It also takes care of a lot of quoting issues, and other things.
$sql = 'INSERT INTO family(
family_declaracao_renda,
familia_renda_mensal,
family_assinatura_local,
family_assinatura_dia,
family_assinatura_mes,
family_assinatura_ano,
family_deficiente_presente,
family_adaptacao_necessaria_imovel
)VALUES(
?,
?,
?,
?,
?,
?,
?,
?
)';
$stmt = mysqli_stmt_init($link);
mysqli_stmt_prepare($stmt, $sql);
mysqli_stmt_bind_param(
$stmt,
'ssssssss', //must be the same as number of argments, types s=string, i=int, d=double/float, b=blob
$family->getDeclaracaoRendaFamiliar(),
$family->getRendaBrutaMensal(),
$family->getAssinaturaLocal(),
$family->getAssinaturaDia(),
$family->getAssinaturaMes(),
$family->getAssinaturaAno(),
$family->getDeficientePresente(),
$family->getAdaptacaoImovelNecessaria()
);
//Executar query
if (mysqli_stmt_execute($stmt)){
echo 'SUCCESS!';
} else {
echo 'ERROR!';
}
It's not hard, even if the MySqli procedural interface is crap. Consider using the Object oriented interface, or switch to PDO (my preferred). MySqli's OOP interface, is a bit easier but I won't go into covering it just for the sake of length. You can look it up on PHP.net, or I am sure there are some tutorials out there.
One word of warning I haven't used MySqli in several years (maybe 6-8) and the procedural style even longer. So I can't gurantee that will work exactly as I have it here. In fact I don't think I ever did use the procedural style of MySqli. When I moved away from mysql_ (so I could do prepared statements) was right around the middle run of PHP5.3 (2010ish maybe a year after I finished web-design college), and I was getting into using OOP (classes and object) almost exclusively in my code. So most of the mysqli code I just copied from PHP.net.
http://php.net/manual/en/mysqli.prepare.php
In PDO:
$sql = 'INSERT INTO family(
family_declaracao_renda,
familia_renda_mensal,
family_assinatura_local,
family_assinatura_dia,
family_assinatura_mes,
family_assinatura_ano,
family_deficiente_presente,
family_adaptacao_necessaria_imovel
)VALUES(
:family_declaracao_renda,
:familia_renda_mensal,
:family_assinatura_local,
:family_assinatura_dia,
:family_assinatura_mes,
:family_assinatura_ano,
:family_deficiente_presente,
:family_adaptacao_necessaria_imovel
)';
try{
$stmt = mysqli_stmt_init($link);
$Pdo->prepare($sql)->execute([
':family_declaracao_renda' => $family->getDeclaracaoRendaFamiliar(),
':familia_renda_mensal' => $family->getRendaBrutaMensal(),
':family_assinatura_local' => $family->getAssinaturaLocal(),
':family_assinatura_dia' => $family->getAssinaturaDia(),
':family_assinatura_mes' => $family->getAssinaturaMes(),
':family_assinatura_ano' => $family->getAssinaturaAno(),
':family_deficiente_presente' => $family->getDeficientePresente(),
':family_adaptacao_necessaria_imovel'=> $family->getAdaptacaoImovelNecessaria(),
]);
}catch(PDOException $e){
echo "PDOException[{$e->getCode()}] {$e->getMessage()} in {$e->getFile()} on {$e->getLine()}";
}
In PDO you can use named place holders :name instead of ? as MySqli uses (you can still use the ? in PDO). What I typically do is copy the field names and add : to the front of them. Named placeholders make it easy to keep track of what value goes where, and the order of the data array (used in execute doesn't even matter). As you can see the mysqli version took around 4 calls, the PDO version took only 2, and with method chaining $Pdo->prepare($sql)->execute(...) you don't even need to put it on another line or set a local variable. That is equivalent to this:
//$Pdo->prepare($sql)->execute(...)
$stmt = $Pdo->prepare($sql); //returns PDOStatement
$stmt->execute(...); //makes a call on PDOStatement
Because $Pdo->prepare($sql) returns a PDOStatement if we don't need that object for anything else, we can just call ->execute(...) on the return value of the previous method in the chain. I felt I should explain that as you may not be used to using Objects. But, chaining keeps the code tidy (no superfluous variables) which makes it bit easier to keep track things (there are less things, to keep track of).
In short, its very easy to do it in both MySqli & PDO, but PDO is well worth learning how to use as it has many advantages. Better interface, better fetch methods, named placeholders, exceptions etc...
Cheers!
$sql = 'INSERT INTO family(
family_declaracao_renda,
familia_renda_mensal,
family_assinatura_local,
family_assinatura_dia,
family_assinatura_mes,
family_assinatura_ano,
family_deficiente_presente,
family_adaptacao_necessaria_imovel
)VALUES(
:family_declaracao_renda,
:familia_renda_mensal,
:family_assinatura_local,
:family_assinatura_dia,
:family_assinatura_mes,
:family_assinatura_ano,
:family_deficiente_presente,
:family_adaptacao_necessaria_imovel
)';
try{
$stmt = mysqli_stmt_init($link);
$Pdo->prepare($sql)->execute([
':family_declaracao_renda' => $family->getDeclaracaoRendaFamiliar(),
':familia_renda_mensal' => $family->getRendaBrutaMensal(),
':family_assinatura_local' => $family->getAssinaturaLocal(),
':family_assinatura_dia' => $family->getAssinaturaDia(),
':family_assinatura_mes' => $family->getAssinaturaMes(),
':family_assinatura_ano' => $family->getAssinaturaAno(),
':family_deficiente_presente' => $family->getDeficientePresente(),
':family_adaptacao_necessaria_imovel'=> $family->getAdaptacaoImovelNecessaria(),
]);
}catch(PDOException $e){
echo "PDOException[{$e->getCode()}] {$e->getMessage()} in {$e->getFile()} on {$e->getLine()}";
}
Right now i have a site that i need to secure from basic SQL injection attacks. The site is very basic, just a form for login and a product search page. Right now i have a file where i am keeping all of my functions used on the website. Here is a couple examples of what they look like:
function createUser($userName,$userPass){
$query = <<<STR
INSERT INTO Users (userName,userPass,userTypeID)
VALUES ('$userName','$userPass',2)
STR;
return executeQuery($query);
}
Or
function getProductByName($productName){
$query = <<<STR
SELECT productID,productName, productPic,productDesc
FROM Products
WHERE productName LIKE '%$productName%'
STR;
return executeQuery($query);
}
I want to change these so that they make use of prepared statements but am having trouble understanding how i can convert them. I found examples that make use of bindParam such as this one:
$stmt = $dbh->prepare("INSERT INTO Customers (CustomerName,Address,City)
VALUES (:nam, :add, :cit)");
$stmt->bindParam(':nam', $txtNam);
$stmt->bindParam(':add', $txtAdd)
$stmt->bindParam(':cit', $txtCit);
$stmt->execute();
Can i incorporate bindparam into my functions somehow? Any leads would be great.
To convert them, just do the prepare() and bindParam() parts in your shown functions and the execute() in the executeQuery() function like this
function createUser($userName,$userPass,$dbh) {
$stmt = $dbh->prepare("INSERT INTO Users (userName, userPass, userTypeID) VALUES (:name, :pass, :id)");
$stmt->bindParam(':name', $userName);
//etc
return executeQuery($stmt);
}
function executeQuery($stmt) {
$stmt->execute();
}
If you don't want to inject the $dbh, you can use global $dbh; or similar methods in the function (and don't have to change your existing code base).
But of course this will be very repetitive, as you need to write multiple bindParam() statements. The simpler way, assuming you are using PDO, would be to pass an array to execute() (as shown in the manual)
function createUser($userName,$userPass,$dbh) {
$stmt = $dbh->prepare("INSERT INTO Users (userName, userPass, userTypeID) VALUES (:name, :pass, :id)");
$params = array(
':name' => $userName,
//etc
);
return executeQuery($stmt, $params);
}
function executeQuery($stmt, $params) {
$stmt->execute($params);
}
To elaborate a bit: The real power of bindParam() comes when you a) want to use the $data_type or $length parameters (What are those?) or b) when you are using multiple execute() statements, eg a bulk insert
$stmt = $dbh->prepare("INSERT INTO users (name) VALUES (:name)");
$stmt->bindParam(":name", $name);
for (...) {
$name = ...
$stmt->execute();
}
When I run this code I get the error "Object of class mysqli could not be converted to string" on the line where I declare a new mysqli object. I can't find the error no matter how many times I read it over.
if(isset($_SESSION['username']))
{
echo $_POST['course'],
$mysqli = new mysqli("localhost","sec_user","Uzg82t=u%#bNgPJw","GPA_Tracker");
$user = $_SESSION['username'];
$stmt = $mysqli->prepare("INSERT into assessment_type (username, courseID, assessment, percentage) VALUES (?, ?, ?, ?)");
$stmt->bind_param('ssss', $user, $_POST['course'], $_POST['assesment'], $_POST['percentage']);
$stmt->execute();
}
As noted in the comments, this is where you have the problem:
echo $_POST['course'], //notice the comma, rather than a semi-colon ";"
$mysqli = new mysqli("localhost","sec_user","Uzg82t=u%#bNgPJw","GPA_Tracker");
The echo statement/construct accepts a comma-separated list of statements, hence coming across the , it thinks the next statement following it is also to be echoed. As it turns out, that next statement is an object-creation statement, whereas echo accepts only strings.
To fix the error, properly close your echo $_POST['course'] with a semicolon like below:
echo $_POST['course'];
I have two inserts on a processing page. The first works without a hitch. The second will not even activate. I even tried putting a PDO query to see if it would work but still nothing.
$cpinsert = $db->prepare('insert into Chatposts values (0, :chatid, :name, :url, :text, now(), :ipaddress, 0)');
$cpinsert -> bindParam(':chatid', $chatroomid, PDO::PARAM_INT);
$cpinsert -> bindParam(':name', $name, PDO::PARAM_STR);
$cpinsert -> bindParam(':url', $url, PDO::PARAM_STR);
$cpinsert -> bindParam(':text', $text, PDO::PARAM_STR);
$cpinsert -> bindParam(':ipaddress', $ipaddress, PDO::PARAM_STR);
$cpinsert -> execute();
// Needs an error checker
$cpid = $cpinsert ->lastInsertID();
$cpinsert->closeCursor();
^ That works fine, though I don't know about the lastinsertid since I cannot test it.
\V/ Nothing in there will execute no matter what I try. Something is preventing anything there from executing or I didn't close the above's connection properly.
// Targets Insert
//if (isset($target)):
$query = "insert into Targets values (9,'rommel')";
$db->query($query);
$targetinsert = $db->prepare('insert into Targets values (:cpid,:tname)');
foreach ($target as $tname):
$targetinsert -> bindParam(':cpid', $cpid, PDO::PARAM_INT);
$targetinsert -> bindParam(':tname', $tname, PDO::PARAM_STR);
endforeach;
$targetinsert -> execute();
//endif;
I have tried everything I know of and no luck. It is quite possible I did a minor mistake since I am new to PDO. Closecursor didn't seem to do anything either when I added it in.
You said you need to do execute many inserts, but for some reason you are executing your query only once.
Also, please remove these try..catch things from your code - they are the reason why you have no idea what's going wrong
I had to recreate a half functional model of my page but I found the problem, it was lastinsertid(). I was applying a PDOstatement class on a PDO class.
Before:
$cpid = $cpinsert->lastInsertID();
After:
$cpid = $db->lastInsertID();
I just had to call the Database instead of the prepare with that line of code.
Is there a way I can put these bindParam statements into one statement?
$q = $dbc -> prepare("INSERT INTO accounts (username, email, password) VALUES (:username, :email, :password)");
$q -> bindParam(':username', $_POST['username']);
$q -> bindParam(':email', $_POST['email']);
$q -> bindParam(':password', $_POST['password']);
$q -> execute();
I was using mysqli prepared before where it was possible, I switched to PDO for assoc_array support. On the php.net website for PDO it shows them on seperate lines, and in all examples I have seen it is on seperate lines.
Is it possible?
Example 2 on the execute page is what you want:
$sth->execute(array(':calories' => $calories, ':colour' => $colour));
You may want to look at the other examples too. With question mark parameters, it would be:
$q = $dbc -> prepare("INSERT INTO accounts (username, email, password) VALUES (?, ?, ?)");
$q->execute(array($_POST['username'], $_POST['email'], $_POST['password']));
If those are the only columns, you can just write:
$q = $dbc -> prepare("INSERT INTO accounts VALUES (?, ?, ?)");
$q->execute(array($_POST['username'], $_POST['email'], $_POST['password']));
helper function is a function that makes you help to avoid writing bunch of repetitive code every time you want to run a query.
This is called "programming" and there is almost none of it on this site, at least under "PHP" tag.
While many peiople thinks that programming stands for copy/pasting chunks of code from manual examples, it's somewhat different.
Although it's hard to learn but really worth it, especially if you're devoting yourself to web-developing.
As you can see, no accepted answer did no real help for you, as you still have to write something like
$sth->execute(array(':username' => $_POST['username'],
':email' => $_POST['email']
':password' => $_POST['password']);
as many times as many fields in your table, which makes not much difference from your initial approach, still makes you write each field name FOUR times.
But being a programmer, you can use powers of programming. A loop, for example - one of cornerstone programming operators.
Every time you see repetitions, you know there should be a loop.
for example, you can set up a list of fields, naming them only once.
And let a program do the rest.
Say, such a function like this one
function pdoSet($fields, &$values, $source = array()) {
$set = '';
$values = array();
if (!$source) $source = &$_POST;
foreach ($fields as $field) {
if (isset($source[$field])) {
$set.="`$field`=:$field, ";
$values[$field] = $source[$field];
}
}
return substr($set, 0, -2);
}
being given an array of field names, it can produce both insert statement and data array for you. Programmatically. So, your code become no more than these 3 short lines:
$fields = array('username', 'email', 'password');
$stmt = $dbh->prepare("INSERT INTO accounts SET ".pdoSet($fields,$values));
$stmt->execute($values);
Your Common Sense is totally right that the aim of coding is to save typing... but his solution doesn't help with the BindParams bit. I couldn't find anything else about this online, so here's something I finally just persuaded to work - I hope it's useful for someone!
//First, a function to add the colon for each field value.
function PrepareString($array){
//takes array (title,author);
//and returns the middle bit of pdo update query :title,:author etc
foreach($array as $k =>$v){
$array[$k]=':'.$v;
}
return implode(', ', $array);
}
Then...
function PdoInsert($table_name,$array){
$db = new PDO(); //however you create your own pdo
//get $fields and $vals for statement
$fields_vals=array_keys($array);
$fields=implode(',',$fields_vals);
$vals=PrepareString($fields_vals);
$sql = "INSERT INTO $table_name($fields) VALUES ($vals)";
$qwe=$db->prepare($sql);
foreach ($array as $k =>$v ){
//add the colon to the key
$y=':'.$k;
//god knows why it doesn't like $qwe->bindParam($y,$v,PDO::PARAM_STR);
// but it really doesn't! So we refer back to $array.
//add checks for different binding types here
(see PDO::PARAM_INT is important in bindParam?)
$qwe->bindParam($y,$array[$k],PDO::PARAM_STR);
}
if ($qwe->execute()==true){
return $db->lastInsertId();
}
else {
return $db->errorCode();
}
}
Then you can insert anything by doing
PdoInsert('MyTableName',array('field1'=>$value1,'field2'=>$value2...));
Having previously sanitized your values of course.
+1 to Matthew Flaschen for the accepted answer, but I'll show you another tip. If you use SQL parameters with names the same as the entries in $_POST, you could take advantage of the fact that $_POST is already an array:
$q->execute($_POST);
The SQL parameter names are prefixed with a colon (:) but the keys in the $_POST array are not. But modern versions of PDO account for this - you no longer need to use colon prefixes in the keys in the array you pass to execute().
But you should be careful that anyone can add extra parameters to any web request, and you should get only the subset of $_POST params that match parameters in your query.
$q = $dbc -> prepare("INSERT INTO accounts (username, email, password)
VALUES (:username, :email, :password)");
$params = array_intersect_key($_POST, array("username"=>1,"email"=>1,"password"=>1));
$q->execute($params);
Personally, I prefer to use a wrapper function for all of pdo, which simplifies the code necessary substantially.
For example, to run bound queries (well, all my queries), I do this:
$iterable_resultset = query("INSERT INTO accounts (username, email, password) VALUES (:username, :email, :password)", array(':username'=>'bob', ':email'=>'bob#example.com', ':password'=>'bobpassword'));
Note that not only is the sql simply a string, but it's actually a reusable string, as you can simply pass the sql as a string and change the array of variables to pass in if you want to perform a similar insert right after that one (not applicable to this situation, but applicable to other sql use cases).
The code that I use to create this wrapper function is as below:
/**
* Run bound queries on the database.
*
* Use: query('select all from players limit :count', array('count'=>10));
* Or: query('select all from players limit :count', array('count'=>array(10, PDO::PARAM_INT)));
*
* Note that it returns foreachable resultset object unless an array is specifically requested.
**/
function query($sql, $bindings=array(), $return_resultset=true) {
DatabaseConnection::getInstance(); // Gets a singleton database connection
$statement = DatabaseConnection::$pdo->prepare($sql); // Get your pdo instance, in this case I use a static singleton instance. You may want to do something simpler.
foreach ($bindings as $binding => $value) {
if (is_array($value)) {
$first = reset($value);
$last = end($value);
// Cast the bindings when something to cast to was sent in.
$statement->bindParam($binding, $first, $last);
} else {
$statement->bindValue($binding, $value);
}
}
$statement->execute();
if ($return_resultset) {
return $statement; // Returns a foreachable resultset
} else {
// Otherwise returns all the data an associative array.
return $statement->fetchAll(PDO::FETCH_ASSOC);
}
}
// Wrapper to explicitly & simply get a multi-dimensional array.
function query_array($sql_query, $bindings=array()) {
return query($sql_query, $bindings, false); // Set return_resultset to false to return the array.
}
As noted in the comments, you'd want to use your own method for setting up a database connection and getting an initialized pdo, but in general it allows your bound sql to be cut down to just a single line.