PHP PDO bindParam() and MySQL BIT - php

I'm trying to update data in a table with a BIT type value in it, like the following :
// $show_contact is either '1' or '0'
$query->bindValue(':scontact', $show_contact, PDO::PARAM_INT);
The problem is, it never changes the value, it remains '1' as set on PHPMyAdmin. I tried different PDO::PARAM_ types without success, everything else is working.
edit full script
$sql = "UPDATE users SET password = :password, address = :address, postal = :postal, city = :city, contact = :contact, show_contact = :scontact WHERE id = :id";
$query = $dbh->prepare($sql);
$query->bindValue(':id', $user->id, PDO::PARAM_INT);
$query->bindValue(':password', md5($password), PDO::PARAM_STR);
$query->bindValue(':address', $address, PDO::PARAM_STR);
$query->bindValue(':postal', $postal, PDO::PARAM_STR);
$query->bindValue(':city', $city, PDO::PARAM_STR);
$query->bindValue(':contact', $contact, PDO::PARAM_STR);
$query->bindValue(':scontact', $show_contact, PDO::PARAM_INT);
$query->execute();

PDO has a bit of a bug where any parameter passed to a query, even when specifically given as PDO::PARAM_INT is treated as a string and enclosed with quotes. READ THIS
The only way to tackle it is to try the following:
$show_contact = (int)$show_contact;
$query->bindValue(':scontact', $show_contact, PDO::PARAM_INT);

I believe that the BIT type is mapped to PDO's PARAM_BOOL. Try using it with strictly boolean input.
$show_contact = (bool) $show_contact; // '0' => FALSE, '1' => TRUE
$query->bindValue(':scontact', $show_contact, PDO::PARAM_BOOL);

Related

Someone kindly help me here: SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

public function register($uname,$age,$sex,$image,$dpart,$joind,$job,$uposition,$phone,$umail,$upass,
$unumber,$address,$nssf,$bank,$passp,$home,$village,$nation,$permit)
{
try
{
$new_password = password_hash($upass, PASSWORD_DEFAULT);
$stmt = $this->conn->prepare("INSERT INTO users(user_name,birth,gender,image,job_title,curr_position,telephone,department,joining_date,user_email,user_pass,box_number,residence,nssf_number,bank_account,passport_number,home_district,village,nationality,work_permit)
VALUES(:uname,:age,:sex,:image,:dpart,:joind,:job,:uposition,:phone,:umail,:upass,:unumber,:nssf,:bank,:passp,:home,:village,:nation,:permit)");
$stmt->bindparam(":uname",$uname);
$stmt->bindparam(":age",$age);
$stmt->bindparam(":sex",$sex);
$stmt->bindparam(":image",$image);
$stmt->bindparam(":dpart",$dpart);
$stmt->bindparam(":joind",$joind);
$stmt->bindparam(":job",$job);
$stmt->bindparam(":uposition",$uposition);
$stmt->bindparam(":phone",$phone);
$stmt->bindparam(":umail",$umail);
$stmt->bindparam(":upass",$new_password);
$stmt->bindparam(":unumber",$unumber);
$stmt->bindparam(":address",$address);
$stmt->bindparam(":nssf",$nssf);
$stmt->bindparam(":bank",$bank);
$stmt->bindparam(":passp",$passp);
$stmt->bindparam(":home",$home);
$stmt->bindparam(":village",$village);
$stmt->bindparam(":nation",$nation);
$stmt->bindparam(":permit",$permit);
$stmt->execute();
return $stmt;
}
catch(PDOException $e)
{
echo $e->getMessage();
}
}
I'm posting this as a community wiki answer, since there shouldn't be any rep from this, nor do I want rep from it; given an answer that can't determine which one is missing.
It's the one for $stmt->bindparam(":address",$address); that is missing in the VALUES().
Also make sure that all variables do contain value.
PHP's error reporting will be of help:
http://php.net/manual/en/function.error-reporting.php
Side note: Using a code editor that automatically finds matching words when double-clicked and using the same naming convention would have helped you greatly.
One (free) of which that has option, is Notepad++.
Your sql statement is inconsistent: the table columns and the values
to insert don't correspond. For example, in a curr_position field
you are trying to insert a value of :joind, etc.
Also, in terms of number, the columns and the values to insert don't
coincide: 19 values to insert in 20 fields.
Recommendations:
My recommendation would be to always use column names for the marker names. Then you know exactly to which markers you are inserting the corresponding values.
NB: Markers: "...VALUES (:marker1, :marker2, ...);".
You should also define the type of input parameteres that you are binding. Example:
$stmt->bindparam(":age", $age, PDO::PARAM_INT);
Try to maintain some consistency between the function parameters and the field names, if it's possible and... makes sense.
My code proposal would look like this:
<?php
public function register(
$userName
, $birth
, $gender
, $image
, $jobTitle
, $currPosition
, $telephone
, $department
, $joiningDate
, $userEmail
, $userPass
, $boxNumber
, $residence
, $nssfNumber
, $bankAccount
, $passportNumber
, $homeDistrict
, $village
, $nationality
, $workPermit
) {
try {
$newUserPassword = password_hash($userPass, PASSWORD_DEFAULT);
$stmt = $this->conn->prepare('INSERT INTO users (
user_name,
birth,
gender,
image,
job_title,
curr_position,
telephone,
department,
joining_date,
user_email,
user_pass,
box_number,
residence,
nssf_number,
bank_account,
passport_number,
home_district,
village,
nationality,
work_permit
) VALUES (
:user_name,
:birth,
:gender,
:image,
:job_title,
:curr_position,
:telephone,
:department,
:joining_date,
:user_email,
:user_pass,
:box_number,
:residence,
:nssf_number,
:bank_account,
:passport_number,
:home_district,
:village,
:nationality,
:work_permit
)');
$stmt->bindparam(":user_name", $userName, PDO::PARAM_STR);
$stmt->bindparam(":birth", $birth, PDO::PARAM_INT);
$stmt->bindparam(":gender", $gender, PDO::PARAM_STR);
$stmt->bindparam(":image", $image, PDO::PARAM_STR);
$stmt->bindparam(":job_title", $jobTitle, PDO::PARAM_STR);
$stmt->bindparam(":curr_position", $currPosition, PDO::PARAM_STR);
$stmt->bindparam(":telephone", $telephone, PDO::PARAM_STR);
$stmt->bindparam(":department", $department, PDO::PARAM_STR);
$stmt->bindparam(":joining_date", $joiningDate, PDO::PARAM_STR);
$stmt->bindparam(":user_email", $userEmail, PDO::PARAM_STR);
$stmt->bindparam(":user_pass", $newUserPassword, PDO::PARAM_STR);
$stmt->bindparam(":box_number", $boxNumber, PDO::PARAM_INT);
$stmt->bindparam(":residence", $residence, PDO::PARAM_STR);
$stmt->bindparam(":nssf_number", $nssfNumber, PDO::PARAM_INT);
$stmt->bindparam(":bank_account", $bankAccount, PDO::PARAM_STR);
$stmt->bindparam(":passport_number", $passportNumber, PDO::PARAM_STR);
$stmt->bindparam(":home_district", $homeDistrict, PDO::PARAM_STR);
$stmt->bindparam(":village", $village, PDO::PARAM_STR);
$stmt->bindparam(":nationality", $nationality, PDO::PARAM_STR);
$stmt->bindparam(":work_permit", $workPermit, PDO::PARAM_STR);
$stmt->execute();
return $stmt;
} catch (PDOException $e) {
echo $e->getMessage();
}
}
Good luck!
Thank you all for your efforts and input i figured out the problem actually was this one:
$stmt->bindParam("userPass", $newUserPassword, PDO::PARAM_STR);
which had to be changed to this:
$stmt->bindParam("userPass", $userPass, PDO::PARAM_STR);
I was trying to use a parameter that i had not defined all because i di this:
$newUserPassword = password_hash($userPass, PASSWORD_DEFAULT);
So I thought of replacing it in the bindParameters....Hope it helps other!

Invalid parameter number for PDO statement

Next code gives me an error SQLSTATE[HY093]: Invalid parameter number
$sql = "INSERT INTO `users` (`id`, `date_install`, `date_ping`, `cc`, `uv`, `pid`, `pv`, `aff_id`, `sub_id`, `channel`, `cid`, `os`, `av`, `db`) VALUES (:id, :date_install, now(), :country, :updaterVersion, :productId, :productVersion, :affiliateId, :subId, :channel, :commandId, :os, :av, :defaultBrowser) "
. "ON DUPLICATE KEY UPDATE `date_install` = :date_install, `date_ping` = now(), `cc` = :country, `uv` = :updaterVersion, `pid` = :productId, `pv` = :productVersion, `aff_id` = :affiliateId, `sub_id` = :subId, `channel` = :channel, `cid` = :commandId, `os` = :os, `av` = :av, `db` = :defaultBrowser ";
$statement = $database->prepare($sql);
$statement->bindValue(":id", $user->id, PDO::PARAM_INT);
$statement->bindValue(":date_install", $user->date_install, PDO::PARAM_STR);
$statement->bindValue(":country", $user->cc, PDO::PARAM_STR);
$statement->bindValue(":updaterVersion", $user->uv, PDO::PARAM_INT);
$statement->bindValue(":productId", $user->pid, PDO::PARAM_INT);
$statement->bindValue(":productVersion", $user->pv, PDO::PARAM_INT);
$statement->bindValue(":affiliateId", $user->aff_id, PDO::PARAM_INT);
$statement->bindValue(":subId", $user->sub_id, PDO::PARAM_INT);
$statement->bindValue(":channel", $user->channel, PDO::PARAM_STR);
$statement->bindValue(":commandId", $user->cid, PDO::PARAM_INT);
$statement->bindValue(":os", $user->os, PDO::PARAM_STR);
$statement->bindValue(":av", $user->av, PDO::PARAM_STR);
$statement->bindValue(":defaultBrowser", $user->db, PDO::PARAM_STR);
$statement->execute();
I have no idea what is wrong and if I copy/paste SQL into console and replace all values manually everything works. Also if I comment out part ON DUPLICATE KEY..., also works.
If emulation mode is turned off for your PDO instance, you won't be able to use the same placeholder name more than once in the query.
Besides, for the ON DUPLICATE it is not necessary either, as you can always use the VALUES operator that will take the value from the VALUES clause:
ON DUPLICATE KEY UPDATE `date_install` = VALUES(date_install), ...
PDO Doesn't allow repetition of variable names. Your ON DUPLICATE KEY UPDATE should have it's own variable names. Which also means you have to assign your variables 2 times. This is a sad limitation :(.
In order to use the same parameter names twice, you must set PDO::ATTR_EMULATE_PREPARES attribute to true:
$database->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

Reversing AES_ENCRYPT Difficulties

I am consistently receiving null whenever I try to reverse the cipher text that I store in a MySQL database through PHP.
The PHP code used to insert the data:
public function insertChildren($employeeID, $empData, $key, $childName, $childBirth, $childGender, $childSSN, $isStep, $isFoster, $isStudent, $isHandicap, $address) {
$conn = $this->connect('insurance');
$insertChildren = $conn->prepare('INSERT INTO dependent_children (emp_id, ssn, name, dob, gender, handicap, student, foster, step, address) VALUES (:emp_id, AES_ENCRYPT(:ssn, AES_ENCRYPT(:key, UNHEX(sha1(:empData)))), :name, :dob, :gender, :handicap, :student, :foster, :step, :address)');
$insertChildren->bindParam(":emp_id", $employeeID, PDO::PARAM_INT);
$insertChildren->bindParam(":name", $childName, PDO::PARAM_STR);
$insertChildren->bindParam(':dob', $childBirth, PDO::PARAM_STR);
$insertChildren->bindParam(':empData', $empData, PDO::PARAM_STR);
$insertChildren->bindParam(':gender', $childGender, PDO::PARAM_STR);
$insertChildren->bindParam(':key', $key);
$insertChildren->bindParam(':ssn', $childSSN, PDO::PARAM_LOB);
$insertChildren->bindParam(':handicap', $isHandicap, PDO::PARAM_STR);
$insertChildren->bindParam(':student', $isStudent, PDO::PARAM_STR);
$insertChildren->bindParam(':foster', $isFoster, PDO::PARAM_STR);
$insertChildren->bindParam(':step', $isStep, PDO::PARAM_STR);
$insertChildren->bindParam(':address', $address, PDO::PARAM_STR);
$insertChildren->execute();
}
The SQL query that I thought would reverse it:
SELECT CAST(AES_DECRYPT(ssn, AES_DECRYPT('/ same random hexadecimal key bound in the php statement / ', unhex(sha1('1234')))) AS CHAR(50)) from dependent_children
Please note 1234 is the value that should be bound to the empData field in the PHP. I thought the latter query would correctly decrypt the first, but such is the case. Instead, I receive null. I am sure it is something simple, but I have not been able to locate the error source. Thanks so much!

filtered/sanitised form textarea causes MySQL insert to fail

I'm using the below prepared statement to submit data from a form via post
the user data has been filtered and sanitised. using PHP filter functions However the insert into MySQL fails on inserting the "Address" value which comes from a text area form input. I've tried various versions of the data and it seems that any input with newlines "\n\t\r" fails as well as their HTML encoded equivalents. I didn't think these were problamatic for MySQL? Am I missing the obvious?
Thanks
PS follows:
//DB_Connection
$SP1 = 'call account_register(:Title, :Name, :Surname, :Email, :Mobile, :Password, :Status, :LoginIP, :Token, :TokenExpiry, :Company, :BuildingNumber, :Address, :Street, :City, :County, :PostCode, :ReturnStatus)';
$Statement = $DBConnection->prepare($SP1);
#Bind parameters
$Statement->bindParam(':Title', $_UserData['Title'], PDO::PARAM_STR);
$Statement->bindParam(':Name', $_UserData['Name'], PDO::PARAM_STR);
$Statement->bindParam(':Surname', $_UserData['Surname'], PDO::PARAM_STR);
$Statement->bindParam(':Email', $_UserData['Email'], PDO::PARAM_STR);
$Statement->bindParam(':Mobile', $_UserData['Mobile'], PDO::PARAM_STR);
$Statement->bindParam(':Password', $_UserData['Password'], PDO::PARAM_LOB);
$Statement->bindParam(':Status', $_UserData['UserStatus'], PDO::PARAM_INT);
$Statement->bindParam(':LoginIP', $_UserData['LoginIP'], PDO::PARAM_STR);
$Statement->bindParam(':Token', $_UserData['ActivationToken'], PDO::PARAM_LOB);
$Statement->bindParam(':TokenExpiry', $_UserData['TokenExpiry'], PDO::PARAM_STR);
$Statement->bindParam(':Company', $_UserData['Company'], PDO::PARAM_STR);
$Statement->bindParam(':BuildingNumber', $_UserData['BuildingNumber'], PDO::PARAM_STR);
//$Statement->bindParam(':Address', $_UserData['Address'], PDO::PARAM_STR);
//$Address = 'line 1
line 2'; //This is the value of $_USERData after using FILTER_SANITIZE_SPECIAL_CHARS insert fails
//$Address = 'Line 1'; //after changing the value of the $_UserData to this the insert is successful
//$Address = 'line 1
line 2'; //After extracting from the $_UserData This fails
$Address = 'Line 1
line 2
line 3'; //This fails. I thought newlines were ok?
$Statement->bindParam(':Address', $Address, PDO::PARAM_STR);
$Statement->bindParam(':Street', $_UserData['Street'], PDO::PARAM_STR);
$Statement->bindParam(':City', $_UserData['City'], PDO::PARAM_STR);
$Statement->bindParam(':County', $_UserData['County'], PDO::PARAM_STR);
$Statement->bindParam(':PostCode', $_UserData['PostCode'], PDO::PARAM_STR);
$ReturnStatus = null; //Return variable for SP must be defined
$Statement->bindParam(':ReturnStatus', $ReturnStatus, PDO::PARAM_INT | PDO::PARAM_INPUT_OUTPUT, 1);
$Statement->execute();
Well finally got to the bottom of the problem. retyping the line calling the SP solved it.
I do copy and paste a lot so maybe some miscellaneous unprintable chars got in that line. That's the only thing I can think as my retype is exactly the same...

PDO with bound parameters sql query If statement in update

I am attempting to create a 'trigger' not in the sql sense but I want to update the date_added field when the status field is set to 100
$sql='UPDATE table
SET status=:status,
date_added=[PSEUDO CODE :status=100 ? now() : null;]
WHERE id=:id';
$stmt=$conn->prepare($sql);
$stmt->bindParam(':id', $id, PDO::PARAM_STR);
$stmt->bindParam(':status', $status, PDO::PARAM_STR);
$stmt->bindParam(':sign_id', $sign_id, PDO::PARAM_STR);
$stmt->execute();
Would it be better to attempt this in the sql query(unsure how to
perform this) or on the php page (think I could stumble through
that one) prior to issuing the query?
Are there any performance gains one way or the other?
Thanks in advance for any help
date_added = :date
$date = $status == 100 ? date('Y-m-d H:i:s') : null;
$stmt->bindParam(":date", $date);
You can do this comparison in MySQL as well using IF. I don't think that one is particularly faster than the other, but it makes more sense to me to use PHP for the comparison.
This should work:
$sql='UPDATE table
SET status=:status,
date_added=IF(:status=100, NOW(), NULL)
WHERE id=:id';
$stmt=$conn->prepare($sql);
$stmt->bindParam(':id', $id);
$stmt->bindParam(':status', $status);
$stmt->execute();
But using the same parameter name twice in one statement only works if you configure PDO to use emulated prepare. If you use native prepare, then you should make distinct parameter names even for the same value:
$sql='UPDATE table
SET status=:status,
date_added=IF(:status_again=100, NOW(), NULL)
WHERE id=:id';
$stmt=$conn->prepare($sql);
$stmt->bindParam(':id', $id);
$stmt->bindParam(':status', $status);
$stmt->bindParam(':status_again', $status);
$stmt->execute();
Or else it'd be simpler to use positional parameters. You can also skip the bindParam() if you just pass an array of values to execute(). There's an example of the latter two changes together:
$sql='UPDATE table
SET status=?,
date_added=IF(?=100, NOW(), NULL)
WHERE id=?';
$stmt=$conn->prepare($sql);
$stmt->execute([$status, $status, $id]);

Categories