PHP Prepared Statement not executing correctly - php

I have been staring at a problem for far to long now.
I have a PHP file that is preparing an insert query, binding the params, and executing the query (Simple).
QUERY
$stmt = $db->prepare("SELECT insert_user(?, ?, ?, ?, ?, ?)");
For some reason the $stmt object returned by execute returns -1 for the affected rows. If I alter the code to do just an insert query with the values I wanted to bind, hard coded instead, the query works just fine.
HARD CODED QUERY
$db->query("SELECT insert_user('Test', 'Account', 'testAccount#testApp.io', 'testAccount9', '1980-01-01', 1)");
Something is going wrong in the bind_param section. I have errors turned on and am checking for mysqli errors too, but both are returning no errors.
PHP FILE
...
$postdata = file_get_contents("php://input");
if (isset($postdata) && !empty($postdata)) {
$request = json_decode($postdata);
}
if (
validate_string($request->fname)
&& validate_string($request->lname)
&& validate_integer(intval($request->gender))) {
$stmt = $db->prepare("SELECT insert_user(?, ?, ?, ?, ?, ?)");
if ($stmt) {
$stmt->bind_param("ssssis", $first_name, $last_name, $email, $password, $gender, $dob);
$first_name = $request->fname;
$last_name = request->lname;
$email = $request->email;
$password = password_hash($request->password, PASSWORD_BCRYPT);
$gender = intval($request->gender);
$dob = $request->dob;
$stmt->execute();
if ($stmt->affected_rows > 0) {
echo toJson('success');
} else {
echo toJson('fail');
}
} else {
echo toJson("Prepare failed: (" . $db->errno . ") " . $db->error);
}
} else {
echo toJson('fail - passed data not valid');
}
...
I feel the error must be simple at this point, but I have tried at least 23,432 different things to no success.

My guess the problem is here "ssssis" .
The fifth should be string, the last integer. Maybe this should work?

Related

Do i need to use $stmt = $conn->prepare for this prepared statement?

The current syntax for my prepared statements is incorrect, where I: (INSERT...?, ?, ?)
I have tried to copy syntax from different examples but it seems the more I try the more I break my login system.
I have somewhat conflicting examples and am not sure which syntax is the correct one to use. Do I need to use $stmt = $conn->prepare before INSERT?
// create preprepared statement
$sql = "INSERT INTO `user` (username, password, email) VALUES (?, ?, ?)";
// check if sql statement is correct
if ($stmt = mysqli_prepare($connection, $sql)) {
// Add the variables to the stmt
mysqli_stmt_bind_param($stmt, "sss", $param_username, $param_hashed_password, $param_email);
$param_username = $username;
$param_password = $hashed_password;
$param_email = $email;
// Attempt to execute the stmt
if(mysqli_stmt_execute($stmt)) {
// If statement executed
$_SESSION["username"] = $username;
header("location: login.php");
At the moment it's not inserting any values into my db and user registration is failing.
EDIT:
$password = $_POST['password'];
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
It has just occurred to me that this might be incorrect usage of password_hash?
Do i need to use "$stmt = $conn->prepare" for this prepared statement? - in short, No! You must however use the prepare method to actually generate the prepared statement, it MUST be done before actually attempting to do the INSERT and it is wise to assign that to a variable so that you can fork the program logic dependant upon success/failure.
My preference is to do as below - use a try/catch block and use the return values or variables at various stages to determine whether to throw meaningful(?) exceptions to help debug - so as example you could do this
/*
assumed that $username,$password & $email
are all defined and available at ths stage.
also assumed that `session_start()` has
been called and that no html output occurs
before this point ( unless using output buffering )
*/
try{
# example
$sql = "INSERT INTO `user` ( `username`, `password`, `email` ) VALUES (?, ?, ?)";
$stmt = $connection->prepare( $sql );
if( $stmt ){
/* if there were no problems continue with the database operations */
$stmt->bind_param('sss', $username, $hash, $email );
/* not sure how the hash was generated so just put this in to be sure... */
$hash = password_hash( $password, PASSWORD_BCRYPT );
$result = $stmt->execute();
$stmt->free_result();
$stmt->close();
if( $result ){
$_SESSION['username'] = $username;
exit( header( 'Location: login.php' ) );
} else {
throw new Exception('Bogus! There was some sort of problem...');
}
} else {
throw new Exception('Failed to prepare sql query');
}
}catch( Exception $e ){
exit( $e->getMessage() );
}

PHP SQL Query is not returning anything using bind_param (returning false)

I have a register PHP function that registers a user like so
//Function to create a new user
public function register($firstName, $lastName, $email, $state, $city, $password, $zipCode, $isHMan, $skills, $experience)
{
$password = md5($password);
$stmt = $this->conn->prepare("INSERT INTO handyman(firstName, lastName, email, state, city, password, zipCode, isHMan, skills, experience) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->bind_param("ssssssssss", $firstName, $lastName, $email, $state, $city, $password, $zipCode, $isHMan, $skills, $experience);
$result = $stmt->execute();
$stmt->close();
if ($result) {
return true;
} else {
return false;
}
}
This function works perfectly when called and everything is inserted into my handyman table (including the md5 hashed password). My problem is with my login function. This is the function:
public function login($email, $password) {
$password1 = md5($password);
$stmt = $this->conn->prepare("SELECT * FROM `handyman` WHERE email = ? AND password = ?");
$stmt->bind_param("ss", $email, $password1);
$result = $stmt->execute();
$stmt->close();
if (mysqli_num_rows($result) != 0) {
echo "results acquired";
return true;
} else {
echo "no results";
return false;
}
}
When I run this I get this warning:
Warning: mysqli_num_rows() expects parameter 1 to be mysqli_result,
boolean given.
And the program outputs no results.
After doing a search here on stackoverflow, people were saying that this is caused by an error in the SQL query and the $result variable actually is returning a false boolean value. I input the query directly into my database with an email and password from my database and it executed perfectly. I cannot for the life of me figure out what is wrong. My values and SQL query seem to be correct. I am guessing it may have something to do with the bind_param function but I don't know what. Any help would be appreciated.
You have mixed procedural style mysqli functions with object oriented.
You'll need to adjust that code you have to this:
public function login($email, $password) {
$password1 = md5($password); // don't do this btw
$stmt = $this->conn->prepare("SELECT * FROM `handyman` WHERE email = ? AND password = ?");
$stmt->bind_param("ss", $email, $password1);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows != 0) {
echo "results acquired";
return true;
} else {
echo "no results";
return false;
}
}
Note: In order to check num_rows you need to have called store_result first.
You also need to alter your register method too since its looking at a failure bool from execute:
public function register($firstName, $lastName, $email, $state, $city, $password, $zipCode, $isHMan, $skills, $experience)
{
$password = md5($password); // ahem .. no
$stmt = $this->conn->prepare("INSERT INTO handyman(firstName, lastName, email, state, city, password, zipCode, isHMan, skills, experience) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->bind_param("ssssssssss", $firstName, $lastName, $email, $state, $city, $password, $zipCode, $isHMan, $skills, $experience);
$stmt->execute();
$stmt->store_result();
if ($stmt->affected_rows) {
return true;
} else {
return false;
}
}
Note: Using affected_rows also needs store_result to be called. Don't check on the bool result of execute since that is used for failures in sql.
PASSWORDS:
Please look into password_hash() and password_verify() for your storage and login procedures. md5 is insecure, and out dated. Its beyond the scope of this Q/A to provide the full working usage of those functions. Please do look into them though.

Insert data into MySQLi via PHP - WAMP server

I have a wamp server setup. It works perfectly :)
I then entered phpMyAdmin and created a table. With an android app I have made, I would like to insert a record in my database. The android (java) code is correct, I'm 100% sure of that. When I create a record though, it doesn't work.
Since I don't know PHP very well at all I assume my mistake lies somewhere in Register.php
Here is the file:
Any insight into what my problem is would be fantastic!
Please note that I am using my correct public ip in the true file. I just entered a random one for the code below. Also, I have created a user with permissions required (in the place of username and password). The database "database" also DOES exist.
Register.php
$con = mysqli_connect("http://148.12.0.153:3306","username","password", "database");
$username = $_POST["username"];
$email = $_POST["email"];
$password = $_POST["password"];
$phone = $_POST["phone"];
$balance = $_POST["balance"];
$NameAndSurname = $_POST["NameAndSurname"];
$DateOfBirth = $_POST["DateOfBirth"];
$SchoolName = $_POST["SchoolName"];
$Gender = $_POST["Gender"];
$Grade = $_POST["Grade"];
$Class = $_POST["Class"];
$Country = $_POST["Country"];
$Province = $_POST["Province"];
$Address = $_POST["Address"];
$City = $_POST["City"];
$PostalCode = $_POST["PostalCode"];
$statement = mysqli_prepare($con, "INSERT INTO users (username, email, password, phone, balance, NameAndSurname, DateOfBirth, SchoolName, Gender, Grade, Class, Country, Province, Address, City, PostalCode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
mysqli_stmt_bind_param($statement, "ssssisssiisssssi", $username, $email, $password, $phone, $balance, $NameAndSurname, $DateOfBirth, $SchoolName, $Gender, $Grade, $Class, $Country, $Province, $Address, $City, $PostalCode);
mysqli_stmt_execute($statement);
mysqli_stmt_close($statement);
mysqli_close($con);
Ok a number of things to mention here.
First you are using the android app to launch this Register.php script on your Apache server, just like it was a web page, so this script is running on the server and not your phone or tablet. Therefore Apache and MySQL and the script are all running on the WAMPServer PC. So your connection string does not need some real ip address, it can use and should use something like localhost or 127.0.0.1
Next your database access code is assuming everything will just happen correctly and this may not be the case see above paragraph. So always check status codes and report back the status's to the calling program so it can make sensible decisions about what to do next. Its also a good idea to log errors to the PHP Error log, so when this goes live you can check logs and see if anything is going wrong without needing to run the phone app.
So try these changes :
// init the reply class
$result = new stdClass();
$result->status = 'OK';
$con = mysqli_connect("127.0.0.1","username","password", "database");
if ( ! $con ) {
$result->status = 'ERROR';
$result->error_code = mysqli_connect_errno();
$result->error_message = mysqli_connect_error();
// terminate and report to error log
error_log('Database connection failed'.mysqli_connect_error(), 0);
echo json_encode($result); // return status as json
exit;
}
// You should never use data sent from the screen without
// validating it and cleaning it up so you need some sort of
// $_POST = validate_sanity($_POST);
$username = $_POST["username"];
$email = $_POST["email"];
$password = $_POST["password"];
$phone = $_POST["phone"];
$balance = $_POST["balance"];
$NameAndSurname = $_POST["NameAndSurname"];
$DateOfBirth = $_POST["DateOfBirth"];
$SchoolName = $_POST["SchoolName"];
$Gender = $_POST["Gender"];
$Grade = $_POST["Grade"];
$Class = $_POST["Class"];
$Country = $_POST["Country"];
$Province = $_POST["Province"];
$Address = $_POST["Address"];
$City = $_POST["City"];
$PostalCode = $_POST["PostalCode"];
$sql = "INSERT INTO users
(username, email, password, phone,
balance, NameAndSurname, DateOfBirth,
SchoolName, Gender, Grade, Class,
Country, Province, Address, City,
PostalCode)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$statement = mysqli_prepare($con, $sql );
if ( ! $statement ) {
$result->status = 'ERROR';
$result->error_code = mysqli_errno();
$result->error_message = mysqli_error();
// terminate and report to error log
error_log('Database connection failed'.mysqli_error(), 0);
echo json_encode($result); // return status as json
exit;
}
$res = mysqli_stmt_bind_param($statement, "ssssisssiisssssi",
$username, $email, $password, $phone, $balance,
$NameAndSurname, $DateOfBirth, $SchoolName, $Gender,
$Grade, $Class, $Country, $Province, $Address, $City,
$PostalCode);
if ( ! $res ) {
$result->status = 'ERROR';
$result->error_code = mysqli_errno();
$result->error_message = mysqli_error();
// terminate and report to error log
error_log('Database connection failed'.mysqli_error(), 0);
echo json_encode($result); // return status as json
exit;
}
if ( mysqli_stmt_execute($statement) ) {
$result->status = 'OK';
$result->message = 'Row deleted';
echo json_encode($result); // return status as json
exit;
} else {
$result->status = 'ERROR';
$result->error_code = mysqli_errno();
$result->error_message = mysqli_error();
// terminate and report to error log
error_log('Database DELETE failed'.mysqli_error(), 0);
echo json_encode($result); // return status as json
exit;
}
//mysqli_close($con);
//PHP will do all the connection and statment closing automatically
// So you dont actually need to do any of this unless you are running
// a script the will consume large numbers of statement and you may
// feel it necessary to close them out to kepp the memory footprint smaller
Change the mysqli_stmt_close to
mysqli_stmt_close($statement) or die(mysqli_error());
This will give you a more precise error as to why this is failing.

mysqli insert statement problems

am getting the following error from my code:
Binding parameters failed: (1064) You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? (Name, Address, Location, Phone, Email, Time, Website, Photo1, Rating, Date_Pu' at line 1
Can anyone help me out please? Here is my code:
include("mysqli.php");
$search_tbl = mysql_query("SELECT * from listing_title where listing_title_ID = '$main_id'");
$tbl_name = $search_tbl['tbl_name'];
$stmt = $db->stmt_init();
global $tbl_name;
if($stmt->prepare("INSERT INTO ? (Name, Address, Location, Phone, Email, Time, Website, Photo1, Rating, Date_Published, categories_ID) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"))
{
$stmt->bind_param('sssssssssisi',$tbl_name,$title,$address,$location,$phone,$email,$time,$website,$name,$rating,$date,$sub_cat);
$title = $_POST['name'];
$email = $_POST['email'];
$address = $_POST['address'];
$location = $_POST['location'];
$phone = $_POST['phone'];
$time = $_POST['time'];
$rating = $_POST['rating'];
$main = $_POST['main'];
$website = $_POST['website'];
$date = date('Y-m-d');
$stmt->execute();
$stmt->close();
}
else
{
echo "Binding parameters failed: (" . $stmt->errno . ") " . $stmt->error;
}
}
else
{
echo 'a';
}
your script appears to be incomplete, but doing the best i could with what you had this is what you need. first of all, ditch whatever mysqli wrapper crap you are using. it is teaching you bad principles.
first file, your db info. call it config.php or whatever the hell you want. use require once instead of include. also, ditch the parenthesis around the requires these are not necessary at all, and use single quotes instead of double quotes. single quotes are treated as strings while double quotes php will search for variables inside, thus spending more resources from the cpu/cache.
config.php
$host = 'localhost';//your db host
$user = 'someuser'; //your db user
$pass = 'somepass'; //your db password
$name = 'somedb'; //the name of your db
$mysqli = new mysqli($host,$user,$pass,$name);
if(mysqli_connect_errno()) {
echo "Connection Failed: " . mysqli_connect_errno();
exit;
}else{
global $mysqli;//make your db connection available globally
}
Now for your script
script.php
require_once 'config.php';
//keep your post variables up here. you still need to santize and trim these
$title = $_POST['name'];
$email = $_POST['email'];
$address = $_POST['address'];
$location = $_POST['location'];
$phone = $_POST['phone'];
$time = $_POST['time'];
$rating = $_POST['rating'];
$main = $_POST['main'];
$website = $_POST['website'];
$date = date('Y-m-d');
global $mysqli;//fetch your db connection
$stmt = $mysqli->prepare("SELECT tbl_name from listing_title where listing_title_ID = ? ");
$stmt->bind_param('i',$main_id);
if($stmt->execute()) {
$stmt->bind_result($tbl_name);
$stmt->close();
$stmt = $mysqli->prepare("INSERT INTO ".$tbl_name."
(Name, Address, Location, Phone, Email, Time, Website, Photo1, Rating, Date_Published, categories_ID)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);");
$stmt->bind_param('ssssssssisi',$title,$address,$location,$phone,$email,$time,$website,$name,$rating,$date,$sub_cat);
if($stmt->execute()) {
$stmt->close();
}else{
$stmt->close();
//catch the error
}
}else{
$stmt->close();
//throw an exception or handle the error here.
}
Please note, this still needs work. you need to sanitize and trim your variables. here's an example function. to include funcs, just add a require_once to the config.php file, and it will be included in any file you include config.php in.
example of this:
require_once 'funcs.php';
example sanitize function:
funcs.php
function security($value) {
if(is_array($value)) {
$value = array_map('security', $value);
} else {
if(!get_magic_quotes_gpc()) {
$value = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
} else {
$value = htmlspecialchars(stripslashes($value), ENT_QUOTES, 'UTF-8');
}
$value = str_replace("\\", "\\\\", $value);
}
return $value;
}
to call the function
$title = security(trim($_POST['name']));
I leave the sanitizing to you. its a valuable exercise and you have an example that will sanitize anything, whether it be integers, arrays, objects, or strings.
you should only use trims on strings though. if you want to sanitize an entire array, just use the security function.
good luck.

"No data supplied for parameters in prepared statement"

So I am reworking a script to include prepared statements. It was working fine before, but now I am getting "No data supplied for parameters in prepared statement" when the script runs. What is the issue here?
<?php
require_once("models/config.php");
$firstname = htmlspecialchars(trim($_POST['firstname']));
$firstname = mysqli_real_escape_string($mysqli, $firstname);
$surname = htmlspecialchars(trim($_POST['surname']));
$surname = mysqli_real_escape_string($mysqli, $surname);
$address = htmlspecialchars(trim($_POST['address']));
$address = mysqli_real_escape_string($mysqli, $address);
$gender = htmlspecialchars(trim($_POST['gender']));
$gender = mysqli_real_escape_string($mysqli, $gender);
$city = htmlspecialchars(trim($_POST['city']));
$city = mysqli_real_escape_string($mysqli, $city);
$province = htmlspecialchars(trim($_POST['province']));
$province = mysqli_real_escape_string($mysqli, $province);
$phone = htmlspecialchars(trim($_POST['phone']));
$phone = mysqli_real_escape_string($mysqli, $phone);
$secondphone = htmlspecialchars(trim($_POST['secondphone']));
$secondphone = mysqli_real_escape_string($mysqli, $secondphone);
$postalcode = htmlspecialchars(trim($_POST['postalcode']));
$postalcode = mysqli_real_escape_string($mysqli, $postalcode);
$email = htmlspecialchars(trim($_POST['email']));
$email = mysqli_real_escape_string($mysqli, $email);
$organization = htmlspecialchars(trim($_POST['organization']));
$organization = mysqli_real_escape_string($mysqli, $organization);
$inriding = htmlspecialchars(trim($_POST['inriding']));
$inriding = mysqli_real_escape_string($mysqli, $inriding);
$ethnicity = htmlspecialchars(trim($_POST['ethnicity']));
$ethnicity = mysqli_real_escape_string($mysqli, $ethnicity);
$senior = htmlspecialchars(trim($_POST['senior']));
$senior = mysqli_real_escape_string($mysqli, $senior);
$student = htmlspecialchars(trim($_POST['student']));
$student = mysqli_real_escape_string($mysqli, $student);
$order= "INSERT INTO persons (firstname, surname, address, gender, city, province, postalcode, phone, secondphone, email, organization, inriding, ethnicity, senior, student_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = mysqli_prepare($mysqli, $order);
mysqli_stmt_bind_param($stmt, "sssd", $firstname, $surname, $address, $gender, $city, $province, $postalcode, $phone, $secondphone, $email, $organization, $inriding, $ethnicity, $senior, $student);
mysqli_stmt_execute($stmt);
echo $stmt->error;
$result = mysqli_query($mysqli,$stmt);
if ($result === false) {
echo "Error entering data! <BR>";
echo mysqli_error($mysqli);
} else {
echo "User $firstname added <BR>";
}
?>
Thanks in advance.
You have only bound four arguments, by the control string "sssd", but you have many parameters. When binding variables with mysqli, you need one character for each parameter, for example:
mysqli_stmt_bind_param($stmt, "sssdsssssssssdd", $firstname, $surname, $address,
$gender, $city, $province, $postalcode, $phone, $secondphone, $email,
$organization, $inriding, $ethnicity, $senior, $student);
(I'm assuming senior and student are integers, and need the "d" code.)
You don't need to treat any of your variables with mysqli_real_escape_string() -- that's the point of using parameters. If you do escaping as well, you'll get literal backslash characters in your data in the database.
And you never need to use htmlspecialchars() in any case - you would use that when outputting to HTML, not when inserting to the database. You're going to get literal sequences like & in your data in the database.
Re your next error:
"Catchable fatal error: Object of class mysqli_stmt could not be converted to string in..."
This is caused by the following:
$result = mysqli_query($mysqli,$stmt);
That function expects the second argument to be a string, a new SQL query. But you've already prepared that query, so you need the following:
$result = mysqli_stmt_execute($stmt);

Categories