A Possible Bug with PDO's bindParam, bindValue and foreach - php

I have been getting an error with this code.
$Database = new Database('localhost','root','password','Db');
$Statement = $Database->prepare("INSERT INTO User VALUES(:ID,:FirstName,:MiddleName:LastName,:RegisteredDate")
$Array_Bind = array(
'ID'=>$ID,
'FirstName'=>$FirstName,
'MIddeName'=>$MiddleName,
'LastName'=>$LastName
'RegisteredDate'=>$Date
)
foreach($Array_Bind AS $Key=>$value){
$Statement->bindParam(':' .$Key, $value)
}
if($Statement->execute()){
echo 'Successfully inserted into the database';
}else{
echo 'could not insert into database';
};
The following have been noted IF the $ID (PrimaryKey) is NOT by DEFAULT an AUTO-INCREMENTING value in the MySQL Database.
ALL Fields except DATETIME Fields gets the value of the last element in the array when inserted into the database.
i.e.
ID = $LastName
FirstName = $LastName
MiddleName = $LastName
LastName = $LastName
RegisteredDate = $RegisteredDate
The same error is outputted when bindValue is used.
So I ended up using
if($Statement->execute($Array_Bind)){
echo 'Successfully inserted into the database';
}else{
echo 'could not insert into database';
};
QUESTIONS
It is recommended to use execute($array_Bind) assuming all data have been sanitize as against using bindParam or bindValue in this scenario?
If Not is there a way to use bindParam or bindValue and arrays?
Is this a bug or wrong coding architecture.

I noticed you had a typo in the placeholder name. Don't know if this will sort the problem
$Statement = $Database->prepare("INSERT INTO User VALUES(:ID,:FirstName,:MiddleName:LastName,:RegisteredDate")
$Array_Bind = array(
'ID'=>$ID,
'FirstName'=>$FirstName,
'MIddeName'=>$MiddleName,
'LastName'=>$LastName
'RegisteredDate'=>$Date
)
You use a placeholder MIddeName but in your query you use MiddleName
Change this
'MIddeName'=>$MiddleName,
to this
'MiddleName'=>$MiddleName,

You need to check your premises. Then double check. Then check documentation.
with bindParam it is not a bug but essential feature.
with bindValue it never happen
of course passing array into execute() is preferred, due to overall sanity and amount of code compared to other methods.

Related

mysqli prepared statement update query fails when id is cast as integer but not as string

In the below code, I am attempting to take three form variables ($nps,$sch,$joint) and an id ($weld_id) and insert them into an UPDATE query. The problem is that I get $stmt->error "No data supplied for parameters in prepared statement."
When I cast the id of the row as "i". The weird thing is that the statement will execute error free if I put single quotes around the last question mark and cast as 's', however, the actual database row will not update. The function on row 3 does NOT use prepared statements to select the current values for this row in the database.
I have var_dumped all variables, copied them and successfuly run the query on MySQL workbench. I am out of ideas, please help.
$weld = mysqli_real_escape_string($db,$_POST['id']);
$weld = single_weld_query($db,$weld);
if(isset($_POST['edit_weld_parameters'])){
// Query to update 3 parameters on database entry where id = N
$stmt = $db->prepare("UPDATE `welds` SET `size` = '?' , `sch` = '?' , `joint` = '?' WHERE `id` = ?;");
$stmt->bind_param("sssi", $nps, $sch, $joint, $weld_id);
$nps = isset($_POST['size'])? mysqli_real_escape_string($db,$_POST['size']): $weld['size'];
$sch = isset($_POST['sch'])? mysqli_real_escape_string($db,$_POST['sch']): $weld['sch'];
$joint = isset($_POST['joint'])? mysqli_real_escape_string($db,$_POST['joint']): $weld['joint'];
$nps = (strlen($nps) and in_array($nps,$pipe_obj->sizes))? $nps: $weld['size'];
$sch = (strlen($sch) and in_array($sch,$pipe_obj->schedules))? $sch: $weld['sch'];
$joint = (strlen($joint) and in_array(strtoupper($joint),$pipe_obj->joint_types))? $joint: $weld['joint'];
$weld_id = $weld['id'];
if($stmt->execute()){
echo $weld['weld_number'].' parameters edited.';
}else{
echo $stmt->error;
}
}else{
echo 'ERROR: Form failure.';
}
You're using prepared statements with placeholder values, which is great, but you're also escaping things, which is bad. That ends up double-escaping them. Leave the escaping up to the driver, use placeholder values, and you'll be fine:
if (isset($_POST['edit_weld_parameters'])) {
// Query to update 3 parameters on database entry where id = N
$stmt = $db->prepare("UPDATE `welds` SET `size` = ? , `sch` = ? , `joint` = ? WHERE `id` = ?;");
$stmt->bind_param("sssi",
isset($_POST['size']) ? $_POST['size'] : $weld['size'],
isset($_POST['sch']) ? $_POST['sch'] : $weld['sch'],
isset($_POST['joint'])? $db,$_POST['joint'] : $weld['joint'],
$weld['id']
);
if ($stmt->execute()) {
echo $weld['weld_number'].' parameters edited.';
}
else {
echo $stmt->error;
}
}
else {
echo 'ERROR: Form failure.';
}
There's other code you'll need to wrangle in there, you're doing some very odd things to validate those after the fact, but try and stick with this general pattern.
Let the driver do the work. Do not put '?' in your query. Do not inline strings with interpolation. Don't escape anything that's already a placeholder value. Do try and keep your logic clean and obvious.

Checking results of PDO statement to run a conditional

I am trying too input a new number into a databasee only if it does not already exist, in order to do this, I am doing the following
if (isset($_POST['Number'])) {
$number = ($_POST['Number']);
$NCheck = "SELECT COUNT(*) FROM `DS_Numbers` WHERE `Number` = '$number' ";
$stmt = $dbCon->prepare($sql);//* prepared statement for result which populates table
$stmt->execute();
$result = $stmt->fetchColumn(0);
if($result > 0){ // if there is a value then block
$ENumber = "This Number already exists";
$errors[] = 'error';
} else {
echo "number done";// echo statement to see that it has reached this
$number1 = ($_POST['Number']);
}
} else {
$errors[] = 'error';
$ENumber = "Please enter only digits from 0-9";
}
What is currently happening is that my PDO statement is not returning, so my result value is always 0, but none of my research has shown that I have an issue with it
any suggestions on where I am going wrong?
I am getting Number from an HTML form on the page, no issues on that side.
First thing to notice is that you are writing your query in a variable called $NCheck, not $sql.
So this line
$stmt = $dbCon->prepare($sql);
should be
$stmt = $dbCon->prepare($NCheck);
Also please note that one of the main advantages in using PDO is to use bound parameters, but you are not doing this and you are in fact exposed to SQL injection since you are just adding that value you get in POST in the query.
If the number must be unique in the table then you should probably add a UNIQUE constraint to the column. You can then just insert the number and if it was not unique the database will return a 'duplicate key' error.
If you use the 'check-first-insert-later' method you are open to race conditions; if two processes try to insert the same number at the same time they will both run the select at the same time and they will both get zero and try to insert, which without a unique constraint will work for both.

PHP filter_input() and bindValue()

I am writing a basic CRUD application to get my head around PHP. I am a little confused as to what exactly the following code is doing. I understand the general concept of it but I am not 100% sure of the logic going on.
I am hoping someone might be able to help me understand it a bit better?
This is my script
<?php
//Establish connection to db
require_once 'includes/db.php';
//Array for validation
$errors = array();
//Sanitize the fields to ensure db integrity.
$title = filter_input(INPUT_POST, 'title', FILTER_SANITIZE_STRING);
$release_date = filter_input(INPUT_POST, 'release_date', FILTER_SANITIZE_NUMBER_INT);
$publisher = filter_input(INPUT_POST, 'publisher', FILTER_SANITIZE_STRING);
$system = filter_input(INPUT_POST, 'system', FILTER_SANITIZE_STRING);
$rating = filter_input(INPUT_POST, 'rating', FILTER_SANITIZE_NUMBER_INT);
$num_players = filter_input(INPUT_POST, 'num_players', FILTER_SANITIZE_NUMBER_INT);
if($_SERVER['REQUEST_METHOD']=='POST'){
//Validate the form
if(empty($title)){
$errors['title'] = true;
}
if(empty($release_date)){
$errors['release_date'] = true;
}
if(empty($publisher)){
$errors['publisher'] = true;
}
if(empty($system)){
$errors['system'] = true;
}
if(empty($rating)){
$errors['rating'] = true;
}
if(empty($num_players)){
$errors['num_players'] = true;
}
//If no errors
if(empty($errors)){
//Build SQL Statement
$sql = $db->prepare("INSERT INTO videogames SET title = :title, release_date = :release_date, publisher = :publisher, system = :system, rating = :rating, num_players = :num_players");
//Bind values
$sql -> bindValue(':title', $title, PDO::PARAM_STR);
$sql -> bindValue(':release_date', $release_date, PDO::PARAM_STR);
$sql -> bindValue(':publisher', $publisher, PDO::PARAM_STR);
$sql -> bindValue(':system', $system, PDO::PARAM_STR);
$sql -> bindValue(':rating', $rating, PDO::PARAM_INT);
$sql -> bindValue(':num_players', $num_players, PDO::PARAM_INT);
//Execute SQL
$sql -> execute();
//Redirect back to homepage
header('Location: index.php');
exit();
}
}
?>
I am a little confused as to what this line of code is doing:
$title = filter_input(INPUT_POST, 'title', FILTER_SANITIZE_STRING);
Is it assigning the value in the input field of the form to the $title variable?
Also this line:
$sql -> bindValue(':title', $title, PDO::PARAM_STR);
I read in the documentation that this
Binds a value to a corresponding named or question mark placeholder in
the SQL statement that was used to prepare the statement.
If I am storing the data already in the $title variable then is there another way to prepare my SQL statement?
I'd appreciate any help, as I am trying to expand my knowledge of PHP. Many thanks!
Question 1 - filter_input
I am a little confused as to what this line of code is doing:
$title = filter_input(INPUT_POST, 'title', FILTER_SANITIZE_STRING);
Is it assigning the value in the input field of the form to the $title variable?
Yes.
filter_input was introduced in PHP 5. What this line of code is doing is grabbing the input variable ($_POST['title']), and then applying a SANITIZE method on it, that is; "Strip tags, optionally strip or encode special characters."
Question 2 - bindValue
Also this line:
$sql -> bindValue(':title', $title, PDO::PARAM_STR);
If I am storing the data already in the $title variable then is there another way to prepare my SQL statement?
I don't actually understand this question, however I'll try my best.
This is a PDO method to bind a value to a prepared query. Effectively, it does this:
Hey MySQL, here's a query I need you to run
Query these tables
Get these columns back
MySQL does this
Hey MySQL, remember that query I asked you to run? Here's the values to filter the result set
You can read more about preparing and binding here: http://use-the-index-luke.com/sql/where-clause/bind-parameters
Another way in which you could prepare the query is the use of mysqli, but the logic is the same.
This question was asked a year ago. I didn't revive it, OP edited his question and it was bumped. I see nothing wrong answering a question if OP still shows interest in an answer (by bumping) no matter the age of the question

SQLSTATE[HY000]: General error with PHP and PDO

I have a little login script.
function login($sql) {
try {
$fbhost = "localhost";
$fbname = "foodbank";
$fbusername = "root";
$fbpassword = "";
$DBH = new PDO("mysql:host=$fbhost;dbname=$fbname",$fbusername,$fbpassword);
$DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$STH = $DBH->query($sql);
$STH->setFetchMode(PDO::FETCH_ASSOC);
session_start();
if ($row = $STH->fetch()) {
$_SESSION['username'] = "$row[username]";
header("Location:index.php");
}
} catch(PDOException $e) {
echo $e->getMessage();
}
}
EDITS:
index.php
$sql = "SELECT username from users where username = ". $_POST['username'] ." AND password = ". $_POST['password'] ."";
login($sql);
Changed above from insert to select query. Now I get new error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'pvtpyro' in 'where clause'
Based on your latest edit: You can't fetch results with PDO after executing an INSERT query. See here: http://www.php.net/manual/en/pdostatement.fetch.php#105682
Edit: I suppose, since the function's called "login", you want to have something like this as $sql: "SELECT password FROM users WHERE username = :username", and then iterate over the results with the while loop, and then log in the user if the password matches?
Edit2: Based on your edit to provide a SELECT query: DO NOT USE THIS QUERY. What you are doing is NOT SQL injection proof. Never ever use variables from user input (i.e. $_POST, $_GET et al) and put them unfiltered into an SQL query. Please look up the term "prepared statements" here at SO or Google.
As you can see, since you forgot to put single ticks (apostrophes) before and after the double quotes, MySQL thinks that your input refers to another column ("pvtpyro") instead of comparing the value in the column against a string. ALWAYS use the ":username", ":password" syntax (the one with prepended colons) or your queries will be unsafe and enormously dangerous to your application.
The constructor of PDO uses 2 variables which are not defined in the code you supplied - $fbhost and $fbname.
EDIT:
You're calling session_start() inside the while loop, which can cause errors. Take it out of the loop.
EDIT 2:
You should really debug the code. Either via putting die in different parts of the code, outputting some helpful information just before (which is the less preferred way) OR by using xdebug and an IDE, which will allow you to run line by line, and see the exact state of each variable and such.
If I undestand correctly, $data $STH->execute($data); should be an array, even if value is one. So, you may try replacing that query with $STH->execute(array($data));
edited:
Change your lines to this:
$data = array($_POST["username"], $_POST["password"]);
$sql = "INSERT INTO users (username, password) value (?, ?)";
$STH = $DBH->prepare($sql);
$STH->execute($data);
Seems to me that you're not connected to your database properly... I had this error earlier today and it was for that reason. Either that or you have an incorrect string

Empty MySQL query result in PHP

Here's the problematic PHP function:
//Get data associated with $criteria from db
function getUserData($criteria, $value) {
//obtain user data from db based on $criteria=$value
global $pdo;
//echo $criteria . " " . $value;
try {
$sql = 'SELECT id, first, last, email, userid FROM users WHERE :criteria= :value';
//var_dump($sql);
$st = $pdo->prepare($sql);
$st->bindValue(':criteria', $criteria);
$st->bindValue(':value', $value);
$st->execute();
}
catch (PDOException $ex) {
$error = "Failed to obtain user data.";
$errorDetails = $ex->getMessage();
include 'error.html.php';
exit();
}
$row = $st->fetch();
//var_dump($row);
if ($row)
{
$userdata = array();
$userdata['id'] = $row['id'];
$userdata['first'] = $row['first'];
$userdata['last'] = $row['last'];
$userdata['email'] = $row['email'];
$userdata['userid'] = $row['userid'];
return $userdata;
}
return FALSE;
}
I use this function to return a whole row of data associated with specific column in it.
When used at it's current state, with a call like that getUserData("email", "John_Stewart_2013"), it returns false, meaning an empty result, while the same query runs fine in MySQL CLI.
If I, on the other hand, substitute the query string $sql with :
$sql = "SELECT id, first, last, email, userid FROM users WHERE $criteria='$value'";
And comment out the bindValue calls, Every thing runs fine in PHP, and the query returns as desired.
But the problem is, those function arguments are user-submitted form data, meaning the solution is vulnerable to SQL Injection.
What's wrong here in the first query form?
You can't use bindValue with column names I'm afraid.
If you think about what a prepared statement is, this should become more obvious. Basically, when you prepare a statement with the database server, it creates an execution plan for the query beforehand, rather than generating it at the time of running the query. This makes it not only faster but more secure, as it knows where it's going, and the datatypes that it will be using and which are going to be input.
If the column/table names were bindable in any way, it would not be able to generate this execution plan, making the whole prepared statement idea somewhat redundant.
The best way would be to use a hybrid query like so:
$sql = "SELECT id, first, last, email, userid FROM users WHERE $criteria = :value";
I'm going to hope that the $criteria column isn't entirely free form from the client anyway. If it is, you'd be best limiting it to a specific set of allowed options. A simplistic way to do would be to build an array of allowed columns, and check if it's valid with in_array, like so:
$allowed_columns = array('email', 'telephone', 'somethingelse');
if (!in_array($criteria, $allowed_columns))
{
$error = "The column name passed was not allowed.";
$errorDetails = $ex->getMessage();
include 'error.html.php';
exit;
}

Categories