insert sqlite row from php array - php

So I am attempting to write a generic sqlite insert that can be used no matter how many items a row has. This is for a single row, assumes all columns other than ID, which is set to autoincrementing integer, are assigned, and bindparam must be used. I have been attempting it like so:
Table Quarterbacks
ID---firstName---lastName
public static function insert($table, $values)
{
$pdo = new PDO('sqlite:testTable.sqlite');
$inputString = implode(",", $values);
$statement = $pdo->prepare("insert into $table values (:value)");
$statement->bindParam(':value', $inputString);
$statement->execute();
}
$new = array("Steve", "Young");
Query::insert("Quarterbacks", $new);
The idea being that the table will now add a new row, increment the ID, and add Steve Young. But I get the generic error that the prepare statement is false. I know my pdo is connecting to the database, as other test methods work. There's a lot of array related threads out there but it seems like they're much more complicated than what I'm trying to do. I'm pretty sure it has to do with it treating the single string as invalid, but it also won't take an array of strings for values.
Edit:I'm starting to lean towards a compromise like bash, ie provide a large but not infinite amount of function parameters. Also open to the ...$ style but I feel like that ends up with the same problem as now.
I was able to get this to work
$name = "('daunte' , 'culpepper')";
$cats = "('firstName', 'lastName')";
$statement = $pdo->prepare("insert into $table" .$cats ." values" .$name);
$statement->execute();
But not this
$name = "('reggie' , 'wayne')";
$cats = "('firstName', 'lastName')";
$statement = $pdo->prepare("insert into $table:cats values:name");
$statement->bindParam(':cats', $cats);
$statement->bindParam(':name', $name);
$statement->execute();

Related

PHP and MySQL, call_user_func_array doesn't work with variable as argument

I'm trying to set up a dynamic MySQL query, using mysqli prepared statements. I need to compare multiple values (unknown how many) to a single column.
The following works:
$qs = '?,?';
$query = 'SELECT r_id FROM sometable WHERE someindex IN ('.$qs.')';
$statement = $db_conn->prepare($query);
call_user_func_array(array($statement, 'bind_param'), array('ss', 'Something', 'somethingelse'));
I get a result back from the DB, and can do as I please with the return. BUT, the following does not work:
$qs = '?,?';
$query = 'SELECT r_id FROM sometable WHERE someindex IN ('.$qs.')';
$statement = $db_conn->prepare($query);
$test = array('ss', 'Something', 'something else');
call_user_func_array(array($statement, 'bind_param'), $test);
With the only difference being the assignment of $test, instead of creating the array directly within call_user_func_array.
Similarly:
$one = 'pizza';
$two = 'pie';
call_user_func_array(array($statement, 'bind_param'), array('ss', $one, $two));
also does not work. The statement doesn't even execute.
If I can't put variable names directly into the statement, I can't have the dynamic queries. What's going on? Is there a fix for this, or maybe another (equally simple) way I can run a query on a single column, with an unknown number of terms? References or pointers (i.e. &$one) do not work either.
There seems to be a couple known issues with call_user_func_array back in 2012, or around there, but I can't find anything more recent. I am using PHP 5.5
EDIT 1
Known issue, back in 2007 here

Memcache and Mysqli Prepared Statement Issue

This has been driving me crazy, the issue is I cannot work out How i can get and set the cached data to be displayed within my view.
public function get_something($id, $account_name)
{
$sql = "SELECT one,two,three FROM table WHERE id = ? and account_name = ? ";
$key = md5("SELECT one,two,three FROM table WHERE id = $id and account_name = $account_name ");
$get_result = $this->Core->Core->Memcache->get($key);
if($get_result)
{
// How would I set the Data
}
else
{
$stmt = $this->Core->Database->prepare($sql);
$stmt->bind_param("is", $id, $account_name);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($one, $two, $three);
$stmt->fetch();
//Below is how i set the data
$this->Core->Template->set_data('one', $one);
//Set the Memcache
$this->Core->Memcache->set($key, $stmt, TRUE, 20);
}
So my question is how can I get and set the data from a prepared statement fetch within memcache?
Memcache is a key/value storage system with both the key and the value needing to be serialized. From the php.net page:
Remember that resource variables (i.e. file and connection descriptors) cannot be stored in the cache, because they cannot be adequately represented in serialized state.
It appears your sql statement is looking for three values in a single row. I'm no expert on mysqli, but this is kind of what you want to do:
public function get_something($id, $account_name){
$sql = "SELECT one,two,three FROM table WHERE id = ? and account_name = ? ";
$key = md5("SELECT one,two,three FROM table WHERE id = $id and account_name = $account_name ");
$get_result = $this->Core->Core->Memcache->get($key);
if($get_result){
return $get_result;//#1 just return it, the format is an array like what is being built below
}
else{
$stmt = $this->Core->Database->prepare($sql);
$stmt->bind_param("is", $id, $account_name);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($one, $two, $three);
$stmt->fetch();
//Below is how i set the data
$this->Core->Template->set_data('one', $one);//#2 I don't know what this line does or is for, presumably for something else besides memcache stuff, maybe it acts like return
//Set the Memcache
$array=array();//#3
$array[]=$one;
$array[]=$two;
$array[]=$three;
$this->Core->Memcache->set($key, $array, TRUE, 20);
//this is a function, do you want to return your values somewhere?
}
A few notes, #1 the answer to your question is simple, just return $get_result. It should be an array with three values. #2 I'm not familiar with this line, nor what it does. Is this how your "return" the values to your controller? If so, you'll want to mimick that line where I put the return inside the if #3 This is your problem. You can't save the $stmt variable in memcache, it's a mysqli object, not the data you want. You need to build an array and then save that array. And that should do it for you.
There are other nuances to do, you can loop on the returned values. You should check for mysql not returning anything. But this is the basic starting point to get this going.
Let me know if this works for you.

Passing query to a object, that contains bounded values

I am using PDO to connect with my MySQL db
So before in my users list i did:
Build the query by conditions (online? new user? sex?)
prepared the query
bound the values to the query
execute
fetch in a while() to show the results
Now I am just calling a object, UserCollection, load it and foreach the output.
$list = new UserCollection( $connect );
$list->load();
like that. In load() is the standard query for now:
$stmt = $this->_pdo->query( 'SELECT id FROM users' );
$data = $stmt->fetchAll( PDO::FETCH_ASSOC );
Theres no bounded variables to it, so it is not prepared.
Works fine, and it grabs just all the users.
Now what I wish to do is pass the query, that has been build to UserCollection() and use it to load() in the query.
I could do this easy, if the query was not bounded to variables, like the query above.
So what should I do if i want to pass a variable like this:
SELECT firstname, lastname, id, sex, last_access, bostadsort FROM users WHERE sex=:sex
Then i would need to bind :sex, and i cant just write bindValue() as
the value is not inside the UserCollection object
sometimes it could be only WHERE firstname=:firstname and not :sex, so it would throw error that I have bound a value that I dont use..
So what should I do here? what can i do?
Thanks in forward
You could modify your load method:
function load($params = array()){
$stmt = $this->_pdo->prepare("*your sql here*");
if (!empty($params)){
foreach ($params as $param_key=>$param_value)
$stmt ->bindParam($param_key, $param_value);
}
$data = $stmt->fetchAll( PDO::FETCH_ASSOC );
}
this way you don't have to modify existing code. To use it with new queries with arbitrarry number of parameters you'll just call it like:
$sex = 'male';
$firstname = 'Richard';
$params = array(':sex'=>&$sex, ':firstname'=>&$firstname);
$list->load($parameters);
Don't have PHP at hand, so can't test it, but I think that should work.

PDO bindParam into one statement?

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.

MySQL Prepared statements with a variable size variable list

How would you write a prepared MySQL statement in PHP that takes a differing number of arguments each time? An example such query is:
SELECT `age`, `name` FROM `people` WHERE id IN (12, 45, 65, 33)
The IN clause will have a different number of ids each time it is run.
I have two possible solutions in my mind but want to see if there is a better way.
Possible Solution 1 Make the statement accept 100 variables and fill the rest with dummy values guaranteed not to be in the table; make multiple calls for more than 100 values.
Possible Solution 2 Don't use a prepared statement; build and run the query checking stringently for possible injection attacks.
I can think of a couple solutions.
One solution might be to create a temporary table. Do an insert into the table for each parameter that you would have in the in clause. Then do a simple join against your temporary table.
Another method might be to do something like this.
$dbh=new PDO($dbConnect, $dbUser, $dbPass);
$parms=array(12, 45, 65, 33);
$parmcount=count($parms); // = 4
$inclause=implode(',',array_fill(0,$parmcount,'?')); // = ?,?,?,?
$sql='SELECT age, name FROM people WHERE id IN (%s)';
$preparesql=sprintf($sql,$inclause); // = example statement used in the question
$st=$dbh->prepare($preparesql);
$st->execute($parms);
I suspect, but have no proof, that the first solution might be better for larger lists, and the later would work for smaller lists.
To make #orrd happy here is a terse version.
$dbh=new PDO($dbConnect, $dbUser, $dbPass);
$parms=array(12, 45, 65, 33);
$st=$dbh->prepare(sprintf('SELECT age, name FROM people WHERE id IN (%s)',
implode(',',array_fill(0,count($parms),'?'))));
$st->execute($parms);
There is also the FIND_IN_SET function whose second parameter is a string of comma separated values:
SELECT age, name FROM people WHERE FIND_IN_SET(id, '12,45,65,33')
decent sql wrappers support binding to array values.
i.e.
$sql = "... WHERE id IN (?)";
$values = array(1, 2, 3, 4);
$result = $dbw -> prepare ($sql, $values) -> execute ();
Please take #2 off the table. Prepared statements are the only way you should consider protecting yourself against SQL injection.
What you can do, however, is generate a dynamic set of binding variables. i.e. don't make 100 if you need 7 (or 103).
I got my answer from: http://bugs.php.net/bug.php?id=43568.
This is my working mysqli solution to my problem. Now I can dynamically use as many parameters as I want. They will be the same number as I have in an array or as in this case I am passing the ids from the last query ( which found all the ids where email = 'johndoe#gmail.com') to the dynamic query to get all the info about each of these id no matter how many I end up needing.
<?php $NumofIds = 2; //this is the number of ids I got from the last query
$parameters=implode(',',array_fill(0,$NumofIds,'?'));
// = ?,? the same number of ?'s as ids we are looking for<br />
$paramtype=implode('',array_fill(0,$NumofIds,'i')); // = ii<br/>
//make the array to build the bind_param function<br/>
$idAr[] = $paramtype; //'ii' or how ever many ?'s we have<br/>
while($statement->fetch()){ //this is my last query i am getting the id out of<br/>
$idAr[] = $id;
}
//now this array looks like this array:<br/>
//$idAr = array('ii', 128, 237);
$query = "SELECT id,studentid,book_title,date FROM contracts WHERE studentid IN ($parameters)";
$statement = $db->prepare($query);
//build the bind_param function
call_user_func_array (array($statement, "bind_param"), $idAr);
//here is what we used to do before making it dynamic
//statement->bind_param($paramtype,$v1,$v2);
$statement->execute();
?>
If you're only using integer values in your IN clause, there's nothing that argues against constructing your query dynamically without the use of SQL parameters.
function convertToInt(&$value, $key)
{
$value = intval($value);
}
$ids = array('12', '45', '65', '33');
array_walk($ids, 'convertToInt');
$sql = 'SELECT age, name FROM people WHERE id IN (' . implode(', ', $ids) . ')';
// $sql will contain SELECT age, name FROM people WHERE id IN (12, 45, 65, 33)
But without doubt the solution here is the more general approach to this problem.
I had a similiar problem today and I found this topic. Looking at the answers and searching around the google I found a pretty solution.
Although, my problem is a little bit more complicated. Because I have fixed binding values and dynamic too.
This is the mysqli solution.
$params = array()
$all_ids = $this->get_all_ids();
for($i = 0; $i <= sizeof($all_ids) - 1; $i++){
array_push($params, $all_ids[$i]['id']);
}
$clause = implode(',', array_fill(0, count($params), '?')); // output ?, ?, ?
$total_i = implode('', array_fill(0, count($params), 'i')); // output iiii
$types = "ss" . $total_i; // will reproduce : ssiiii ..etc
// %% it's necessary because of sprintf function
$query = $db->prepare(sprintf("SELECT *
FROM clients
WHERE name LIKE CONCAT('%%', ?, '%%')
AND IFNULL(description, '') LIKE CONCAT('%%', ?, '%%')
AND id IN (%s)", $clause));
$thearray = array($name, $description);
$merge = array_merge($thearray, $params); // output: "John", "Cool guy!", 1, 2, 3, 4
// We need to pass variables instead of values by reference
// So we need a function to that
call_user_func_array('mysqli_stmt_bind_param', array_merge (array($query, $types), $this->makeValuesReferenced($merge)));
And the function makeValuesreferenced:
public function makeValuesReferenced($arr){
$refs = array();
foreach($arr as $key => $value)
$refs[$key] = &$arr[$key];
return $refs;
}
Links for getting this 'know-how': https://bugs.php.net/bug.php?id=49946, PHP append one array to another (not array_push or +), [PHP]: Error -> Too few arguments in sprintf();, http://no2.php.net/manual/en/mysqli-stmt.bind-param.php#89171, Pass by reference problem with PHP 5.3.1

Categories