PDO/SQL Select * from function showing no results [closed] - php

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
I am using this PHP function:
if(!function_exists("SelectQuery2")) {
function SelectQuery2($table, array $where) {
//PDO
global $pdo_conn;
$sql = "SELECT * FROM `$table` ";
$values = null;
if ($where) {
$sql .= "WHERE " . implode(" AND ", array_map(function ($c) { return "`$c` = ?"; }, array_keys($where)));
$values = array_values($where);
}
$stmt = $pdo_conn->prepare($sql);
$stmt->execute($values);
}
}
And I am calling it here:
$where = array(" ticketnumber = ".$_GET["seq"]." ");
$ticket=SelectQuery2("tickets", $where);
$ticket = $ticket[0];
but I am not getting any results.
I use $ticket["column_name"] to show my results.
What could be the problem?

If you are creating such a complicated function, why don't you check first the returning values?
I did this test
function SelectQuery2($table, array $where) {
$sql = "SELECT * FROM `$table` ";
$values = null;
if ($where) {
$sql .= "WHERE " . implode(" AND ", array_map(function ($c) { return "`$c` = ?"; }, array_keys($where)));
$values = array_values($where);
}
return array($sql, $values);
}
$where = array("ticketnumber = asdasdasd", "ticketbla = dfgdfgdfg");
$ticket=SelectQuery2("tickets", $where);
var_dump($ticket);
And the output is:
array (size=2)
0 => string 'SELECT * FROM `tickets` WHERE `0` = ? AND `1` = ?' (length=49)
1 =>
array (size=2)
0 => string 'ticketnumber = asdasdasd' (length=26)
1 => string 'ticketbla = dfgdfgdfg' (length=21)
So, are you expecting your column names to be 0 and 1, instead of the real column names?
I guess - no. You need array_keys() in the closure, but you are passing array $where as numeric array (with only values), so the keys are 0, 1, etc...
You would need an associative array, which passes the column name as key because the closure requires it this way, and the value as value because the PDOStatement::execute requires it this way, when not using named placeholders, but ?.
http://www.php.net/manual/en/pdostatement.execute.php
The appropriate example from here is:
<?php
/* Execute a prepared statement by passing an array of insert values */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < ? AND colour = ?');
$sth->execute(array($calories, $colour));
?>
Which says that execute() here accepts the values 150 and red as array values (array(150, 'red')) which in your case will be (array($_GET["seq"])).
The right example of the passed array should be:
$where = array('ticketnumber' => $_GET["seq"]);
If you need multiple column names and values, just add more key => value pairs.
$where = array(
'ticketnumber' => $_GET["seq"],
'ticket_owner' => $_GET['owner'],
'ticket_etc' => $_GET['etc']
);
But, here you have some disadvantages:
Your function is strictly complicated to rules col1 = val1 AND col2 = val2 AND.... Your SELECT query is only for simple data extraction. You barely can use SQL functions, HAVING clauses, date operators, BETWEEN, nor <; >.
That's why I would not suggest this variant of wrapping PDO. At its nature, PDO is already some kind of wrapper. At least don't make the query string dynamic. You can try to wrap the prepare(), execute(), fetch(), but stop there. Also don't do it in procedural PHP functions. It will just increase the agony of bad design, such as using globals, because your PDO object is not shared between them.

Related

PHP implode in bind_param

I'm trying to create an advanced search in php. The inputs are not required, users can decide if they want to search for a manufacturer or just set the minimum price, etc. I'm trying to save the "s" and "i" for the bind_param in an array, and the variables in another array, then implode them in the bind_param part.
This is where I got the problem. The $params implode works fine, but when I'm trying to implode the $vars array, I get the error message that says "Only variables should be passed by reference".
It's because if I push a variable to my array, it stores it's value and not the variable itself. I've tried to push them as strings, like '$example', but in this case, when I implode it, got the same message because it's a string.
So, how should I store them in the array to be able to use them in the bind_param?
In this example I show only 2 inputs, but ofc I have a lot more.
if ($_SERVER['REQUEST_METHOD'] == 'GET' && isset($_GET['search_button'])) {
$params[] = "i";
$vars[] = '$status';
$sql_search = 'SELECT m.*, u.premium, u.avatar FROM motorcycles m INNER JOIN users u ON u.id = m.userid WHERE status = ?';
if (isset($_GET['manufacturer_search']) && $_GET['manufacturer_search'] !== "") {
$manufacturer_search = $_GET['manufacturer_search'];
$sql_search .= " AND manufacturer LIKE ?";
array_push($params, 's');
array_push($vars, '$manufacturer_search');
}
if (isset($_GET['min_price']) && $_GET['min_price'] !== "") {
$min_price = $_GET['min_price'];
$sql_search .= " AND price >= ?";
array_push($params, 'i');
array_push($vars, '$min_price');
}
$sql_search .= " ORDER BY u.premium DESC LIMIT ?, ?";
array_push($params, 'ii');
array_push($vars, '$this_page_first_result', '$results_per_page');
$stmt_search = $link->prepare($sql_search);
$stmt_search->bind_param(implode("", $params), implode(",", $vars));
$stmt_search->execute();
$result = $stmt_search->get_result();
}
You should provide the variables you want separately as the last parameter of bind_params, what you are doing is creating a string of all your variables and passing that.
Change
$stmt_search->bind_param(implode("", $params), implode(",", $vars));
To
$stmt_search->bind_param(implode("", $params), ...$vars );
And PHP will take all entries inside your $vars array and pass them as separate parameters of the function.
For more information on this see the Documentation of bind_param, PHP's introduction of the splat operator here and here and some extra information on stack overflow.

Dynamic select mysqli query with dynamic parameters returns error doesn't match number of bind variables [duplicate]

This question already has answers here:
Use an array in a mysqli prepared statement: `WHERE .. IN(..)` query [duplicate]
(8 answers)
Closed 11 months ago.
I'm trying to create a select query with dynamic where clause and dynamic parameters but I always get error :
Warning: mysqli_stmt::bind_param(): Number of elements in type
definition string doesn't match number of bind variables
Which I sincerely do not understand since it seems the count is alright. So this is what the code really looks like in its rude format. I can't see what I'm doing wrong.
//get variables
$mediaArray ='Facebook,Twitter,Twitch,';
$otherMedia = 'House';
//convert string to array
$socialArray = explode(',', $mediaArray)
//declare some variables to be used later
$andwhere = '';
$bp = '';
$socialmarray = ''
//get every value from array of social media
foreach($socialArray as $socialmedia){
$socialmarray .=$socialmedia.',';
$andwhere .= " AND socialmedianame=?";
$bp .='s';
}
//test strings
echo $wheres = $andwhere;//AND socialmedianame=? AND socialmedianame=? AND socialmedianame=?
echo $bip = $bp.'s';//ssss
echo $validarayy = rtrim($socialmarray,',');//Facebook,Twitter,Twitch
//select query
$selectquery = $conn->prepare("select * from mediaservices where socialmedianame=? $wheres");
$selectquery->bind_param("$bip",$otherMedia,$validarayy);
$selectquery->execute();
$resultquery = $selectquery->get_result();
Because:
You are using user-supplied data, you must assume that your query is vulnerable to a malicious injection attack and
the amount of data that is to be built into the query is variable/indefinite and
you are only writing conditional checks on a single table column
You should use a prepared statement and merge all of the WHERE clause logic into a single IN statement.
Building this dynamic prepared statement is more convoluted (in terms of syntax) than using pdo, but it doesn't mean that you need to abandon mysqli simply because of this task.
$mediaArray ='Facebook,Twitter,Twitch,';
$otherMedia = 'House';
$media = array_unique(explode(',', $mediaArray . $otherMedia));
$count = count($media);
$conn = new mysqli("localhost", "root", "", "myDB");
$sql = "SELECT * FROM mediaservices";
if ($count) {
$stmt = $conn->prepare("$sql WHERE socialmedianame IN (" . implode(',', array_fill(0, $count, '?')) . ")");
$stmt->bind_param(str_repeat('s', $count), ...$media);
$stmt->execute();
$result = $stmt->get_result();
} else {
$result = $conn->query($sql);
}
foreach ($result as $row) {
// access values like $row['socialmedianame']
}
For anyone looking for similar dynamic querying techniques:
SELECT with dynamic number of LIKE conditions
INSERT dynamic number of rows with one execute() call
In your query:
$selectquery = $conn->prepare("select * from mediaservices where socialmedianame=? $wheres");
The ? represents one parameter to pass in, and the evaluation of $wheres adds another three, giving you four total parameters.
bind_param() should take a string representing the types of the variables to insert as the first parameter, and the variables themselves as the subsequent parameters.
In your bind:
$selectquery->bind_param("$bip",$otherMedia,$validarayy);
$bip evaluates to ssss and $otherMedia is a single string ("House"). You might expect $validarayy to be three strings, but rtrim() returns a string. Thus, it is only one string ("Facebook,Twitter,Twitch"). You pass through two variables when the query is expecting four:
$conn->prepare("select * from mediaservices where socialmedianame=House AND socialmedianame=Facebook,Twitter,Twitch AND socialmedianame=? AND socialmedianame=? AND socialmedianame=?"
To correct this, you'll want to convert $validarayy back to an array, and use the index for the various inputs:
$socialmarray2 = explode(',', $validarayy);
$selectquery->bind_param("$bip", $otherMedia, $socialmarray2[0], $socialmarray2[1], $socialmarray2[2]);
Also note that your sample code has a few missing semicolons; you'll need to fix these in order for your code to work correctly.
This can be seen working here.
Finally, note that even if you were to split the three strings out correctly, the selection of ... AND socialmedianame=Facebook AND socialmedianame=Twitter AND socialmedianame=Twitch will never match any results; socialmedianame can only contain one value. You're probably looking to substitute your AND statements with OR statements.

PHP: "Value Expected To Be A Reference" with Call_User_Func and Bind Params [duplicate]

This question already has answers here:
mysqli bind_param() expected to be a reference, value given
(3 answers)
Closed 11 months ago.
I have been trying to bind an array with a prepared statement for days, I have managed to create the number of ?,? and the i,i these are represented in my code as $params and $type. The problem comes when using call_user_func_array with my code since I haven't managed to make it work, I have tried multiple answers from this site but I haven't got it working.
The full query code is:
$params = implode(',', array_fill(0, count($greaterThan), '?'));
$stmt11 = $mysqli->prepare("SELECT nombre FROM usuarios WHERE id IN (".$params.")");
$type = implode('', array_fill(0, count($greaterThan), 'i'));
$param = implode(",", $greaterThan);
call_user_func_array(array($stmt11, "bind_param"), array_merge(array($type), $greaterThan));
print_r(error_get_last());
$stmt11->execute();
$stmt11->store_result();
$stmt11->bind_result($nombresmayores);
$arraynombresmayores = array();
$stmt11->store_result();
while($stmt11->fetch()){
$arraynombresmayores[] = $nombresmayores;
}
Where $param are the values separated by comas (just in case you need it). The array I'm trying to bind is $greaterThan, the query works perfectly because I have made some debugging. The error the program outputs is:
Parameter 2 to mysqli_stmt::bind_param() expected to be a reference, value given
Finally, the content of the array is:
array(2) {
[0]=>
int(2)
[1]=>
int(4)
}
if your php version is not outdated you can make it much simper
$params = implode(',', array_fill(0, count($greaterThan), '?'));
$stmt = $mysqli->prepare("SELECT nombre FROM usuarios WHERE id IN ($params)");
$types = str_repeat('i',count($greaterThan));
$stmt->bind_param($types, ...$greaterThan);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($nombresmayores);
$arraynombresmayores = array();
$stmt->store_result();
while($stmt->fetch()){
$arraynombresmayores[] = $nombresmayores;
}
but better yet use PDO:
$params = implode(',', array_fill(0, count($greaterThan), '?'));
$stmt = $pdo->prepare("SELECT nombre FROM usuarios WHERE id IN ($params)");
$stmt->execute($greaterThan);
$arraynombresmayores = $stmt->fetchAll(PDO::FETCH_COLUMN);
just compare this neat code with that awful mess you have now.
I have method i use in an class i made to work mysqli in a fashinable way. The key reside in the foreach loop: Basicaly you create a copy of your array with another one by reference and then bind the referenced array. Hope this helps.
UPDATED :
in your case you need to create an array with your string $type and the array of greateThan. From the temp to the bind array, make sure the keys stay the sames and don't unset your data before having called the bond_param
$type = implode('', array_fill(0, count($greaterThan), 'i'));
//$param = implode(",", $greaterThan); //Not needed
//merge the 'type' as an array and the variables to pass
$temp_params= array_merge([$type],$greaterThan);
//Create a referenced array but dont'reference the 'type' argument
foreach($temp_params as $k=>$v){
if($k===0){
$bind_params[$k]=$temp_params[$k];
}else{
$bind_params[$k]=&$temp_params[$k];
}
}
//use the referenced array
call_user_func_array(array($stmt11, "bind_param"), array_merge(array($type), $bind_params));
Just try to assign that expression to a variable, and bind a variable. Expressions cannot be used as references, so you have to pass a variable.

Why does PDO 'execute' work irrespective of whether colon is included on a bound parameter variable?

I'm working on an application which uses PDO.
I've noticed that when binding parameters the query still works irrespective of whether I use or omit a colon on the bound parameter variable.
Example:
$sql = "SELECT * FROM `" . $this->table . "` WHERE id = :id LIMIT 1";
$stmt = $this->ci->db->prepare($sql);
$result = $stmt->execute([
"id" => $id,
]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
Gives exactly the same result as:
$sql = "SELECT * FROM `" . $this->table . "` WHERE id = :id LIMIT 1";
$stmt = $this->ci->db->prepare($sql);
$result = $stmt->execute([
":id" => $id,
]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
The difference (which might be hard to spot) is "id" => $id vs ":id" => $id
So, which should be used? And is it the expected behaviour for them to give the same output?
A quick glance at the PHP source code turns up this code in pdo_stmt.c (this is the current master branch, so PHP 7.1, but I imagine it's fundamentally the same in all versions):
if (param->name) {
if (is_param && ZSTR_VAL(param->name)[0] != ':') {
zend_string *temp = zend_string_alloc(ZSTR_LEN(param->name) + 1, 0);
ZSTR_VAL(temp)[0] = ':';
memmove(ZSTR_VAL(temp) + 1, ZSTR_VAL(param->name), ZSTR_LEN(param->name) + 1);
param->name = temp;
} else {
param->name = zend_string_init(ZSTR_VAL(param->name), ZSTR_LEN(param->name), 0);
}
}
Roughly, the key part can be read as:
if ( char 0 of param->name is not ':' ) {
set param->name to ':' concatenated with param->name
}
So, the form with the : on the front is the "correct" form, but if you pass any string without a : prefix, the PDO code will add one on internally, just to be user-friendly.
Note that this has nothing to do with how arrays work: the PHP array is just a way of getting a bunch of name-value pairs into the PDO code. It's the same as if an API accepted either the US spelling 'color' or the UK spelling 'colour' for a parameter; they're not "the same key", but the API can decide they have the same meaning.
In practice, the answer remains "use whichever you prefer".
Why does PDO query work irrespective
Because it's handy.
So, which should be used?
One that suits you best.
And is it the expected behaviour for them to give the same output?
Yes.

PDO Use MySQL's NOW()

How would I use MySQL's NOW() in PDO prepared statements, or how would I workaround using it while keeping in mind that possibly the Apache Server and Database Server might have either a slightly current time mismatch (few seconds), or in rare occasions might be timezones apart?
I have the following function in my code:
try {
$dbh->insert("users", array(
"email" => $email,
"password" => $password,
"salt" => $salt,
"ingame" => $ingame,
"kiosk" => $kiosk
));
} catch (PDOException $ex) {
error($ex);
}
Which calls:
/**
* Inserts data into a table. Data must be given in key-value pairs.
*
* Example: $dbh->insert("table", array(
* "data1" => $data1,
* "data2" => $data2
* );
*
* #param type $table The table to insert to
* #param type $keyvaluepairs The key-value pairs.
* #return type The statement that this query produced.
*/
public function insert($table, $keyvaluepairs) {
$sql = "INSERT INTO `{$table}` (";
$values_sql = ") VALUES(";
$values = array();
foreach ($keyvaluepairs as $key => $value) {
$sql .= "`${key}`, ";
$values_sql .= "?, ";
$values[] = $value;
}
$query = substr($sql, 0, -2).substr($values_sql, 0, -2).")";
return $this->query($query, $values);
}
Which calls:
//TODO update documentation to show it also handles associative arrays with bindvalue
/**
* Can be called to create a query. Use either unnamed or named placeholders for the prepared statements.
*
* Example: $dbh->query("INSERT INTO table (data1, data2) VALUES(?, ?)", array($data1, $data2));
*
* #param type $query The input query, including unnamed or named placeholders
* #param type $values The input values. If it's not an array, then it will be an one-element array
* #return type The statement constructed by this query
*/
public function query($query, $values = array()) {
if (!is_array($values)) {
$values = array($values);
}
$statement = $this->dbh->prepare($query);
$statement->setFetchMode(PDO::FETCH_OBJ);
$i = 1;
if (is_assoc($values)) {
foreach ($values as $key => $value) {
$statement->bindValue($key, $value);
}
}
else {
foreach ($values as $value) {
$statement->bindValue($i++, $value);
}
}
$statement->execute();
return $statement;
}
Where I have the function:
function is_assoc($array) {
return (bool)count(array_filter(array_keys($array), 'is_string'));
}
So the deal here is that I cannot use custom MySQL queries for the inserts as I've encapsulated those for the sake of easyness, but I still want to be able to insert NOW() without making use of TIMESTAMP / CURRENT_TIMESTAMP().
I hope you understand that this question requires an explained answer as I have already read the 'normal' answers and shown that they do not satisfy my needs.
UPDATE: I have added const SQL_NOW = 1; to my DBH class, however now I want to modify the insert to something like this:
public function insert($table, $keyvaluepairs) {
$sql = "INSERT INTO `{$table}` (";
$values_sql = ") VALUES(";
$values = array();
foreach ($keyvaluepairs as $key => $value) {
if ($value == SELF::SQL_NOW) {
$sql .= "NOW(), ";
}
else {
$sql .= "`${key}`, ";
$values_sql .= "?, ";
$values[] = $value;
}
}
$query = substr($sql, 0, -2).substr($values_sql, 0, -2).")";
return $this->query($query, $values);
}
Which may be a suitable solution, however I cannot use 1 as SQL_NOW value, as it would then fail if I'd want to insert an integer 1. If I go with this solution, what value would SQL_NOW then have? Is it even possible to give it no value?
Excellent question!
This is a perfect example that clearly shows why all these numerous insert(), update() and all other stuff, intended to substitute SQL, are wrong by design.
NOW() is not the only issue you will face with. Just because SQL is not that silly a language as it seems at first glance. And it was invented on purpose. And it's reliability was proven for decades. Means it is not that easy to write whole SQL just as an exercise while learning PHP.
So, the best thing you could do is to keep SQL as is.
What you really, really need is a helper function or two. To automate the repetitive tasks. That is ALL. While SQL have to be left as is. Which will let you to use ALL it's power including use of functions, functions with arguments(!), query modifiers, such as 'INSERT IGNORE' or extended syntax like JOINS.
In case you are using PDO, it is not that simple but feasible.
However, the only proper solution is to use a placeholder of the special type for the SET statement.
$data = array(
"email" => $email,
"password" => $password,
"salt" => $salt,
"ingame" => $ingame,
"kiosk" => $kiosk,
);
$dbh->query("INSERT INTO ?n SET reg = NOW(), ?u","users", $data);
Just one single line to solve all that mess and many, many, many other issues.
Just one single query() method to run any query you want, even REPLACE INTO.
Update.
Just look what are you doing!
You were planning your class to simplify things. But at the moment you are making it more and more complex. In the end you will have a hulking giant which inconsistent syntax for the numerous exceptions scarcely understood even by it's creator and noone else. And which still don't let you run some queries.
Please rethink your design before it's too late.
You can write a wrapper that does this if you pass it an additional argument containing all the column=>SQLfunction values you need.
Mine looks like this:
function pInsertFunc($action, $table, $values, $sqlfunctions)
{
global $pdb;
// There's no way to pass an SQL function like "NOW()" as a PDO parameter,
// so this function builds the query string with those functions. $values
// and $sqlfunctions should be key => value arrays, with column names
// as keys. The $values values will be passed in as parameters, and the
// $sqlfunction values will be made part of the query string.
$value_columns = array_keys($values);
$sqlfunc_columns = array_keys($sqlfunctions);
$columns = array_merge($value_columns, $sqlfunc_columns);
// Only $values become ':paramname' PDO parameters.
$value_parameters = array_map(function($col) {return (':' . $col);}, $value_columns);
// SQL functions go straight in as strings.
$sqlfunc_parameters = array_values($sqlfunctions);
$parameters = array_merge($value_parameters, $sqlfunc_parameters);
$column_list = join(', ', $columns);
$parameter_list = join(', ', $parameters);
$query = "$action $table ($column_list) VALUES ($parameter_list)";
$stmt = $pdb->prepare($query);
$stmt->execute($values);
}
Use it like this:
$values = array(
'ID' => NULL,
'name' => $username,
'address' => $address,
);
$sqlfuncs = array(
'date' => 'NOW()',
);
pInsertFunc("INSERT INTO", "addresses", $values, $sqlfuncs);
The query string that results looks like this:
INSERT INTO addresses (ID, name, address, date) VALUES (:ID, :name, :address, NOW())

Categories