I have a variable number of parameters to insert and I got the error (2031) No data supplied for parameters in prepared statement after the warning Number of variables doesn't match number of parameters in prepared statement in SaveIntermediateData.php5 on line 49.
$link = new mysqli( DB_HOST, DB_USER, DB_PASSWORD, DB_NAME );
if( ! $link ) {
echo "<h1>new mysqli() failed!</h1>";
exit( 0 );
}
$queryText =
"CREATE TABLE IF NOT EXISTS visitors (".
"id VARCHAR( 512) CHARACTER SET ASCII NOT NULL,".
"name VARCHAR( 80) CHARACTER SET ASCII NOT NULL,".
"value VARCHAR(4096) NOT NULL,".
"PRIMARY KEY ( `id`, `name` )".
")";
$link->query( $queryText );
$queryText = "INSERT INTO visitors (id,name,value) VALUES ";
foreach( $_POST as $name => $value ) {
$queryText .= '(?,?,?),';
}
$queryText = substr( $queryText, 0, -1 );
$queryText .= ' ON DUPLICATE KEY UPDATE name = VALUES( name ), value = VALUES( value )';
$id = session_id();
$stmt = $link->prepare( $queryText );
if( $stmt ) {
$param_nr = 1;
foreach( $_POST as $name => $value ) {
$stmt->bind_param( 'sss', $id, $name, $value ); //<<<<<<<<< line 49
}
if( $stmt->execute()) {
echo '<h1>OK</h1>';
}
else {
echo "<h1>(".$stmt->errno.") ".$stmt->error."</h1>";
}
}
else {
echo "<h1>".$link->error."</h1>";
}
$link->close();
I believe only the last bind_param is taken in account. In Java, it's possible to use an index to bind a parameter but I don't know such a method with mysqli.
I may create a full text query but I prefer use binding to avoid injection.
You can only call bind_param once, so you'll have to add all the params you want into an array, then call it via call_user_func_array.
Try this:
$params = array('');
foreach( $_POST as $name => $value ) {
$params[0] .= 'sss';
array_push($params, $id, $name, $value);
}
call_user_func_array(array($stmt, 'bind_param'), $params);
if( $stmt->execute()) {
echo '<h1>OK</h1>';
}
Related
I am trying to write prepared statement for user input. parameter numbers are variable depends on user input. Oam trying this code
PHP code:
$string = "my name";
$search_exploded = explode( " ", $string );
$num = count( $search_exploded );
$cart = array();
for ( $i = 1; $i <= $num; $i ++ ) {
$cart[] = 's';
}
$str = implode( '', $cart );
$inputArray[] = &$str;
$j = count( $search_exploded );
for ( $i = 0; $i < $j; $i ++ ) {
$inputArray[] = &$search_exploded[ $i ];
}
print_r( $inputArray );
foreach ( $search_exploded as $search_each ) {
$x ++;
if ( $x == 1 ) {
$construct .= "name LIKE %?%";
} else {
$construct .= " or name LIKE %?%";
}
}
$query = "SELECT * FROM info WHERE $construct";
$stmt = mysqli_prepare( $conn, $query );
call_user_func_array( array( $stmt, 'bind_param' ), $inputArray );
if ( mysqli_stmt_execute( $stmt ) ) {
$result = mysqli_stmt_get_result( $stmt );
if ( mysqli_num_rows( $result ) > 0 ) {
echo $foundnum = mysqli_num_rows( $result );
while( $row = mysqli_fetch_array( $result, MYSQLI_ASSOC ) ) {
echo $id = $row['id'];
echo $name = $row['name'];
}
}
}
When I print_r($inputArray), the output is:
Array ( [0] => ss [1] => my [2] => name )
There is no error showing in error log.
What is wrong?
The % wrapping goes around the parameters, not the placeholders.
My snippet will be using object-oriented mysqli syntax instead of the procedural syntax that your code demonstrates.
First you need to set up the necessary ingredients:
the WHERE clause expressions -- to be separated by ORs
the data types of your values -- your values are strings, so use "s"
the parameters to be bound to the prepared statement
I am going to combine #2 and #3 into one variable for simpler "unpacking" with the splat operator (...). The data type string must be the first element, then one or more elements will represent the bound values.
As a logical inclusion, if you have no conditions in your WHERE clause, there is no benefit to using a prepared statement; just directly query the table.
Code: (100% Tested / Successful Code)
$string = "my name";
$conditions = [];
$parameters = [''];
foreach (array_unique(explode(' ', $string)) as $value) {
$conditions[] = "name LIKE ?";
$parameters[0] .= 's';
$parameters[] = "%{$value}%";
}
// $parameters now holds ['ss', '%my%', '%name%']
$query = "SELECT * FROM info";
if ($conditions) {
$stmt = $conn->prepare($query . ' WHERE ' . implode(' OR ', $conditions));
$stmt->bind_param(...$parameters);
$stmt->execute();
$result = $stmt->get_result();
} else {
$result = $conn->query($query);
}
foreach ($result as $row) {
echo "<div>{$row['name']} and whatever other columns you want</div>";
}
For anyone looking for similar dynamic querying techniques:
SELECT with dynamic number of values in IN()
INSERT dynamic number of rows with one execute() call
Write a generic query handler and pass it your query, the array of parameters, and the list of parameter types. Get back an array of results or messages. Here's my own personal version for mysqli (I mostly use PDO, but have a similar function set up for that as well). Do the same for inserts, updates, and deletes. Then simply maintain your one library and use it for everything you do :) Note that if you start with this, you'll probably want to do a better job of dealing with connection errors, etc.
<?php
// this is normally in an include() file
function getDBConnection(){
// your DB credentials
$hostname="127.0.0.1";
$username="ausername";
$password="supersecret";
$database="some_db_name";
$con = new mysqli($hostname, $username,$password, $database);
if($con->connect_error) {
return false;
}
return $con;
}
// generic select function.
// takes a query string, an array of parameters, and a string of
// parameter types
// returns an array -
// if $retVal[0] is true, query was successful and returned data
// and $revVal[1...N] contain the results as an associative array
// if $retVal[0] is false, then $retVal[1] either contains the
// message "no records returned" OR it contains a mysql error message
function selectFromDB($query,$params,$paramtypes){
// intitial return;
$retVal[0]=false;
// establish connection
$con = getDBConnection();
if(!$con){
die("db connection error");
exit;
}
// sets up a prepared statement
$stmnt=$con->prepare($query);
$stmnt->bind_param($paramtypes, ...$params);
$stmnt->execute();
// get our results
$result=$stmnt->get_result()->fetch_all(MYSQLI_ASSOC);
if(!$result){
$retVal[1]="No records returned";
}else{
$retVal[0]=true;
for($i=0;$i<count($result);$i++){
$retVal[]=$result[$i];
}
}
// close the connection
$con->close();
return $retVal;
}
$myusername=$_POST['username'];
$mypassword=$_POST['password'];
// our query, using ? as positional placeholders for our parameters
$q="SELECT useridnum,username FROM users WHERE username=? and password=?";
// our parameters as an array -
$p=array($myusername,$mypassword);
// what data types are our params? both strings in this case
$ps="ss";
// run query and get results
$result=selectFromDB($q,$p,$ps);
// no matching record OR a query error
if(!$result[0]){
if($result[1]=="no records returned"){
// no records
// do stuff
}else{
// query error
die($result[1]);
exit;
}
}else{ // we have matches!
for($i=1;$i<count($result);$i++){
foreach($result[$i] as $key->$val){
print("key:".$key." -> value:".$val);
}
}
}
?>
I'm working with PHP PDO and I have the following problem:
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in /var/www/site/classes/enterprise.php on line 63
Here is my code:
public function getCompaniesByCity(City $city, $options = null) {
$database = Connection::getConnection();
if(empty($options)) {
$statement = $database->prepare("SELECT * FROM `empresas` WHERE `empresas`.`cidades_codigo` = ?");
$statement->bindValue(1, $city->getId());
}
else {
$sql = "SELECT * FROM `empresas`
INNER JOIN `prods_empresas` ON `prods_empresas`.`empresas_codigo` = `empresas`.`codigo` WHERE ";
foreach($options as $option) {
$sql .= '`prods_empresas`.`produtos_codigo` = ? OR ';
}
$sql = substr($sql, 0, -4);
$sql .= ' AND `empresas`.`cidades_codigo` = ?';
$statement = $database->prepare($sql);
echo $sql;
foreach($options as $i => $option) {
$statement->bindValue($i + 1, $option->getId());
}
$statement->bindValue(count($options), $city->getId());
}
$statement->execute();
$objects = $statement->fetchAll(PDO::FETCH_OBJ);
$companies = array();
if(!empty($objects)) {
foreach($objects as $object) {
$data = array(
'id' => $object->codigo,
'name' => $object->nome,
'link' => $object->link,
'email' => $object->email,
'details' => $object->detalhes,
'logo' => $object->logo
);
$enterprise = new Enterprise($data);
array_push($companies, $enterprise);
}
return $companies;
}
}
It looks like you're trying to build a long(?) series of 'or' comparisons: if (x=1) or (x=2) or (x=3) etc.... You may find it easier to replace it with:
$cnt = count($options);
if ($cnt > 0) {
$placeholders = str_repeat(', ?', $cnt - 1);
$sql .= 'WHERE '`prods_empresas`.`produtos_codigo` IN (?' . $placeholders . ')';
}
which, if there were 5 options, would give you
WHERE prods_empresas.produtos_condigo IN (?, ?, ?, ?, ?)
And then do the values binding with:
$pos = 1;
foreach ($options as $option) {
$statement->bindValue($pos, $option->getId());
$pos++
}
You have a mismatch between the amount of bound parameters and the amount of binds in the SQL. Double check that the amount of ? and the amount of bound parameters is the same.
Additionally, HY093 will show up if you have tried to bind a parameter that does not exist:
$stmt = "INSERT INTO table VALUES (:some_value)";
$stmt->bindValue(':someValue', $someValue, PDO::PARAM_STR);
See that :some_value does not match :someValue! The fix is:
$stmt = "INSERT INTO table VALUES (:some_value)";
$stmt->bindValue(':some_value', $someValue, PDO::PARAM_STR);
Positional parameters in SQL start at 1. You're handling this by binding to position $i+1 in your $options loop.
But then you bind the last parameter for cidades_codigo to position count($options), which overwrites the last parameter set in the $options loop.
You need to bind the last parameter to position count($options)+1.
FWIW, you don't need to bindValue() at all. It's easier to just pass an array of parameters to execute(). Here's how I'd write this function:
public function getCompaniesByCity(City $city, $options = null) {
$database = Connection::getConnection();
$sql = "SELECT * FROM `empresas` WHERE `empresas`.`cidades_codigo` = ?"
$params = array();
$params[] = $city->getId();
if ($options) {
$sql .= " AND `prods_empresas`.`produtos_codigo` IN ("
. join(",", array_fill(1, count($options), "?") . ")";
foreach ((array)$options as $option) {
$params[] = $option->getId();
}
}
$statement = $database->prepare($sql);
echo $sql;
$statement->execute($params);
. . .
Also be sure to check the return value of prepare() and execute(), it will be false if there's an error, and you need to check for that and report the error. Or else enable PDO to throw exceptions on error.
I was running into this problem due to having extra entries in the named parameter mapping array passed to PDO::Statement->execute()
$args=array (":x" => 17 );
$pdo->prepare("insert into foo (x) values (:x)");
$pdo->execute($args); // success
$args[':irrelevant']=23;
$pdo->execute($args) // throws exception with HY093
Since you have made $i+1 in the loop so count($options) would equal the last $i+1 which makes a duplicate binding.Try
foreach($options as $i => $option)
{
$statement->bindValue($i + 1, $option->getId());
}
$statement->bindValue(count($options)+1, $city->getId());
In MySQL we used the mysqli_stmt_bind_param to bind parameters.
What should I use to bind parameters in sqlsrv?
$sql = "SELECT * FROM dbo.[user] WHERE username = ? and password = ?";
$stmt = sqlsrv_prepare($conn, $sql, $params);
if($stmt === false){
die( print_r( sqlsrv_errors(), true));
}
How can I bind this parameters? This is a php file and I need to bind them without pdo.
You don't explicitly bind the parameters by using another function, you do it when preparing the statement.
See the example from the manual.
$sql = "UPDATE Table_1
SET OrderQty = ?
WHERE SalesOrderID = ?";
// Initialize parameters and prepare the statement.
// Variables $qty and $id are bound to the statement, $stmt.
$qty = 0; $id = 0;
$stmt = sqlsrv_prepare( $conn, $sql, array( &$qty, &$id));
if( !$stmt ) {
die( print_r( sqlsrv_errors(), true));
}
// Set up the SalesOrderDetailID and OrderQty information.
// This array maps the order ID to order quantity in key=>value pairs.
$orders = array( 1=>10, 2=>20, 3=>30);
// Execute the statement for each order.
foreach( $orders as $id => $qty) {
// Because $id and $qty are bound to $stmt1, their updated
// values are used with each execution of the statement.
if( sqlsrv_execute( $stmt ) === false ) {
die( print_r( sqlsrv_errors(), true));
}
}
I am attempting to set up a class for the simplification of MySQLi queries using the mysqli class. However, when I set up my class to accept an instance of mysqli, I receive this error:
Undefined variable: dbobj in C:\wamp\www\inc\classes\ezsql.class.php on line 93
I've never really done dependency injection before, plus my coding skills aren't with me today, so syntax mistakes might be plentiful.
Here's the class I've created:
<?php
/*/////////////////////////////////////////////////////////////////////////////////////////////
EZSQL
A PHP class that utilizes the PDO MySQLi DB class to make database querying easier and quicker.
/////////////////////////////////////////////////////////////////////////////////////////////*/
class EZSQL {
public $dbobj;
public function __construct( mysqli $dbobject ) {
echo "var_dunp before making this->dbobj\n";
var_dump( $dbobj );
if ( is_object( $dbobject ) ) {
$this->dbobj = $dbobject;
} else {
// Just in case...
die( "EZSQL Error #0: EZSQL must be initialized with a PDO MySQLi DB instance." );
}
echo "var_dunp after making this->dbobj\n";
var_dump( $this->dbobj );
}
public function select( $exp, $table, $where ) {
// Make $where optional
if ( isset( $where ) ) {
$queryString = "SELECT {$exp} FROM {$table} WHERE {$where}";
} else {
$queryString = "SELECT {$exp} FROM {$table}";
}
if ( $returnObj = $this->dbobj->query( $queryString ) ) {
$return = $returnObj->fetch_assoc();
return $return;
} else {
// Some error handling
die( "[EZSQL]: SQL SELECT query error #{$this->dbobj->errno}: {$this->dbobj->error}" );
}
}
public function insert( $tab, $cols, $values ) {
// Create the general query string format
$queryString = "INSERT INTO {$tab} (";
$colsCt = count( $cols );
foreach ( $cols as $key=>$col ) {
if ( $key != ( $colsCt - 1 ) ) {
$queryString .= "{$col}, ";
} else {
$queryString .= "{$col})";
}
}
// More general formatting
$queryString .= " VALUES (";
$valCt = count( $values );
foreach( $values as $key=>$value ) {
if ( $key != ( $valCt - 1 ) ) {
$queryString .= "{$value}, ";
} else {
$queryString .= "{$value})";
}
}
if ( $query = $this->dbobj->prepare( $queryString ) ) {
$query->execute();
} else {
die( "[EZSQL]: SQL INSERT query prepare error #{$this->dbobj->errno}: {$this->dbobj->error}" );
}
}
public function createTable( $name, $ino, $colData ) {
// Create the general query string format
$queryString = "CREATE TABLE";
if ( $ino ) {
$queryString .= " IF NOT EXISTS";
}
$queryString .= " {$name}";
$colCt = count( $colData );
$queryString .= " (";
$iter = 1;
foreach ( $colData as $name=>$type ) {
$iter++;
if ( $iter != ( $colCt ) ) {
$queryString .= " {$name} {$type},";
} else {
$queryString .= " {$name} {$type});";
}
}
if ( !$this->dbobj->query( $queryString ) ) {
die( "[EZSQL]: SQL CREATE TABLE query error #{$this->dbobj->errno}: {$this->dbobj->error}" );
}
}
}
?>
Line 93 is contained inside the function createTable, specifically this if statement:
if ( !$this->dbobj->query( $queryString ) ) {
die( "[EZSQL]: SQL CREATE TABLE query error #{$this->dbobj->errno}: {$this->dbobj->error}" );
}
Any and all help will be greatly appreciated, as I can't quite tell what is wrong.
Again, keep in mind I am new to dependency injection. Syntax errors in PHP are not a rarity for me.
Thanks.
Edit 1: Here's the connection I'm using in my index.php page:
<?php
require( "inc/classes/ezsql.class.php" );
// require( "inc/config.php" );
// require( "inc/proc/db.php" );
$host = "127.0.0.1";
$user = "root";
$pass = "";
$db = "testdb";
$port = "3306";
$db = new mysqli( $host, $user, $pass, $db, $port );
echo "var_dunp before creating instance of EZSQL\n";
var_dump( $db );
if ( !$db ) {
die( "error" );
}
$ezsql = new EZSQL( $db );
$cols = array(
"int" => "int",
"text" => "varchar(255)"
);
$ezsql->createTable( "test1", TRUE, $cols );
?>
Edit 2: Added var_dump()s to certain locations. See this Pastebin for what they return.
Use a pdo wrapper such as this:
http://pastebin.com/AHdJkCBz
Then in the same directory, add settings.ini.php like this http://pastebin.com/cu1kY8kL
Do not commit this settings file to your repository. Copy and paste it always.
The way to call this would be:
function checkPassword($email, $password)
{
$db = new db();
$binding = array('emailId'=>$email, 'hashed_password'=>md5($password));
return $db->single("SELECT id FROM members WHERE pc_address = :emailId AND hashed_password = :hashed_password", $binding);
}
Check the whole class for detailed methods.
I have also made two general purpose functions to add/update database.
function assetsUtilInsert($table, $params, $debug=false){
$db = new db();
if($table && is_array($params)){
$bindings = $params;
if(!empty($bindings)) {
$fields = array_keys($bindings);
$fieldsvals = array(implode(",",$fields),":" . implode(",:",$fields));
$sql = "INSERT INTO ".$table." (".$fieldsvals[0].") VALUES (".$fieldsvals[1].")";
}
if($debug==true)
echo $sql;
$result = $db->query($sql,$bindings);
return $db->lastInsertId();
} else {
assetsUtilInvokeError($this->registry->log, "Could not insert because either table does not exist or parameters is not an array");
}
}
function assetsUtilUpdate($table, $params, $where, $exit=0){
$db = new db();
$sql = '';
if($table && is_array($params) && is_array($where)){
$sql = "UPDATE $table SET ";
foreach($params as $key => $val){
$sql .= $key ."=:$key, ";
}
unset($key); unset($val);
$sql = substr($sql, 0, (strlen($sql)-2));
$sql .= " WHERE ";
foreach($where as $key => $val){
$sql .= $key ."=:$key AND ";
}
unset($key); unset($val);
$bindings = array_merge($params, $where);
unset($params); unset($where);
$sql = substr($sql, 0, (strlen($sql)-4));
if($exit==1){
echo $sql; exit;
}
return $db->query($sql, $bindings);
} else {
assetsUtilInvokeError($this->registry->log, "Could not insert because either table does not exist or params is not an array or where condition is not an array");
}
}
I have used log4php for logging. You could adapt with this code.
Just changing the settings.ini.php for driver can help you use the same code for different databases supported by PDO.
Fixed.
I am an idiot, apparently, and screwed up a few things.
Thanks for the suggestions, but I'm not doing this professionally, so I don't care about injection attacks at this point in time. All of this is hosted locally and not port forwarded, so none of this data really matters.
Input sanitization is next on my list, though.
Check if you have enabled mysqli extension or just test if you can instance outside your class.
Also can we see how you instance and use your class when you get this error.
EDIT
Check mysqli object with var_dump() when you pass to your constructor.
I want to use PDO prepared statements but i find it really time consuming to type. it would be super useful if there is a function to just pass the following associative array:
array(
"title"=>$title
"userid"=>$userid
"post"=>$body
)
Keeping in mind that the keys in the array always match the rows in the SQL table. recaping everything, this should cut off the effort to type the :foo and type them again in the execute function.
I'm specifically talking about the INSERT query.
How to do that?
function pdo_insert($table, $arr=array())
{
if (!is_array($arr) || !count($arr)) return false;
// your pdo connection
$dbh = '...';
$bind = ':'.implode(',:', array_keys($arr));
$sql = 'insert into '.$table.'('.implode(',', array_keys($arr)).') '.
'values ('.$bind.')';
$stmt = $dbh->prepare($sql);
$stmt->execute(array_combine(explode(',',$bind), array_values($arr)));
if ($stmt->rowCount() > 0)
{
return true;
}
return false;
}
pdo_insert($table, array('title'=>$title, 'userid'=>$user_id, 'post'=>$body));
Slighly improved PDO Insert function that also takes security into consideration by preventing SQL Injection attacks:
// Insert an array with key-value pairs into a specified database table (MySQL).
function pdo_insert($dbh,$table,$keyvals) {
$sql = sprintf("INSERT INTO %s ( `%s` ) %sVALUES ( :%s );",
$table,
implode("`, `", array_keys($keyvals)),
PHP_EOL,
implode(", :", array_keys($keyvals))
);
$stmt = $dbh->prepare($sql);
foreach ($keyvals as $field => $value) {
$stmt->bindValue(":$field", $value, PDO::PARAM_STR);
}
$stmt->execute();
return $dbh->lastInsertId();
}
// Convert special characters to HTML safe entities.
function h($str) {
return trim(stripslashes(htmlspecialchars($str, ENT_QUOTES, 'utf-8')));
}
Example:
$dbh = new PDO($dsn);
$dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$keyvals = [
'id' => isset($_POST['id']) ? h( $_POST['id'] ) : null,
'title' => isset($_POST['title']) ? h( $_POST['title'] ) : null,
'description' => isset($_POST['description']) ? h( $_POST['description'] ) : null,
'created_at' => time(),
'created_by' => 1,
];
$last_ids[] = pdo_insert($dbh,'products',$keyvals);