MySQLi Dynamic Prepared Statements strange behavior - php

So I have successfully wrote a script that will prepare and bind any parameters to a sql query but I am having trouble on the binding results part.
Here is my script:
public function query( $query_string, $params = null, $param_types = null) {
//prepare the statement
$stmt = $this->db->prepare($query_string);
//check if the sql query is wrong.
if($stmt === false) {
echo "Wrong SQL: " . $query_string . "<br />Error: " . $this->db->errno . " " . $this->db->error;
}
if($params != null && $param_types != null) {
//build the array that needs to pass the args to bind_param.
$a_params = array();
$a_params[] = &$param_types;
for($i=0; $i<count($params); $i++)
$a_params[] = &$params[$i];
// $stmt->bind_param('s', $param); equivalent
call_user_func_array(array($stmt, 'bind_param'), $a_params);
}
//run the query
$stmt->execute();
$data = $stmt->result_metadata();
$fields = array();
$out = array();
$count = 0;
while($field = $data->fetch_field()) {
$fields[$count++] = &$out[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $fields);
$results = array();
$k = 0;
// loop through all result rows
while ($stmt->fetch()) {
$results[$k++] = $out;
print_r($out);
echo "<br />";
}
$stmt->close();
return $results;
}
It seems to bind correctly when I output with print_r, but when I add to an array to return (for use in later script it has odd behavior).
Example call to the method:
$users = $TCS->query("SELECT name, age, id FROM test");
foreach($users as $user) {
echo $user['name'] . ': <br />';
echo ' age: ' . $user['age'] . '<br />';
echo ' id: ' . $user['id'] . '<br />';
}
But here is my output:
Array ( [name] => jake [age] => 18 [id] => 1 )
Array ( [name] => ryan [age] => 19 [id] => 2 )
Array ( [name] => stephen [age] => 16 [id] => 3 )
stephen:
age: 16
id: 3
stephen:
age: 16
id: 3
stephen:
age: 16
id: 3

TL;DR
Change $results[$k++] = $out; to $results[$k++] = array_flip(array_flip($out));
Here is an explanation of what is happening.
var_dump($out);
// first iteration
array(2) {
["name"]=>
&string(4) "mike"
["id"]=>
&int(1)
}
// second interation
array(2) {
["name"]=>
&string(6) "amanda"
["id"]=>
&int(2)
}
The important point to notice here are the ampersands, they mean that the values of id and name are references. What those references are pointing to will be changed when $out['id'] and $out['name'] are. Thus the statement $results[$k++] = $out; means copy $out and assign it to $results[$k], this includes copying the references. In the next iteration what is being referenced in $results[$k-1] is changed to the new values behind your back.
Here is a simple example;
$i = 1;
$a = array('id' => &$i);
$b = $a;
$i = 2;
var_dump($a,$b);
// output
array(1) {
["id"]=>
&int(2)
}
array(1) {
["id"]=>
&int(2)
}
You can fix this by modifying the line:
$results[$k++] = $out; to $results[$k++] = array_flip(array_flip($out));
The inner array_flip will dereference the values and make them keys, the outer flip reverse the process.
But I would suggest rewriting everything below $stmt->execute() as follows.
//run the query
$stmt->execute();
$result = $stmt->get_result();
if (!$result) { return array(); }
$ret = array();
while ($row = $result->fetch_assoc()) {
array_push($ret, $row);
}
return $ret;

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);

Array compare if value exist add value otherwise 0 in indexes

After 1st answer my output is one value missing my ratting lenght is According to Array2 not Array !,,,,,Input:
I want this output:
My output is getting 0,0,0,0
I want to compare two arrays with different length, if matches the add the value, if not then add "0" and skip after assign and go to first loop
My code:
for($i=0;$i<count($custt1);$i++){
for($j=0;$j<count($items);$j++){
if($items[$j]==$custt1[$i]){
$x[$j]=$rating[$j];
}
else{
$x[$j]=0;
}
}
for($j=0;$j<count($items1);$j++){
if($items1[$j]==$custt1[$i]){
$y[$j]=$rating1[$j];
}
else{
$y[$j]=0;
}
}
}
I want to save in x and y array if value present then add rating otherwise "0" and go to first loop but I am facing in index 0 if value not present add 0 and on 8 indexes. Hope you understand my explanation and please help me to solve this.
now my output is by applying 1st answer
<br />
<b>Notice</b>: Undefined offset: 5 in
<b>C:\xampp\htdocs\Rest\new2.php</b> on line
<b>79</b>
<br />
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 0
[4] => 5
[5] =>
[6] => 0
[7] => 0
[8] => 0
)
Because i am implementing recommendation system
<?php
$dbhost = "localhost";
$dbuser = "root";
$dbpass = "";
$dbname = "hfc";
$array;
$connection = mysqli_connect($dbhost, $dbuser, $dbpass, $dbname);
if ($connection) {
$sql1="SELECT item_name
from feedback
GROUP BY item_name";
$sql="SELECT customer_email_address AS customeremail,
GROUP_CONCAT(
DISTINCT CONCAT(cook_email_address,'-',item_name,'-',rating)
) AS uniqueItem
FROM feedback
GROUP BY customer_email_address";
$result = mysqli_query($connection, $sql);
while ($row = mysqli_fetch_array($result))
{
$customer_email[] = $row['customeremail'];
$cust1[]= $row['uniqueItem'].",";
}
$item[] = explode(",",$cust1[0]);
for($i=0;$i<count($item[0])-1;$i++){
$item_rating[] = explode("-",$item[0][$i]);
}
print_r ($item_rating);
for($i=0;$i<count($item_rating);$i++){
$items[]=$item_rating[$i][1];
$rating[]=$item_rating[$i][2];
}
$item1[] = explode(",",$cust1[1]);
for($i=0;$i<count($item1[0])-1;$i++){
$item_rating1[] = explode("-",$item1[0][$i]);
}
print_r ($item_rating1);
for($i=0;$i<count($item_rating1);$i++){
$items1[]=$item_rating1[$i][1];
$rating1[]=$item_rating1[$i][2];
}
$result1 = mysqli_query($connection, $sql1);
while ($row = mysqli_fetch_array($result1))
{
$custt1[]= $row['item_name'];
}
print_r ($custt1);
print_r ($items);
$output = array();
foreach ($custt1 as $i =>$item) {
if (in_array($item, $items)) {
$output[] = $rating[$i];
} else {
$output[] = 0;
}
}
print_r ($output);
for($i=0;$i<count($custt1);$i++){
for($j=0;$j<count($items);$j++){
if($items[$j]==$custt1[$i]){
$x[$i]=$rating[$j];
}
else{
$x[$i]=0;
}
}
for($j=0;$j<count($items1);$j++){
if($items1[$j]==$custt1[$i]){
$y[$j]=$rating1[$j];
}
else{
$y[$j]=0;
}
}
}
print_r ($x);
print_r ($y);
for($i1=0;$i1<count($custt1);$i1++)
{
$array[]=explode(",",$custt1[$i1]);
}
// echo count($array);
// print_r($custt1)."<br>";
/*for($i=0;$i<count($array);$i++)
{
for($y=1;$y<count($array);$y++)
{
$pearson=Corr($array[$i],$array[$y],$c=count($array));
}
}*/
$pearson=Corr($array);
echo $pearson;
}
function Corr(&$arr){
$x=$arr[0];
$y=$arr[1];
$length=count($x)-1;
$mean1=array_sum($x)/ $length;
$mean2=array_sum($y)/ $length;
echo $mean1."mean of x";
echo $mean2."mean of y";
echo "\n";
//echo $mean2;
$a=0;
$b=0;
$axb=0;
$a2=0;
$b2=0;
for($i=0;$i<$length;$i++)
{
$a=$x[$i]-$mean1;
$b=$y[$i]-$mean2;
$axb=$axb+($a*$b);
$a2=$a2+ pow($a,2);
$b2=$b2+ pow($b,2);
$corr= $axb / sqrt($a2*$b2);
}
return $corr;
}
?>
Use in_array() to tell if the element of array 1 is in array 2.
$output = array();
foreach ($array1 as $i => $item) {
if (in_array($item, $array2)) {
$output[] = $rating[$i];
} else {
$output[] = 0;
}
}

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

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();
}
}

PHP store 2 values into 1 array index

I'm trying to create a sort of multidimensional array which takes 2 values from a database and stores in into 1 index in the array
Example x[0] = Jille, 595
I'm doing this as such
while ($row = mysql_fetch_array($result2))
{
$opponents[] = $row['opponents'];
$fixId= array($row['fixture_id'] => $opponents) ; //Is this line correct??
}
Then later on in my code I want to use the $fixId array which should hold 2 values per index
I do this as such:
foreach($fixid as $id => $oppname){
echo "<option value=\"$oppname\" >".$oppname;"</option>";
}
However it is not working the values $id and $oppname does not have a value or take on some strange value.
What am I doing wrong?
You can do it like that :
while ($row = mysql_fetch_array($result2))
{
$opponents[] = array('oppname' => $row['opponents'], 'oppid' => $row['fixture_id']) ;
}
foreach ($opponents as $opp) {
echo '<option value="'.$opp['oppid'].'">'.$opp['oppname'].'</option>';
}
Try this:
$fixId = array();
while ($row = mysql_fetch_array($result2))
{
$opponents[] = $row['opponents'];
$fixId[] = array('fixture_id' => $opponents) ;
}
I made this test:
<?php
$fixId = array();
$opponents[] ="Jille, 595";
$fixId[] = array('fixture_id' => $opponents) ;
var_dump($fixId);
?>
Showing: array(1) { [0]=> array(1) { ["fixture_id"]=> array(1) { [0]=> string(10) "Jille, 595" } } }

Mysql extracting array in Php

What is the problem with my mysql array extraction below:
$mysql_array = mysql_query("SELECT * FROM table1 WHERE uid1='$uid1'");
$array = array();
while($row = mysql_fetch_assoc($mysql_array)){
$array[] = $row;
}
$array = array_unique($array);
$array = array_reverse($array);
$emails = array();
$numbers = array();
foreach($array as $row){
$uid2 = $row['uid2'];
$number = number($uid2);
if(strlen($number) > 9){
$numbers[] = array('uid2' => $uid2, 'number' => $number);
}
else{
$email = email($uid2);
$emails[] = array('uid2' => $uid2, 'email' => $email);
}
}
$numbers = array_unique($numbers);
$emails = array_unique($emails);
var_dump($numbers);
var_dump($emails);
There must be something I need to do to convert the "Resource" from mysql into an array.
There must be a problem with the above code.
This is what I am getting on the var_dumps: array(0) { }
and array(1) { [0]=> array(2) { ["uid2"]=> NULL ["email"]=> NULL } }
Look at this foreach:
foreach($array as $uid2){
$uid2 = $array['uid2'];
$number = number($yfbid);
if(strlen($number) > 9){
$numbers[] = array('uid2' => $uid2, 'number' => $number);
}
else{
$email = email($uid2);
$yfbid[] = array('uid2' => $uid2, 'email' => $email);
}
}
You iterate all positions of array and call them $uid2. Then, in the first line you do $uid2 = $array['uid2'];. You loose your array position.
Maybe you wanted something like:
$var = $uid2['uid2'];
I think this is what you're trying to do:
$result = mysql_query("SELECT DISTINCT * FROM table1 WHERE uid1 = '{$uid1}' ORDER BY /* insert column name here, followed by ASC or DESC */");
$table = array();
while ($row = mysql_fetch_assoc($result))
$table[] = $row;
// at this point, each entry in the $table array is one row from 'table1'.
// shouldn't need to do array_unique or array_reverse with the modified SQL query above.
$numbers = $emails = array();
foreach ($table as $row)
{
$number = number($row["uid2"]);
if (strlen($number) > 9 && !in_array($numbers[$row["uid2"]], $number, true))
$numbers[$row["uid2"]][] = $number;
else
{
$email = email($row["uid2"]);
if (!in_array($emails[$row["uid2"]], $email, true))
$emails[$row["uid2"]][] = $email;
}
}
// shouldn't need to do array_unique with the if-statements above
var_dump($numbers);
var_dump($emails);
EDIT Answering question from comments:
Based on the logic you are using, the result will probably be the same. My example above will allow this scenario for $numbers, while your example will not:
array(2)
{
[123] => array(2)
{
[0] => 1234567890, // same as $numbers[456][0]
[1] => 9876543210
},
[456] => array(1)
{
[0] => 1234567890 // same as $numbers[123][0]
}
}
But based on the way that $number is generated based on $uid2, you probably won't see any difference. What I mean is that, if number(123) returns 1234567890, then number(456) probably will not return 1234567890, so you probably wouldn't ever run into a scenario where you would see a difference.
EDIT 2 After thinking about this some more, I'm betting that your code can be greatly simplified to this:
// only selecting uid2 from the table
$result = mysql_query("SELECT DISTINCT uid2 FROM table1 WHERE uid1 = '{$uid1}' ORDER BY /* insert column name here, followed by ASC or DESC */");
$output = array();
while (list($uid2) = mysql_fetch_row($result))
{
$number = number($uid2);
if (strlen($number) > 9)
$output[$uid2]["number"] = $number;
else
$output[$uid2]["email"] = email($uid2);
}
var_dump($output);
LAST EDIT (Hopefully):
// only selecting uid2 from the table
$result = mysql_query("SELECT DISTINCT uid2 FROM table1 WHERE uid1 = '{$uid1}' ORDER BY /* insert column name here, followed by ASC or DESC */");
$numbers = $emails = array();
while (list($uid2) = mysql_fetch_row($result))
{
$number = number($uid2);
if (strlen($number) > 9)
$numbers[$uid2] = $number;
else
$emails[$uid2] = email($uid2);
}
var_dump($numbers);
var_dump($emails);
foreach($array as $uid2){
$uid2 = $array['uid2'];
makes no sense. Loop over each element in $array and assign the elements to $uid2 sequentially, then kill that assigned and pull a fixed value from the same array?
As well, where is $yfbid set? From your code sample, it's undefined, and you try to convert this undefined value to a number, then check the string length of that number. Why not just strlen($yfbid) and let PHP handle the conversion?

Categories