PDO: Making an execute statement dynamic...but not working - php

this should be easy...but I can't figure it out. If the following execute statement works at the end of an insert query.....
$query->execute(array($this->visible_password,
$this->hashed_password,
$this->temp_hashed_password,
$this->first_name,
$this->last_name,
$this->position,
$this->location,
$this->city,
$this->email,
$this->country,
$this->institution,
$this->interests,
$this->profile_comment));
and
$result = join(", ", array_values($place_holder));
echo $result;
gives
$this->visible_password, $this->hashed_password, $this->temp_hashed_password, $this->email, $this->first_name, $this->last_name, $this->position, $this->location, $this->city, $this->country, $this->institution, $this->interests, $this->profile_comment
then why doesn't this work.....
$query->execute(array($result))
var_dump on placeholder gives...
array(13) {
[0]=> string(26) "$this->visible_password"
[1]=> string(25) "$this->hashed_password"
[2]=> string(30) "$this->temp_hashed_password"
[3]=> string(15) "$this->email"
[4]=> string(20) "$this->first_name"
[5]=> string(19) "$this->last_name"
[6]=> string(18) "$this->position"
[7]=> string(18) "$this->location"
[8]=> string(14) "$this->city"
[9]=> string(17) "$this->country"
[10]=> string(21) "$this->institution"
[11]=> string(19) "$this->interests"
[12]=> string(25) "$this->profile_comment" }
$placeholder is an array...that takes the attributes ($attributes) of a class
$place_holder = array();
foreach ($attributes as $key => $value) {
$place_holder[] = "&#36this->" . $key;
}
I have been trying to make create user method in my user class abstract so I could use it in all my classes. I've been converting my site to PDO from mysqli (nightmare). Here is the method....
public function pdo_create_test() {
$attributes = $this->attributes();
$att = array_keys($attributes);
$question_marks = array();
foreach ($attributes as $key => $value) {
$question_marks[] = "?";
}
$place_holder = array();
foreach ($attributes as $key => $value) {
$place_holder[] = "&#36this->" . $key;
}
$result = join(", ", array_values($place_holder));
$sql = "INSERT INTO ".self::$table_name." (";
$sql .= join(", ", array_keys($attributes));
$sql .= ") VALUES (";
$sql .= join(", ", array_values($question_marks));
$sql .= ")";
$query = $handler->prepare($sql);
$query->execute(array($this->visible_password,
$this->hashed_password,
$this->temp_hashed_password,
$this->first_name,
$this->last_name,
$this->position,
$this->location,
$this->city,
$this->email,
$this->country,
$this->institution,
$this->interests,
$this->profile_comment));
}
Here is an update....(I have altered the placeholder to get the value as Bill said below)
I am also echoing the sql and the result of the placeholder to see what is says.
public function pdo_create_test() {
global $handler;
$attributes = $this->attributes();
$att = array_keys($attributes);
$question_marks = array();
foreach ($attributes as $key => $value) {
$question_marks[] = "?";
}
$place_holder = array();
foreach ($attributes as $key => $value) {
$place_holder[] = $this->$key;
}
$result = join(", ", array_values($place_holder));
$sql = "INSERT INTO ".self::$table_name." (";
$sql .= join(", ", array_keys($attributes));
$sql .= ") VALUES (";
$sql .= join(", ", array_values($question_marks));
$sql .= ")";
echo $sql;
echo "<br/>";
echo "<br/>";
echo $result;
$query = $handler->prepare($sql);
$query->execute(array($result));
}
for
$user = new User;
$user->visible_password = "Sam";
$user->hashed_password = "Walsh";
$user->pdo_create_test();
the output is
INSERT INTO users (visible_password, hashed_password, temp_hashed_password, email, first_name, last_name, position, location, city, country, institution, interests, profile_comment) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Sam, Walsh, , , , , , , , , , ,
but no entry into the database....don't see why...the other DB fields are set to NULL so this isn't an issue....

PHP does have the concept of variable variables, so you can do this:
$varname = 'foo';
$foo = 123;
echo $$varname;
But this feature is limited. You can't apply it to all the elements of an array, nor can you use it with $this->foo notation for object variables.
You can use the variable variable syntax to get the value of your object variables corresponding to the attributes this way:
$place_holder = array();
foreach ($attributes as $key => $value) {
$place_holder[] = $this->$key;
}
Notice the $key after ->. Normally object variable syntax does not have a $ in that place. By using a $ there, I'm telling it to get the value of the object variable whose name is the value of $key.
By the way, here's a more succinct (and probably faster) way to do the same thing:
$place_holder = array_intersect_key($attributes, get_object_vars($this));
Re your comment:
Well, first of all, you're doing this wrong:
$result = join(", ", array_values($place_holder));
This produces a single string with values separated by commas. It doesn't matter if you wrap it in array($result), that just creates an array of a single string, even though the single string contains commas.
In other words, these two arrays are quite different:
array('A,B,C') // array with one element
array('A', 'B', 'C') // array with three elements
Instead, you need to pass to execute() is an array with the same number of elements as the number of ? placeholders.
So just pass this:
$query->execute($place_holder);
Second, you need to check for errors after every prepare() or execute():
if (($query = $handler->prepare(...)) === false) {
print_r($handler->errorInfo());
}
if ($query->execute($result) === false) {
print_r($query->errorInfo());
}
Or if that seems like too much coding, you could instead enable PDO exceptions, and an error will cause your app to terminate with an error.
$handler->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

I'm not sure what your place_holder variable contains, but I think this might be the reason. Join is just another name for the implode function which returns a string. Execute needs the actual physical input item.
From the PHP site on execute for the array of input values:
An array of values with as many elements as there are bound parameters
in the SQL statement being executed. All values are treated as
PDO::PARAM_STR.
UPDATE
As stated in comments, here is a function that will execute a SQL string using the given data object. It would be best to separate this out into a couple of functions (create parameter array, bind parameters, ect), but I have put them all into one function for readability.
An example use of the function could be executeQuery('SELECT * FROM users WHERE id = :id', array('id' => 1));
function executeQuery($sql, $data) {
//Assumed that $pdo is created and is a valid PDO object
$params = array();
preg_match_all('/:\w+/', $sql, $matches);
//Loop through all the matches
//Create an array that looks like: array(
// :id => 1,
// :name => 'Me'
//)
foreach($matches[0] as $param) {
$paramName = substr($param, 1);
//If the item is not supplied in the data object, null will be used
$params[$param] = isset($data[$paramName]) && !empty($data[$paramName]) ? $data[$paramName] : null;
}
//Pepare the SQL statement
if(($stmt = $pdo->prepare($sql)) !== false) {
//Bind all parameters
foreach($params as $param => $value) {
$stmt->bindValue($param, $value);
}
//Execute the statement
$stmt->execute();
}
}

Related

Convert NoSQL like syntax to MySQL syntax

This is mysql table structure
item_id
StockNo
SizeCd
1
12003
UNIT
2
12007
JOGO
3
12008
PACOTE
4
12033
JOGO
5
12034
JOGO
6
12038
UNIT
I'm using plugin called DevExtreme for remote data grid. It's API request looks like below.
{
from: get_data
skip: 0
take: 50
requireTotalCount: true
filter: [["SizeCd","=","UNIT"],"or",["SizeCd","=","JOGO"]]
}
Where filter is what I need help with. What I want is I want to convert this string into MySQL where condition syntax.
I tried using php functions like array_merge join but couldn't get it to work. Sometimes it place quotes to both sides or sometimes quotes were missing. It should be like field name without quotes and value with quotes. Like in mysql where syntax.
Sorry for bad formatting and grammar mistakes.
For this array:
$array = [["SizeCd","=","UNIT"],"or",["SizeCd","=","JOGO"],"or",["SizeCd","=","PACOTE"]];
For PDO
You can use the following functions:
function arrayToQuery(string $tableName, array $array) : string
{
$select = "SELECT * FROM `{$tableName}` WHERE ";
foreach($array as $item) {
if(is_array($item)) {
$select .= "`{$item[0]}` {$item[1]} ?";
} else {
$select .= " {$item} ";
}
}
return $select;
}
function arrayToParams(array $array) : array
{
$return = [];
foreach($array as $item) {
if(is_array($item)) {
$return[] = $item[2];
}
}
return $return;
}
var_dump(
arrayToQuery("x", $array),
arrayToParams($array)
);
Output:
string(66) "SELECT * FROM `x` WHERE `SizeCd` = ? or `SizeCd` = ? or `SizeCd` = ?"
array(3) {
[0]=>
string(4) "UNIT"
[1]=>
string(4) "JOGO"
[2]=>
string(6) "PACOTE"
}
Example using PDO
$conn = /* your conn object */;
$sql = arrayToQuery("your_table_name", $array);
$stmt = $conn->prepare($sql);
$stmt->execute(arrayToParams($array));
Update: For Mysqli
For mysqli you can use the following function:
function arrayToQueryMysqli($mysqli, string $table, array $array) : string
{
$select = "SELECT * FROM `{$table}` WHERE ";
foreach($array as $item) {
if(is_array($item)) {
$select .= "`{$item[0]}` {$item[1]} '" . $mysqli->real_escape_string($item[2]) . "'";
} else {
$select .= " {$item} ";
}
}
return $select;
}
$mysqli = new mysqli(/* Your settings */);
$query = arrayToQueryMysqli($mysqli, "tablename", $array);
$result = $mysqli->query($query);
var_dump($result);

SQL injection vulnerability with prepared statement that uses concatenation

Would parameterized code that uses concatenation in this way have a SQL injection vulnerability? I assume that it would, but I'm not certain what POST data would exploit it.
foreach ($_POST as $key => $value) {
$columns .= ($columns == "") ? "" : ", ";
$columns .= $key;
$holders .= ($holders == "") ? "" : ", ";
$holders .= ":".$value;
}
$sql = "INSERT INTO request ($columns) VALUES ($holders)";
$stmt = $this->pdo->prepare($sql);
foreach($_POST as $key => $value) {
$field = ":".$key;
$stmt->bindValue($field, $value);
}
$stmt->execute();
You need an array to store all the columns of request table and check if the post key is present in the array.
PHP code:
$request_columns = array('column1','column2');// all the columns of request table
foreach ($_POST as $key => $value) {
if(in_array($key,$request_columns)){
$columns .= ($columns == "") ? "" : ", ";
$columns .= $key;
$holders .= ($holders == "") ? "" : ", ";
$holders .= ":".$key;
}
}
$sql = "INSERT INTO request ($columns) VALUES ($holders)";
$stmt = $this->pdo->prepare($sql);
foreach($_POST as $key => $value) {
if(in_array($key,$request_columns)){
$field = ":".$key;
$stmt->bindValue($field, $value);
}
}
$stmt->execute();
#KetanYekale is correct that you need to filter $_POST for known column names.
Here's an alternative way of doing it, using some PHP builtin functions.
$request_columns = array('column1','column2');// all the columns of request table
# get a subset of $_POST, only those that have keys matching the known request columns
$post_only_columns = array_intersect_key(
$_POST,
array_flip($request_column)
);
# make sure columns are delimited like `name` in case they are SQL reserved words
$columns = implode(array_map(function ($col) { return "`$col`"; }, array_keys($post_only_columns), ', ';
# use ? positional holders, not named holders. it's easier in this case
$holders = implode(array_fill(1, count($post_only_columns), '?'), ', ');
$sql = "INSERT INTO request ($columns) VALUES ($holders)";
$stmt => $this->pdo->prepare($sql);
# no need to bindValue() or use a loop, just pass the values to execute()
$stmt->execute( array_values($post_only_columns) );
PHP has many Array functions that you can use in different scenarios to make your code faster and more concise. You can use these functions to avoid writing some types of foreach looping code.

How to iterate an associative array and create multiple records with their values

How to iterate an associative array and create multiple records with their values.
The problem seems to be to correctly configure the variables for bind_param.
array(2) {
[0]=>
object(stdClass)#4 (3) {
["number"]=>int(1)
["detail"]=>string(3) "one"
}
[1]=>
object(stdClass)#5 (3) {
["number"]=>int(2)
["detail"]=>string(3) "two"
}
}
PHP and MYSQL
foreach ($myArray as $key => $value){
$one = $value->number;
$two = $value->detail;
$sqlQuery = "INSERT INTO my_tab (number, detail) VALUES (?,?)";
if($statement = $conexion->prepare($sqlQuery)){
$statement->bind_param("is", $one , $two);
$statement->execute();
}else{
...
}
}
Expected result in my_tab:
number detail
1 one
2 two
I don't see any reason why this should NOT work:
$sqlQuery = "INSERT INTO my_tab (number, detail) VALUES (?,?)"; // no need to include this in loop - it's just a static string
if ($conexion) { // make sure this is defined
foreach ($myArray as $key => $value){
$one = (int)$value->number; // enforce this type as int
$two = $value->detail;
$statement = $conexion->prepare($sqlQuery));
if ($statement) {
$statement->bind_param("is", $one , $two);
$statement->execute();
}else{
...
}
}
}

insert array of values into a database using SQL query?

I have a PHP array of the column names in my SQL table. I also have an array of the values I want to assign to these columns. How do I put this in an SQL query. At present im writing out each column title like so:
$query = "INSERT INTO `first_page_data`(`a`, `b`, `c`, `d`, `e`, `f`, `g`, `h`)
VALUES ('$1','$2','$3','$4','$5','$6','$7','$8')";
But there must be a way of just using the arrays?
As an extra, is there a way of defining key/value pairs to keep the two pairs of data together, and then using these to insert into the database? how is this formatted in the SQL query?
Here's another similar solution.
Code:
<?php
function mysql_insert_array($table, $data, $exclude = array()) {
$fields = $values = array();
if( !is_array($exclude) ) $exclude = array($exclude);
foreach( array_keys($data) as $key ) {
if( !in_array($key, $exclude) ) {
$fields[] = "`$key`";
$values[] = "'" . mysql_real_escape_string($data[$key]) . "'";
}
}
$fields = implode(",", $fields);
$values = implode(",", $values);
if( mysql_query("INSERT INTO `$table` ($fields) VALUES ($values)") ) {
return array( "mysql_error" => false,
"mysql_insert_id" => mysql_insert_id(),
"mysql_affected_rows" => mysql_affected_rows(),
"mysql_info" => mysql_info()
);
} else {
return array( "mysql_error" => mysql_error() );
}
}
?>
Example usage:
<?php
// Open database here
// Let's pretend these values were passed by a form
$_POST['name'] = "Bob Marley";
$_POST['country'] = "Jamaica";
$_POST['music'] = "Reggae";
$_POST['submit'] = "Submit";
// Insert all the values of $_POST into the database table `artists`, except
// for $_POST['submit']. Remember, field names are determined by array keys!
$result = mysql_insert_array("artists", $_POST, "submit");
// Results
if( $result['mysql_error'] ) {
echo "Query Failed: " . $result['mysql_error'];
} else {
echo "Query Succeeded! <br />";
echo "<pre>";
print_r($result);
echo "</pre>";
}
// Close database
?>
Source: Inserting An Array into a MySQL Database Table
//Insert ( var , Array )
function insert($table, $inserts) {
$values = array_map('mysql_real_escape_string', array_values($inserts));
$keys = array_keys($inserts);
return mysql_query('INSERT INTO `'.$table.'` (`'.implode('`,`', $keys).'`) VALUES (\''.implode('\',\'', $values).'\')');
}
/* Samples
insert('first_page_data', array(
'a' => 'Just Persian Gulf',
'b' => 'DB9',
'c' => '2009'
));
*/
it's good And Rapid!
Using PHP:
Getting your values into an array as $key => $value depends on the situation, but manually it would happen like so:
$array = array(`a` => '$1',`b` => '$2', ...and so on); //I am assuming that $ is not a variable indicator since it is inside single quotes.
There are a variety of array functions that can help you if you have existing arrays that you would rather manipulate to create the final array.
Once you have it, however:
$query = 'INSTERT INTO `first_page_data` (';
foreach ($array as $key => $value) {
$query .= '`' . $key . '`';
}
$query .= ') VALUES (';
foreach ($array as $value) {
$query .= '`' . $value . '`';
}
$query .= ')';
The code runs a foreach on the array twice, once to get the key and append it to the appropriate part of the string, and the other to add the corresponding values.
Try serialize() before the INSERT and unserialize() to get the array after a SELECT.
You need only one field to insert all the data.
http://ca1.php.net/manual/fr/function.serialize.php
http://ca1.php.net/manual/fr/function.unserialize.php
# Insert this array
$arr = array("sounds" => "one", "sound" => "two", "big" => "blue");
function addQuotes($str){
return "'$str'";
}
# Surround values by quotes
foreach ($arr as $key => &$value) {
$value = addQuotes($value);
}
# Build the column
$columns = implode(",", array_keys($arr));
# Build the values
$values = implode(",", array_values($arr));
# Build the insert query
$query = "INSERT INTO table (".$columns.") VALUES (".$values.")";
echo $query;
// returns
INSERT INTO table (sounds,sound,big) VALUES ('one','two','blue')

PHP Implode Associative Array

So I'm trying to create a function that generates a SQL query string based on a multi dimensional array.
Example:
function createQueryString($arrayToSelect, $table, $conditionalArray) {
$queryStr = "SELECT ".implode(", ", $arrayToSelect)." FROM ".$table." WHERE ";
$queryStr = $queryStr.implode(" AND ",$conditionalArray); /*NEED HELP HERE*/
return $queryStr;
}
$columnsToSelect = array('ID','username');
$table = 'table';
$conditions = array('lastname'=>'doe','zipcode'=>'12345');
echo createQueryString($columnsToSelect, $table, $conditions); /*will result in incorrect SQL syntax*/
as you can see I need help with the 3rd line as it's currently printing
SELECT ID, username FROM table WHERE
lastname AND zipcode
but it should be printing
SELECT ID, username FROM table WHERE
lastname = 'doe' AND zipcode = '12345'
You're not actually imploding a multidimensional array. $conditions is an associative array.
Just use a foreach loop inside your function createQueryString(). Something like this should work, note it's untested.:
$terms = count($conditionalArray);
foreach ($conditionalArray as $field => $value)
{
$terms--;
$queryStr .= $field . ' = ' . $value;
if ($terms)
{
$queryStr .= ' AND ';
}
}
Note: To prevent SQL injection, the values should be escaped and/or quoted as appropriate/necessary for the DB employed. Don't just copy and paste; think!
function implodeItem(&$item, $key) // Note the &$item
{
$item = $key . "=" . $item;
}
[...]
$conditionals = array(
"foo" => "bar"
);
array_walk($conditionals, "implodeItem");
implode(' AND ', $conditionals);
Untested, but something like this should work. This way you can also check if $item is an array and use IN for those cases.
You will have to write another function to process the $conditionalArray, i.e. processing the $key => $value and handling the types, e.g. applying quotes if they're string.
Are you just dealing with = condition? What about LIKE, <, >?
Forgive me if its not too sexy !
$data = array('name'=>'xzy',
'zip'=>'3432',
'city'=>'NYK',
'state'=>'Alaska');
$x=preg_replace('/^(.*)$/e', ' "$1=\'". $data["$1"]."\'" ',array_flip($data));
$x=implode(' AND ' , $x);
So the output will be sth like :
name='xzy' AND zip='3432' AND city='NYK' AND state='Alaska'
I'd advise against automated conditionals creation.
Your case is too local, while there can be many other operators - LIKE, IN, BETWEEN, <, > etc.
Some logic including several ANDs and ORs.
The best way is manual way.
I am always doing such things this way
if (!empty($_GET['rooms'])) $w[]="rooms='".mesc($_GET['rooms'])."'";
if (!empty($_GET['space'])) $w[]="space='".mesc($_GET['space'])."'";
if (!empty($_GET['max_price'])) $w[]="price < '".mesc($_GET['max_price'])."'";
Though if you still want it with this simple array, just iterate it using
foreach ($conditions as $fieldname => $value)...
and then combine these variables in the way you need. you have 2 options: make another array of this with field='value' pairs and then implode it, or just concatenate, and substr trailing AND at the end.
I use a variation of this:
function implode_assoc($glue,$sep,$arr)
{
if (empty($glue)) {$glue='; ';}
if (empty($sep)) {$sep=' = ';}
if (is_array($arr))
{
foreach ($arr as $k=>$v)
{
$str .= $k.$sep.$v.$glue;
}
return $str;
} else {
return false;
}
};
It's rough but works.
Here is a working version:
//use: implode_assoc($v,"="," / ")
//changed: argument order, when passing to function, and in function
//output: $_FILES array ... name=order_btn.jpg / type=image/jpeg / tmp_name=G:\wamp\tmp\phpBDC9.tmp / error=0 / size=0 /
function implode_assoc($arr,$glue,$sep){
$str = '';
if (empty($glue)) {$glue='; ';}
if (empty($sep)) {$sep=' = ';}
if (is_array($arr))
{
foreach ($arr as $key=>$value)
{
$str .= $key.$glue.$value.$sep;
}
return $str;
} else {
return false;
}
}
I know this is for the case of a pdo mysql type.. but what i do is build pdo wrapper methods, and in this case i do this function that helps to build the string, since we work with keys, there is no possible way to mysql inject, since i know the keys i define / accept manually.
imagine this data:
$data=array(
"name"=>$_GET["name"],
"email"=>$_GET["email"]
);
you defined utils methods...
public static function serialize_type($obj,$mode){
$d2="";
if($mode=="insert"){
$d2.=" (".implode(",",array_keys($obj)).") ";
$d2.=" VALUES(";
foreach ($obj as $key=>$item){$d2.=":".$key.",";}
$d2=rtrim($d2,",").")";}
if($mode=="update"){
foreach ($obj as $key=>$item){$d2.=$key."=:".$key.",";}
}
return rtrim($d2,",");
}
then the query bind array builder ( i could use direct array reference but lets simplify):
public static function bind_build($array){
$query_array=$array;
foreach ($query_array as $key => $value) { $query_array[":".$key] = $query_array[$key]; unset($query_array[$key]); } //auto prepair array for PDO
return $query_array; }
then you execute...
$query ="insert into table_x ".self::serialize_type( $data, "insert" );
$me->statement = #$me->dbh->prepare( $query );
$me->result=$me->statement->execute( self::bind_build($data) );
You could also go for an update easy with...
$query ="update table_x set ".self::serialize_type( $data, "update" )." where id=:id";
$me->statement = #$me->dbh->prepare( $query );
$data["id"]="123"; //add the id
$me->result=$me->statement->execute( self::bind_build($data) );
But the most important part here is the serialize_type function
Try this
function GeraSQL($funcao, $tabela, $chave, $valor, $campos) {
$SQL = '';
if ($funcao == 'UPDATE') :
//Formata SQL UPDATE
$SQL = "UPDATE $tabela SET ";
foreach ($campos as $campo => $valor) :
$SQL .= "$campo = '$valor', ";
endforeach;
$SQL = substr($SQL, 0, -2);
$SQL .= " WHERE $chave = '$valor' ";
elseif ($funcao == 'INSERT') :
//Formata SQL INSERT
$SQL = "INSERT INTO $tabela ";
$SQL .= "(" . implode(", ", array_keys($campos) ) . ")";
$SQL .= " VALUES ('" . implode("', '", $campos) . "')";
endif;
return $SQL;
}
//Use
$data = array('NAME' => 'JOHN', 'EMAIL' => 'J#GMAIL.COM');
GeraSQL('INSERT', 'Customers', 'CustID', 1000, $data);

Categories