Wrong parameter count for bind_param in custom mysqli class - php

Trying to generate a custom mysqli class / wrapper with $this->_mysqli as a mysqli instance:
// Query string generator
private function gen_query($type, $data, $table){
switch ($type) {
case 'ins':
$query = "INSERT INTO " .$table .' ';
$query .= implode_key($opr= ', ', $data);
$query .= " VALUES " . value($data);
break;
case 'select':
// yet to generate
default:
$query ='';
break;
}
return $query;
}
// Generates bind parameters
private function gen_param($data){
$_param = "'";
foreach ($data as $v) {
$_param .= $this->detect_type($v);
}
$_param .= "', ";
foreach ($data as $k=>$v) {
if($v == end($data)) {
$_param .="$$k";
continue;
}
$_param .= "$$k, ";
}
return $_param;
}
public function insert( $table, $data ){
$table = $this->_prefix . $table;
$table = $this->escape($table);
$query = $this->gen_query('ins', $data, $table);
$stmt = $this->_mysqli->prepare($query);
foreach ($data as $key => $value) {
$$key = $value;
}
$test = $this->gen_param($data);
if(!$stmt->bind_param($test)) {
echo $this->_mysqli->error;
}
if($stmt->execute()){
print 'Success!'.'<br />';
} else {
die('Error : ('. $this->_mysqli->errno .') '. $this->_mysqli->error);
}
}
So when user inputs
$data = [ 'first_name' => 'foo', 'last_name' => 'bar', 'another_field' => 'blah'];
$db->insert('t1', $data);
I get this error:
Warning: Wrong parameter count for mysqli_stmt::bind_param() in path\to\class-db.php on line 138
This is line 138: if(!$stmt->bind_param($test))

Not sure why the question was downvoted. Anyways, I got this fixed by referring: this repo.
We'll need to get rid of gen_param, use an array instead and call using a callback function to get the parameter values right and refer the values.
The code is now:
public function insert( $table, $data ){
$table = $this->_prefix . $table;
$table = $this->escape($table);
$this->esc_sql_arr($data);
$query = $this->gen_query('ins', $data, $table);
$stmt = $this->_mysqli->prepare($query);
$this->_bind_param($data);
call_user_func_array(array($stmt, 'bind_param'),$this->return_ref($this->bind_arr));
if($stmt->execute()){
echo "Success!";
}else{
die('Error : ('. $this->_mysqli->errno .') '. $this->_mysqli->error);
}
$this->reset();
}
private function _bind_param($data){
foreach($data as $key=>$value) {
$this->_bind_values($value);
}
}
private function _bind_values($value) {
$this->bind_arr[0] .= $this->detect_type($value);
array_push($this->bind_arr, $value);
}
protected function return_ref(array &$arr)
{
//Reference in the function arguments are required for HHVM to work
//https://github.com/facebook/hhvm/issues/5155
//Referenced data array is required by mysqli since PHP 5.3+
if (strnatcmp(phpversion(), '5.3') >= 0) {
$refs = array();
foreach ($arr as $key => $value) {
$refs[$key] = & $arr[$key];
}
return $refs;
}
return $arr;
}
The code is nowhere near complete, but this got me started.

Related

Function for PDO preapred stament not work and get error

I make 2 function with same use to take over PDO Preapred Statement, but both of not work .
Function 1 :
function doSave($array, $table) {
if (count($array) == 0) {
throw new Exception('Array cant be empty');
} else {
global $connect;
//prepare the query first
$prepare_1 = 'INSERT INTO' . ' ' . $table . ' '; //start preparing
$columns = array();
foreach ($array as $key => $value) {
$columns[] = ':' . $key; //gets all columns and add commas
}
foreach ($array as $key => $value) {
$keye[] = $key; //gets all columns and add commas
}
$keyes = implode(', ', $keye);
$column = implode(', ', $columns);
//now you can combine everything and prepare
$stmt99 = $connect->prepare($prepare_1 .'('.$keyes.')'. ' VALUES (' . $column . ')');
//remember to add the values. also test this section as its not tested
foreach ($array as $key => $value) {
$test[] = "':" . $key ."' => ". $value;
}
$tests = implode(', ', $test);
$stmt99->execute($tests);
}
}
When i insert the data i got no error and no data get insert to my database
Function 2 :
function doSave($array, $table) {
if (count($array) == 0) {
throw new Exception('Array cant be empty');
} else {
global $connect;
//prepare the query first
$prepare_1 = 'INSERT INTO' . ' ' . $table . ' '; //start preparing
$columns = array();
foreach ($array as $key => $value) {
$columns[] = ':' . $key; //gets all columns and add commas
}
foreach ($array as $key => $value) {
$keye[] = $key; //gets all columns and add commas
}
$keyes = implode(', ', $keye);
$column = implode(', ', $columns);
//now you can combine everything and prepare
$stmt99 = $connect->prepare($prepare_1 .'('.$keyes.')'. ' VALUES (' . $column . ')');
//remember to add the values. also test this section as its not tested
foreach ($array as $key => $value) {
$test[] = '$stmt99->bindparam('.'":' . $key .'",'. $value.'); ';
}
$tests = implode(' ', $test);
$tests;
$stmt99->execute();
}
}
i got error when use this function :
SQLSTATE[HY093]: Invalid parameter number: no parameters were bound
This How i use the function :
$array = array('categoryName' => $categoryName, 'categorySort' => $categorySort);
doSave($array, 'category');
This the source of the array :
if (!empty($_POST["categoryName"])) {
$categoryName = ($_POST["categoryName"]);
if (!preg_match("/^[a-zA-Z ]*$/",$categoryName)) {
$errMsg = "<div class='alert alert-danger text-center'><strong>Hanya boleh huruf.</strong></div>";
}
}
if ($_POST["categorySort"] == $check['categorySort']) {
$errMsg = "<div class='alert alert-danger text-center'><strong>Urutan sudah digunakan.</strong></div>";
}else{
$categorySort = ($_POST["categorySort"]);
if (!is_numeric($_POST['categorySort'])) {
$errMsg = "<div class='alert alert-danger text-center'><strong>Hanya boleh angka.</strong></div>";
}
}
What possibly go wrong from this 2 function both function for same use. Function 1 (named param) Function 2 (bindparam) ?
The following is not fully tested but displayed what I expected when I tested it using echo statements.
You should check the return value of prepare before attempting to execute the statement because it will return false if the statement failed to be prepared correctly.
function doSave( $array, $table ) {
try{
/* if you throw exceptions you should catch them!! */
if( empty( $array ) )throw new Exception('Array cant be empty');
if( empty( $table ) )throw new Exception('Table name cannot be empty');
global $connect;
/* placeholder variables */
$prepare = $columns = $values = array();
$result = false;
$table = preg_replace("#[',\.]#",'',$table);// whatever chars deemed appropriate to replace
$prepare[]="insert into `{$table}` ";
/* iterate through source array */
foreach( $array as $key => $value ) {
$columns[] = $key;
$values[ $key ] = $value;
}
$strcolumns = implode('`,`',$columns);
$strplaceholders = ':'.implode(', :',$columns);
/* append columns and placeholders */
$prepare[]="( `$strcolumns` ) values ( $strplaceholders );";
/* finalise sql statement */
$sql=implode('',$prepare);
$stmt = $connect->prepare( $sql );
if( $stmt ){
/* bind the params */
foreach( $values as $key => $value ) $stmt->bindParam( ':'.$key, $value );
/* execute the statement */
$result = $stmt->execute();
} else {
throw new Exception('Error preparing sql statement');
}
return $result;
}catch( Exception $e ){
exit( $e->getMessage() );
}
}
The assumption I made for the code was an input array like this
$t='mytable';
$a=array(
'id' => '303',
'name' => 'bob',
'size' => 'small',
'weight'=> 'heavy'
);
NOTE:
You have two functions both with the same name. How is PHP meant to know the which function you're calling?
Function 2:
foreach ($array as $key => $value) {
$test[] = '$stmt99->bindparam('.'":' . $key .'",'. $value.'); ';
}
Because you encased this in [single] quotes, this value is no longer an object method call but is just a string. This means that when you then implode this array all you're making is a longer string.
Also, because you're using single quotes, PHP will not recognise the value $stmt99 as being a PHP object reference, instead taking it literally as dollar sign, s character, t character, m character, etc....
So PDO has no values to bind into the SQL given.
Fix:
foreach ($array as $key => $value) {
$stmt99->bindparam(":" . $key , $value);
}
unset($key,$value); // always tidy up after foreach loops.
A better fix can be found exampled here

PHP Using call_user_func_array to bind_param

i had problem INSERT data to mysql using PHP OOP.
so, this is my code :
public function insert($setType, $setTable, $setRow, $setValues) {
$change = function($values) {
return "?";
};
$row = join(",", $setRow);
$done = join(",", array_map($change, $setValues));
$values[] = join(",", $setValues);
function SqlArrayReferenceValues($arr){
if (strnatcmp(phpversion(),'5.3') >= 0) {
$refs = array();
foreach($arr as $key => $value)
$refs[$key] = &$arr[$key];
return $refs;
}
return $arr;
}
$insert = $this->connect->prepare("INSERT INTO $setTable ($row) VALUES ($done)");
$mergedValues = array_merge(array($setType), $values);
call_user_func_array(array($insert, "bind_param"), SqlArrayReferenceValues($mergedValues));
$insert->execute();
$insert->close();
return $insert;
}
if (empty($_SESSION['transaction'])) :
$idt = date("ymdhis");
$_SESSION['transaction'] = $idt;
endif;
$st = $_SESSION['transaction'];
if (isset($_SESSION['email'])) :
$se = $_SESSION['email'];
$user = $objMysql->query(array("*"), "user", "email = '$se'");
$dataUser = $objMysql->fetch($user);
$ide = $dataUser['id_user'];
else :
$ide = 0;
endif;
$currentLink = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$objMysql->insert("sis", "url", array("id_transaction", "id_user", "url"), array("$st", "$ide", "$currentLink"));
this is my mysql table :
id_transaction row - type varchar.
id_user row - type INT
url row - type text
i got 1 errors,
mysqli_stmt::bind_param(): Number of elements in type definition string doesn't match number of bind variables in
what's wrong with my code?
FINAL SOLUTION BY MARTIN
public function SqlArrayReferenceValues($arr){
if (strnatcmp(phpversion(),'5.3') >= 0) {
$refs = array();
foreach($arr as $key => $value)
$refs[$key] = &$arr[$key];
return $refs;
}
return $arr;
}
public function insert($setType, $setTable, $setRow, $setValues) {
$change = function($values) {
return "?";
};
$row = join(",", $setRow);
$done = join(",", array_map($change, $setValues));
$insert = $this->connect->prepare("INSERT INTO $setTable ($row) VALUES ($done)");
$params = $setValues;
$ww = array_merge(array($setType), $params);
call_user_func_array(array($insert, "bind_param"), $this->SqlArrayReferenceValues($ww));
$insert->execute();
$insert->close();
return $insert;
}
As correctly noted in comments, by Mark Baker and bub, you are passing a string value in the place of an array value.
What you have is an array of values you then turn into a string, before then trying to use them as an array. There is no need use join on the $values variable.
//$values = join(",", $setValues); // comment out this line,
call_user_func_array(array($insert, "bind_param"), array_unshift(array($setType), $setValues));
Here you use array_unshift to insert the array TYPES that you passed to the function, at the start of the array. This will now give you a correct array to insert into bind_param:
array("sis", $st, $side, $currentLink);
Problems with expected by reference:
There is an issue with the above that the MySQLi OOP code expects objects as references, so as well as the above- the values needs to be run through a separate process to give their "true" values to the call_user_func_array() function.
In your class/script add:
private function SqlArrayReferenceValues($arr){
if (strnatcmp(phpversion(),'5.3') >= 0) {
$refs = array();
foreach($arr as $key => $value)
$refs[$key] = &$arr[$key];
return $refs;
}
return $arr;
}
This function takes the values and returns the correct value types to the call_user_array_function.
so:
$mergedValues = array_unshift($setValues, $setType);
call_user_func_array(array($insert, "bind_param"), $this->SqlArrayReferenceValues($mergedValues));
I got this code from somewhere on Stack Overflow and it's been very useful for years! This is the link: https://stackoverflow.com/a/16120923/3536236
Update 3:
How your code should look:
private function SqlArrayReferenceValues($arr){
if (strnatcmp(phpversion(),'5.3') >= 0) {
$refs = array();
foreach($arr as $key => $value)
$refs[$key] = &$arr[$key];
return $refs;
}
return $arr;
}
public function insert($setType, $setTable, $setRow, $setValues) {
$change = function($values) {
return "?";
};
$row = join(",", $setRow);
$setValuesCopy = $setValues;
$done = join(",", array_map($change, $setValuesCopy));
$insert = $this->connect->prepare("INSERT INTO ".$setTable." (".$row.") VALUES (".$done.")");
$mergedValues = array_unshift($setValues, $setType);
//probably not needed but resetting array numerical indexing:
$mergedValues = array_values($mergedValues);
call_user_func_array(array($insert, "bind_param"), $this->SqlArrayReferenceValues($mergedValues));
$insert->execute();
$insert->close();
return $insert;
}
$currentLink = "http://".$_SERVER[HTTP_HOST].$_SERVER[REQUEST_URI]";
$objMysql->insert("sis", "url", array("id_transaction", "id_user", "url"), array($st, $ide, $currentLink));

Why it doesn't return?? PHP

I can't understand and I seek for help=(
Here is my code:
$add_article = $this->M_articles->Add($_POST['title'], $_POST['content']);
echo "sd;flksdf;lksdfl;";
$add_article2 = true;
if ($add_article)
{
echo 'Article Added!';
header("Location:index.php");
die();
}
else
die('Error adding article');
this is function "Add" from M_Articles:
public function Add($title, $content)
{
/*
$title = trim($title);
$content = trim($content);
if ($title == '')
return false;
//запрос
$object = array();
$object['title'] = $title;
$object['content'] = $content;
$this->msql->Insert('articles', $object);
*/
return true;
}
The thing is...even if I comment everything from function "Add" and leave only "return true"... it wouldn't redirect me to index.php. Moreover, it doesn't even echo anything (even those "sd;fkfdsf.." string). The script just dies for some reason. I can't get where is the problem, can some1 explain to newbie what's the problem and how it should be fixed? If you need additional info, i'll provide it.
update: Maybe it's important...but if I delete those comment "/* */" things, it'd correctly add article to a DataBase. But then the script dies=/
update:
ok, now it says: "Notice: Undefined variable: result in Z:\home\myblog\www\c\M_MSQL.php on line 86"
here's my code for M_MSQL on line 86:
public function Insert($table, $object)
{
$columns = array();
$values = array();
foreach ($object as $key => $value)
{
$key = mysql_real_escape_string($key . '');
$columns[] = $key;
if ($value === null)
{
$values[] = "'$value'";
}
else
{
$value = mysql_real_escape_string($value . '');
$values[] = "'$value'";
}
}
$columns_s = implode(',', $columns);
$values_s = implode(',', $values);
$query = "INSERT INTO $table ($columns_s) VALUES ($values_s)";
$result = mysql_query($query);
if (!$result)
die(mysql_error());
return mysql_insert_id();
}
Reason is you are outputing things, so you have a notice : "Headers already sent".
If you remove the "echo" stuff, you'll be alright :
if ($add_article)
{
header('Location: /index.php?article_added=1');
}

PHP MySQL PDO Update not working

This is driving me absolutely crazy, I've rewritten it several times and still no go. My insert function works perfectly fine. I have no idea what I'm overlooking, anything you could suggest that might help would be extremely appreciated.
function update($table, $data, $idName='id')
{
if(empty($data) || !is_array($data))
return false;
$columns = $values = array();
foreach($data as $key => $val)
$columns[] = "$key=:$key";
$columns = makeCSL($columns, false);
try {
$qStr = "UPDATE $table SET $columns WHERE $idName=:id";
echo $qStr;
$query = $this->dbHandle->prepare($qStr);
foreach($data as $key => $val)
$query->bindParam("':$key'", $val, PDO::PARAM_STR);
$query->execute();
} catch(PDOException $e) {
$this->errCode = $e->getCode();
$this->errInfo = $e->errorInfo[2];
}
}
You have several problems.
First, you shouldn't put quotes around the placeholder being bound. Second, you need to use bindValue, because bindParam binds to a reference, so everything will be bound to the value of $val from the last time through the loop. And third, you don't have a binding for :id.
So it should be:
foreach($data as $key => $val) {
if ($key != 'id') {
$columns[] = "$key=:$key";
}
}
...
foreach ($data as $key => $val) {
$query->bindValue(":$key", $val, PDO::PARAM_STR);
}
Got it working with the following code
function update($table, $data, $idName="id")
{
if(empty($data) || !is_array($data))
return false;
$columns = array();
foreach($data as $key => $val)
$columns[] = "$key=:$key";
$columns = makeCSL($columns, false);
try {
$query = $this->dbHandle->prepare("UPDATE $table SET $columns WHERE $idName=:id");
$query->execute($data);
} catch(PDOException $e) {
$this->errCode = $e->getCode();
$this->errInfo = $e->errorInfo[2];
}
}

MySQLi Query Returning Single Result -- Should Be Array of Results

Having some trouble with the following code. I've created a class to manage the DB connection, using what you see below as queryPreparedQuery and works fine when getting data for a single user, or any data that returns a single result using something like this...
include 'stuff/class_stuff.php';
function SweetStuff() {
$foo = new db_connection();
$foo->queryPreparedQuery("SELECT Bacon, Eggs, Coffee FROM Necessary_Items WHERE Available = ?",$bool);
$bar = $foo->Load();
$stuff = 'Brand of Pork is '.$bar['Bacon'].' combined with '.$bar['Eggs'].' eggs and '.$bar['Coffee'].' nectar for energy and heart failure.';
return $stuff;
}
echo SweetStuff();
Problem is, I want to build the functionality in here to allow for a MySQL query which returns multiple results. What am I missing? I know it's staring me right in the face...
class db_connection
{
private $conn;
private $stmt;
private $result;
#Build a mysql connection
public function __construct($host="HOST", $user="USER", $pass="PASS", $db="DB_NAME")
{
$this->conn = new mysqli($host, $user, $pass, $db);
if(mysqli_connect_errno())
{
echo("Database connect Error : "
. mysqli_connect_error());
}
}
#return the connected connection
public function getConnect()
{
return $this->conn;
}
#execute a prepared query without selecting
public function execPreparedQuery($query, $params_r)
{
$stmt = $this->conn->stmt_init();
if (!$stmt->prepare($query))
{
echo("Error in $statement when preparing: "
. mysqli_error($this->conn));
return 0;
}
$types = '';
$values = '';
$index = 0;
if(!is_array($params_r))
$params_r = array($params_r);
$bindParam = '$stmt->bind_param("';
foreach($params_r as $param)
{
if (is_numeric($param)) {
$types.="i";
}
elseif (is_float($param)) {
$types.="d";
}else{
$types.="s";
}
$values .= '$params_r[' . $index . '],';
$index++;
}
$values = rtrim($values, ',');
$bindParam .= $types . '", ' . $values . ');';
if (strlen($types) > 0)
{
//for debug
//if(strpos($query, "INSERT") > 0)
//var_dump($params_r);
eval($bindParam);
}
$stmt->execute();
return $stmt;
}
#execute a prepared query
public function queryPreparedQuery($query, $params_r)
{
$this->stmt = $this->execPreparedQuery($query, $params_r);
$this->stmt->store_result();
$meta = $this->stmt->result_metadata();
$bindResult = '$this->stmt->bind_result(';
while ($columnName = $meta->fetch_field()) {
$bindResult .= '$this->result["'.$columnName->name.'"],';
}
$bindResult = rtrim($bindResult, ',') . ');';
eval($bindResult);
}
#Load result
public function Load(&$result = null)
{
if (func_num_args() == 0)
{
$this->stmt->fetch();
return $this->result;
}
else
{
$res = $this->stmt->fetch();
$result = $this->result;
return $res;
}
}
#Load result
public function Execute(&$result = null)
{
if (func_num_args() == 0)
{
$this->stmt->fetch_array();
return $this->result;
}
else
{
$res = $this->stmt->fetch_array();
$result = $this->result;
return $res;
}
}
private function bindParameters(&$obj, &$bind_params_r)
{
call_user_func_array(array($obj, "bind_param"), $bind_params_r);
}
}
UPDATE
Got this to work with Patrick's help. Was able to find the following code with the help of this question, and with a few tweaks, it works beautifully. Added the following after the execute() statement in ExecPreparedQuery, returning an array at the very end instead of the single result:
# these lines of code below return multi-dimentional/ nested array, similar to mysqli::fetch_all()
$stmt->store_result();
$variables = array();
$data = array();
$meta = $stmt->result_metadata();
while($field = $meta->fetch_field())
$variables[] = &$data[$field->name]; // pass by reference
call_user_func_array(array($stmt, 'bind_result'), $variables);
$i=0;
while($stmt->fetch())
{
$array[$i] = array();
foreach($data as $k=>$v)
$array[$i][$k] = $v;
$i++;
}
# close statement
$stmt->close();
return $array;
As a result of the altered code, I changed the call to interpret multidimensional array data rather than a single result, of course. Thanks again!
In your Execute function you are calling $this->stmt>fetch_array().
That function only returns an array of a single row of the result set.
You probably want:
$this->stmt->fetch_all()
Update
To retrieve the entire result set from a prepared statement:
$this->stmt->store_result()

Categories