Waff
Waff

Reputation: 13

Prepared statement return false always

While trying to insert data into the database using prepared statement the prepared statement always returns false and not complete the connection.

I'm using this connection on cpanel (not sure if that's related) tried to change the order tried to change the data type.

$conn = mysqli_connect($servername,$username,$password,$database);

// $sql=$conn->prepare("insert into asset 'assetName'=?, 'grp' ='?'  ,'Descrip' = '?'  , 'enteredValue' = '?',  'depreciationRate' = '?','entrydate'='?' 'availability'= '?'  ,'enteredBy' = '?' , 'updatedOn' = '?' , 'isPeriodic' = '?' , 'assetType' = '?','Frequency'='?','ExitDate'='?'");

if($sql = $conn->prepare("INSERT INTO `asset`(`id`, `assetName`, `grp`, `Descrip`, `enteredValue`, `depreciationRate`, `entrydate`, `availability`, `enteredBy`, `updatedOn`, `isPeriodic`, `assetType`, `Frequency`, `ExitDate`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)")){

$sql->bind_param("sssssssssss",$name,$group,$value,$depreciation,$entryDate,$availability,$enteredBy,$updatedOn,$isPeriodic,$type,$frequency,$exitDate);

$sql->execute(); always return false and nothing has been inserted in the database.

Upvotes: 0

Views: 285

Answers (3)

ArtisticPhoenix
ArtisticPhoenix

Reputation: 21661

As I said in the comments:

Well you have 14 ? and 11 s by my count. OR sssssssssss and ?????????????? Which as most of us know, is gonna throw an error as your placeholder count doesn't match your values

If you can put your data in an array you can use that array to build your query.

if($sql = $conn->prepare("INSERT INTO `asset`(`id`, `assetName`, `grp`, `Descrip`, `enteredValue`, `depreciationRate`, `entrydate`, `availability`, `enteredBy`, `updatedOn`, `isPeriodic`, `assetType`, `Frequency`, `ExitDate`) VALUES (".implode(',', array_fill(0,count($data), '?')).")")){
   $sql->bind_param(str_repeat('s', count($data)),...$data);

Lets walk thought this a bit

Basically you can create your arguments with the same length as the $data with these 2 pieces of code:

  implode(',', array_fill(0,count($data), '?')) //implode "?" with ","  equal to the length of data
  str_repeat('s', count($data)) //create 's' equal to the length of data

Then the real magic happens here with the ... "variadic" (variable length arguments):

 $sql->bind_param(str_repeat('s', count($data)),...$data);

In PHP v5.6+ you can just inject the data using ... in and it will unwind it for you. Or in other words, put each array item in as a new argument.


For the fields (columns)

If you want to do the fields too, that is a bit more tricky. You have to be careful of what is in those if you put that data directly into the SQL. For example a User could edit the keys used in a $_POST request in such a way as to do SQLInjection if you just concatenate the Post Keys into the SQL.

One of the simplest ways to solve this is to have a whitelist of fields like so (matched to the column names):

 //all allowed column names for this query (case sensitive)
 $whitelist = ["id", "assetName", ...];

You can use array_intersect_key to retain only the data you want for the query (assuming the data has matched keys). The keys will be safe to use now in the query as they must match what is in the $whitelist.

 //remove unknown keys form input data (~ retain only known ones)
 //array_flip($whitelist) = ["id"=>0, "assetName"=>1, ...];
 $data = array_intersect_key($_POST, array_flip($whitelist));

 if($sql = $conn->prepare("INSERT INTO `asset`(`".implode("`,`", array_keys($data))."`)VALUES(".implode(',', array_fill(0,count($data), '?')).")".)){
    $sql->bind_param(str_repeat('s', count($data)),...$data);

Other things

The only thing this doesn't cover is if you want all the fields in $whitelist to always be present. You can solve this with validation of the incoming data or you can merge in some empty fields to insure that all the data is present.

  $default =  array_fill_keys($whitelist, ''); //["id"=>"", "assetName"=>"", ...] ~ create empty "default" row

  //$default['updatedOn'] = date('Y-m-d'); //you can also manually set a value

  $data =  array_intersect_key(
                 array_merge(
                        $default,
                        $_POST  //["id"=>"1", ...] ~ missing assetName
                 ), array_flip($whitelist)); //-> ["id"=>"1","assetName"=>""]

Array fill keys (similar to array fill from before) takes a list of keys, and adds a value in for each. So that gives us an array who's keys are the the values of $whitelist and an empty string for each items value. I call this a default row.

Then we merge this with our original data. Data in the first array will be overwritten by any data with matched keys for the second array ($_POST in my example). So if a key is present like id in the example above, it overwrite the empty one.

Anything not overwritten keeps the empty value from the default row we made. Then the array intersect key removes anything extra like before.

*PS I didn't test any of this so please forgive any syntax errors.

Enjoy!

Upvotes: 1

Yassine CHABLI
Yassine CHABLI

Reputation: 3714

I think you don't execute your query calling the execute method :

$conn = mysqli_connect($servername,$username,$password,$database);

// $sql=$conn->prepare("insert into asset 'assetName'=?, 'grp' ='?'  ,'Descrip' = '?'  , 'enteredValue' = '?',  'depreciationRate' = '?','entrydate'='?' 'availability'= '?'  ,'enteredBy' = '?' , 'updatedOn' = '?' , 'isPeriodic' = '?' , 'assetType' = '?','Frequency'='?','ExitDate'='?'");

if($sql = $conn->prepare("INSERT INTO `asset`(`id`, `assetName`, `grp`, `Descrip`, `enteredValue`, `depreciationRate`, `entrydate`, `availability`, `enteredBy`, `updatedOn`, `isPeriodic`, `assetType`, `Frequency`, `ExitDate`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)")){

$sql->bind_param("sssssssssss",$name,$group,$value,$depreciation,$entryDate,$availability,$enteredBy,$updatedOn,$isPeriodic,$type,$frequency,$exitDate);
sql->execute();
sql->close(); // close connection 

Upvotes: 0

Jonast92
Jonast92

Reputation: 4967

You have to execute the statement once you've bound the data.

$sql->execute();

The number of parameters are also inconsistent as pointed out by the comments.

Upvotes: 0

Related Questions