Prepare statement issues when submitting form [Solved]

Hi all

Been trying to build/update an old form using a preparedstatement, though when I submit the form I get the following errors:

This first warning runs 5 times

Warning: mysqli_real_escape_string() expects exactly 2 parameters, 1 given in …

Warning: mysqli_stmt::bind_param(): Number of variables doesn’t match number of parameters in prepared statement in …

Any ideas what I’m doing wrong, advice thanks.

if(isset($_POST['submit'])){

$venue_id = str_replace("-"," ",mysqli_real_escape_string(strtolower($_GET['venue_id'])));
$review_date = mysqli_real_escape_string($_POST['review_date']);
$publisher = mysqli_real_escape_string($_POST['publisher']);
$comments = mysqli_real_escape_string($_POST['comments']);
$rating = mysqli_real_escape_string($_POST['rating']);

$addReview = $mysqli->prepare("
  INSERT INTO feedback (venue_id
  , review_date
  , publisher
  , comments
  , rating)
  
  VALUES (?,NOW(),?,?,?)");
  $addReview->bind_param('ssssi', $venue_id, $review_date, $publisher, $comments, $rating);
  $addReview->execute();
  $addReview->close();
} 

Check out the documentation for the procedural use of mysqli_real_escape_string() here: http://php.net/manual/en/mysqli.real-escape-string.php . This function requires two parameters - the first one is “A link identifier returned by mysqli_connect() or mysqli_init()”, and the second one is the string.

// Change
$review_date = mysqli_real_escape_string($_POST['review_date']);
// TO
 $review_date = $mysqli->real_escape_string($_POST['review_date']);
// Or better yet since there is no reason to escape when using prepared statements
$review_date = $_POST['review_date'];

And for the second problem, count the number of ? place holder characters in your sql. Does passing 5 parameters to a sql with 4 place holders make sense?

And for crying out loud, enough with that leading comma nonsense. At least wait until you can write a simple sql statement before trying to look like hip.

Thanks WebMachine!

I didn’t realise I needed to make the connection for each value, little confusing though this seems to have fixed this part :slight_smile:

$publisher = $mysqli->real_escape_string($_POST['publisher']);
...

Final warning

Warning: mysqli_stmt::bind_param(): Number of variables doesn’t match number of parameters in prepared statement in /

Thanks ahundiak made me smile :smile:

And for crying out loud, enough with that leading comma nonsense. At least wait until you can write a simple sql statement before trying to look like hip.

And posted same time…

So are you saying because we’re using a prepared statement there is no benefit using

$review_date = $mysqli->real_escape_string($_POST['review_date']);

over

$review_date = $_POST['review_date'];

And for the second problem, count the number of ? place holder characters in your sql.

I was counting NOW()

Update

VALUES (?,NOW(),?,?,?)");
$addReview->bind_param('sssi', $venue_id, $publisher, $comments, $rating);

Its working! :sunglasses:

I didn’t realise NOW() would not be counted as a place holder.

Barry

I’ve just typed the below into the form and got a 403 forbidden white screen


<p>barry</p>
<?php echo "hey!"; ?>

So there is still a problem here?

I also noticed that when I refresh the page the form tries to resubmit, how do I stop this so the form is cleared?

Thanks, Barry

That is exactly what I am saying. As a bonus, it is what the php manual says as well:

[quote]
Escapes special characters in a string for use in an SQL statement, taking into account the current charset of the connection[/quote]

Ok thanks ahundiak!

I’ll need to do more testing because no matter which method I use, I still get the 403, plus the re-submit problem I’m having.

The main problem resolved, so result for now :slight_smile:

There is no benefit in using either of those. The first is pointless as it is for when you jumble the SQL and code together - which you don’t do when you use prepare statements.

The second introduces a security hole because you then can’t tell if $review_date is a valid date or anything at all. If you are not going to actually validate $_POST fields before moving them to a new field then don’t move them and just reference the $_POST throughout your code as a reminder that the value hasn’t been validated.

You mean like this?

$addReview->bind_param('sssi', $_GET['venue_id'], $_POST['publisher'], $_POST['comments'], $_POST['rating']);

But surely I’ll need to validate these fields.
My old mysql code I was using is shown below, just not sure how to mirror this using mysqli prepare?

if(isset($_POST['submit'])){

if (!empty($_POST['publisher'])) {
  $n = escape_data(htmlspecialchars($_POST['publisher']));
  } else {
  echo '<p>Please leave your name</p>';
  $n = FALSE;
}

if (!empty($_POST['comments'])) {
  $c = escape_data(htmlspecialchars($_POST['comments']));
} else {
  echo '<p>You forgot to add your comments</p>';
  $c = FALSE;
}

if (!empty($_POST['rating'])) {
  $rat = escape_data(htmlspecialchars($_POST['rating']));
} else {
  echo '<p>Please select your rating</p>';
$rat = FALSE;
}

$venue_id = str_replace("-"," ",mysql_real_escape_string(strtolower($_GET['venue_id'])));
$review_date = mysql_real_escape_string($_POST['review_date']);
$publisher = mysql_real_escape_string($_POST['publisher']);
$comments = mysql_real_escape_string($_POST['comments']);
$rating = mysql_real_escape_string($_POST['rating']);

if ($venue_id && $n && $c && $rat) {

$q = "INSERT INTO feedback (venue_id, review_date, publisher, comments, rating)
values ('$venue_id', NOW(), '$publisher', '$comments', $rating)";
...

So how would I get the same security with our latest snippet (mysqli prepare) ?

Or is all this checking overkill and not needed now?

Cheers, Barry

Yes you should - and only move the valid value to the new field.

Note that escape and htmlspecialchars are NOT validation functions - they are for specific escaping and convert your input to JUNK if you run them as input functions - they are output functions to be run just before the data is output to the particular destination the function is for and where the data cannot be kept separate from the code.

Validation functions check that the value is valid for what the particular field is allowed to contain. For example “rating” should presumably be a number (possibly between 0 and 5) in which case you’d validate it using:

$rat = FALSE;
if (!empty($_POST['rating'])) {
  if(filter_var(($_POST['rating'], FILTER_VALIDATE_INT) === false) {
      echo '<p>Rating must be a number</p>';
   } else (if $_POST['rating'] < 0 || $_POST['rating'] > 5) {
       echo '<p>Rating must be between 0 and 5 inclusive</p>';
   } else $rat = $_POST['rating']; // only at this point do we copy as we now know it is valid
} else {
  echo '<p>Please select your rating</p>';
}
// all references to $rat from here on are either valid or FALSE.

So we can completely remove all instances of

$review_date = mysql_real_escape_string($_POST['review_date']);
$publisher = mysql_real_escape_string($_POST['publisher']);

and so on

And duplicate what you have done with rating for the strings?
An example maybe :slight_smile:

Barry

Yes - when you switch to using prepare calls the data and code no longer get jumbled together and so there is no need to escape the data to avoid it being interpreted as code.

You just need to validate that the content of the posted values are valid before doing anything at all with the values.

Making more sense :sunny:
Because this is slightly new to me just a little concerned of sql injection or errors

But what about my earlier post regarding…

403 forbidden and the form re-submitting itself on reload/refresh?

injection is only possible when the code and data are jumbled together. When you use prepare statements only the code goes in the prepare statement and the data is kept separate in bind or execute statements. As there is no data in the prepare statement and only the prepare statement contains code there is no way to inject anything into the code.

Right on!

Thanks felgall for the detailed information really helps me understand more :sunglasses:

I’ll dig deeper into this and hopefully have the full script up and running very soon.
Any issues I’ll create new thread.

Barry

I thought $_GET and $_POST were mutely exclusive. A request can be either a GET or a POST
What is the method attribute of the form element.
If it is method = “post” then all your input data is in the $_POST array and if the method is get then all the data is in the $_GET array.
As a my 2c comment, why not use PDO instead of mysql.
PDO takes care of escaping the input values.

They are not - you use $_GET for retrieving data that is not expected to change and $_POST for data that is likely to change - and you can use both in the same call if you really want to (although there is seldom a situation where it makes sense to do so).

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.