Error that will not show itself - php

I'm trying to code a registration system for a system I am making. Currently, I am receiving a MySQL error that makes me want to tear my head out each and every time I see it.
function UserRegister($user,$pass,$email,$first,$last)
{
$sqlfirst = mysql_real_escape_string($first);
$sqllast = mysql_real_escape_string($last);
$sqluser = mysql_real_escape_string($user);
$hashpass = crypt($pass);
$sqlpass = mysql_real_escape_string($hashpass);
$sqlemail = mysql_real_escape_string($email);
$sql = "SELECT *
FROM planerentalusers
WHERE user = '$sqluser' ";
if($result = mysqli_query($GLOBALS['db'],$sql))
{
$rowcount=mysqli_num_rows($result);
if($rowcount == 1)
{
echo "ERROR: There is already an account with that username! Click <a href='/PHPCalTest/login.php>here </a>to login if this is you. Otherwise, go back and try a different username.";
}
else
{
$sql2 = "INSERT INTO planerentalusers (first,last,user,pass,email) VALUES ('$sqlfirst','$sqllast','$sqluser','$sqlpass','$sqlemail')";
$result2 = mysqli_query($GLOBALS['db'],$sql);
if($result2 == true)
{
return true;
}
else return false;
}
}
else return false;
mysqli_free_result($result);
}
Above is the function that throws the error.
there is no PHP stack trace that is being thrown, so here is what I pinpointed it to: the query is failing. But how, I do not understand. Perhaps someone can point me in the right direction.

That is not a direct answer to your question. It has been solved somewhere between the comment lines.
Now, you can streamline and secure your code if you will:
use prepared statements. It's only natural since you are already using mysqli_* extension. Parameters that you pass to the prepared INSERT statement will be properly escaped.
utilize INSERT IGNORE syntax and check for affected rows with affected_rows. That way you do all you need to do hitting your database only once.
For INSERT IGNORE to work properly you have to have a UNIQUE constraint on username column.
ALTER TABLE planerentalusers ADD UNIQUE (username);
Now if you issue an INSERT IGNORE statement and a username doesn't exist a row will be inserted and affected_rows will return 1. If a username already exists then IGNORE clause will allow your INSERT statement to complete without throwing an error and affected_rows will return 0.
That being said an improved version of your function might look like
function UserRegister($db, $username, $pass, $email, $first, $last) {
$sql = "INSERT IGNORE INTO planerentalusers (first, last, username, pass, email) VALUES (?, ?, ?, ?, ?)";
// prepare the statement
$stmt = $db->prepare($sql);
if (!$stmt) {
die('Can\'t prepare: ' . $db->error); //TODO better error handling
}
// bind parameters
$stmt->bind_param('sssss', $first, $last, $username, $pass, $email);
if (!$stmt) {
die('Can\'t bind parameters: ' . $db->error); //TODO better error handling
}
// execute
$stmt->execute();
if (!$stmt) {
die('Query execution failed: ' . $db->error); //TODO better error handling
}
// get the number of affected rows
$affected_rows = $stmt->affected_rows;
// close the statement
$stmt->close();
return $affected_rows;
}
and the calling code
$first = $_POST['first'];
$last = $_POST['last'];
$username = $_POST['username'];
$pass = crypt($_POST['pass']);
$email = $_POST['email'];
//create a connection to the database
$db = new mysqli('localhost', 'user', 'password', 'dbname');
if ($db->connect_errno) {
die('Connection failed: ' . $db->connect_error); //TODO better error handling
}
if (!UserRegister($db, $username, $pass, $email, $first, $last)) {
echo "ERROR: There is already an account with that username! Click <a href='/PHPCalTest/login.php'>here </a>to login if this is you. Otherwise, go back and try a different username.";
} else {
echo "Account successfully created";
}
Note that
A reference to an open db connection is explicitly passed to the function instead of using $_GLOBALS['db']
presentation logic (echoing an error message and a link) is moved out to the calling code
Basic error handling is implemented throughout the function

Related

Unable to method chaining in MySQLi prepared statement using PHP

I am a beginner to PHP. I tried not to put $conn->prepare($sql_stmt) in one variable and just applied method chaining. But I got "Error while executing".
<?php
include_once 'dbh.inc.php';
if(isset($_POST['submit_btn']))
{
$fullname = $_POST['name'];
$username = $_POST['username'];
$password = $_POST['password'];
$sql_stmt = "INSERT INTO signup (name, username, passwrd) VALUES (?,?,?);";
//prepare and bind
$conn->prepare($sql_stmt)->bind_param("sss", $fullname, $username, $password);
//execute
if($conn->prepare($sql_stmt)->execute())
{
echo "User created";
}
else
{
echo "Error while executing";
}
}
else
{
echo "Unable to sign up.";
}
However if I instantiate $sql = $conn->prepare($sql_stmt) like below
<?php
include_once 'dbh.inc.php';
if(isset($_POST['submit_btn']))
{
$fullname = $_POST['name'];
$username = $_POST['username'];
$password = $_POST['password'];
$sql_stmt = "INSERT INTO signup (name, username, passwrd) VALUES (?,?,?);";
//prepare and bind
$sql = $conn->prepare($sql_stmt);
$sql->bind_param("sss", $fullname, $username, $password);
//execute
if($sql->execute())
{
echo "User created";
}
else
{
echo "Error while executing";
}
}
else
{
echo "Unable to sign up.";
}
It works and returns "User created". Why is that so?
Method chaining is not possible with mysqli. The return value of bind_param() is a boolean. It does not return self. You must call the methods like you showed in the second example:
$sql = $conn->prepare($sql_stmt);
$sql->bind_param("sss", $fullname, $username, $password);
$sql->execute();
In fact, mysqli is not very suitable to be used on its own in your application. If you want something simpler, then use PDO. If for some strange reason you must use mysqli, then you require some kind of abstraction layer that will prevent you from dealing with mysqli functions directly.
As of PHP 8.1, you can pass parameters directly in mysqli_stmt::execute(), which enables you to do method chaining in one line:
$sql = $conn->prepare($sql_stmt)->execute([$fullname, $username, $password]);
Also, please stop checking the return value of execute. You should enable mysqli error reporting instead. How to get the error message in MySQLi?

PHP | SQL - mysqli_stmt_prepare fails and connected to the database

I'm trying to execute a parameterized query to update some stuff in the database.
The problem is that it mysqli_stmt_prepare fails.
The require is used to connect to the database.
require 'includes/dbInclude.php';
if ($codeQuery > 0){
$confirmationUsername = $_GET['confirmationUsername'];
$active = "active";
$noCode = "";
$insertSql = "UPDATE users SET accountStatus = ? WHERE username = $confirmationUsername";
$insertSql2 = "UPDATE users SET confirmationCode = ? WHERE username = $confirmationUsername";
$statement = mysqli_stmt_init($connection);
$statement2 = mysqli_stmt_init($connection);
if (!mysqli_stmt_prepare($statement, $insertSql)){
header("Location: registerComplete.php?error=sqlError1");
exit();
}
elseif (!mysqli_stmt_prepare($statement2, $insertSql2)){
header("Location: registerComplete.php?error=sqlError2");
exit();
}
else{
mysqli_stmt_bind_param($statement, "s", $active);
mysqli_stmt_execute($statement);
mysqli_stmt_bind_param($statement2, "s", $noCode);
mysqli_stmt_execute($statement2);
}
}
dbInclude.php contains:
<?php
//connection variables
$serverName = "localhost";
$dbUsername = "root";
$dbPassword = "";
$dbName = "ecglive";
//connection
$connection = mysqli_connect($serverName, $dbUsername, $dbPassword, $dbName);
//connection error
if(!$connection){
die("There was an error connceting to the database: " . mysqli_connect_error());
}
And where I used it works. I alos tried copy that code to this one just to see if there was any problem connecting to the database. It isn't.
It always goes on the first error if, where it says sqlError1 and if I delete it, then it goes to the sqlError2.
Did I make any mistake?
You need to bind the username in addition to the accountstatus to help mitigate SQL injection.
require 'includes/dbInclude.php';
if ($codeQuery > 0){
$confirmationUsername = $_GET['confirmationUsername'];
$active = "active";
$noCode = "";
$insertSql = "UPDATE users SET accountStatus = ? WHERE username = ?";
$insertSql2 = "UPDATE users SET confirmationCode = ? WHERE username = ?";
$statement = mysqli_stmt_init($connection);
$statement2 = mysqli_stmt_init($connection);
if (!mysqli_stmt_prepare($statement, $insertSql)){
exit(header("Location: registerComplete.php?error=sqlError1") );
} elseif (!mysqli_stmt_prepare($statement2, $insertSql2)){
exit(header("Location: registerComplete.php?error=sqlError2") );
} else{
mysqli_stmt_bind_param($statement, "ss", $active,$confirmationUsername);
mysqli_stmt_execute($statement);
mysqli_stmt_bind_param($statement2, "ss", $noCode,$confirmationUsername);
mysqli_stmt_execute($statement2);
}
}
This code uses a very strange style, and one that's far more verbose than necessary. Here's a more minimal form of same:
require 'includes/dbInclude.php';
// Enable exception reporting
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
if ($codeQuery > 0) {
try {
// Prepare one query that sets both properties.
$stmt = $connection->prepare('UPDATE users SET accountStatus=?,confirmationCode=? WHERE username=?');
// Bind parameters directly form the source, no variables needed.
$stmt->bind_param('ss', 'active', '', $_GET['confirmationUsername']);
// Attempt to execute
$stmt->execute();
}
catch (Exception $e) {
// Error handling here...
header("Location: registerComplete.php?error=sqlError2");
exit();
}
}
You're really not doing a lot here, so there's no reason for that code to be so verbose.
That being said, if this is a registration system for some kind of user access control layer and this isn't an academic project you should stop working on this code before you create a huge mess. Writing your own access control layer is not easy and there are many opportunities to get it severely wrong.
Any modern development framework like Laravel comes with a robust authentication system built-in. This is a solved problem and there's no need for you to try and re-invent the wheel here.
At the absolute least follow recommended security best practices and never store passwords as plain-text or a weak hash like SHA1 or MD5.

PHP bindParam not working - blindValue is not the solution

I can't figure this out. I've googled it and a lot of answers refer to blindValue as the solution but I've also tried that with no luck.
The problem is that the SELECT statement is returning zero records but it should return one record. If I hard code the values into the SQL statement it works but passing them in as parameters isn't. Can some one please help me out with this? Thanks.
<?php
function checklogin($email, $password){
try
{
// Connection
$conn;
include_once('connect.php');
// Build Query
$sql = 'SELECT pkUserID, Email, Password, fkUserGroupID FROM tbluser WHERE Email = :email AND Password = :password';
// $sql = 'SELECT pkUserID, Email, Password, fkUserGroupID FROM tbluser WHERE Email = "a" AND Password = "a"';
// Prepare the SQL statement.
$stmt = $conn->prepare($sql);
// Add the value to the SQL statement
$stmt->bindParam(':email', $email, PDO::PARAM_STR);
$stmt->bindParam(':password', $password, PDO::PARAM_STR);
// Execute SQL
$stmt->execute();
// Get the data in the result object
$result = $stmt->fetchAll(); // $result is NULL always...
// echo $stmt->rowCount(); // rowCount is always ZERO....
// Check that we have some data
if ($result != null)
{
// Start session
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
// Search the results
foreach($result as $row){
// Set global environment variables with the key fields required
$_SESSION['UserID'] = $row['pkUserID'];
$_SESSION['Email'] = $row['Email'];
}
echo 'yippee';
// Return empty string
return '';
}
else {
// Failed login
return 'Login unsuccessful!';
}
$conn = null;
}
catch (PDOexception $e)
{
return 'Login failed: ' . $e->getMessage();
}
}
?>
the connect code is;
<?php
$servername = 'localhost';
$username = 'admin';
$password = 'password';
try {
// Change this line to connect to different database
// Also enable the extension in the php.ini for new database engine.
$conn = new PDO('mysql:host=localhost;dbname=database', $username, $password);
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// echo 'Connected successfully';
}
catch(PDOException $e)
{
echo 'Connection failed: ' . $e->getMessage();
}
?>
I'm connecting to mySQL. Thanks for the help,
Jim
It was a simple but stupid error.
I had a variable called $password also in the connect.php file which was overwriting the $password that I was passing to the checklogin.
Jim

Prepared statement didn't execute

I have simple web application with back end in PHP. I tried for three hours but couldn't solve the problem.
My code is as follows:
1. db_connect.php
<?php
define("HOST", '127.0.0.1');
define("USER", 'root');
define("PASSWORD", '');
define("DB", 'tourist guide');
$con = new mysqli(HOST,USER,PASSWORD,DB);
if ($con->connect_errno){
die("Database Connection Failed");
exit();
}
2. index.php
<?php
require_once 'db_connect.php';
$response = array();
$result = "";
if (isset($_POST['firstname']) && isset($_POST['lastname']) && isset($_POST['email'])&& isset($_POST['password'])) {
$firstname = $_POST['firstname'];
$lastname = $_POST['lastname'];
$email = $_POST['email'];
$password = $_POST['password'];
$stmt = $con->prepare("INSERT INTO
user_accounts
(first_name,last_name,email,password)
VALUES
(?,?,?,?)");
echo 'prepared statement executed. ';
$stmt->bind_param('ssss', $firstname, $lastname, $email, $password);
echo 'values given. ';
$result = $stmt->execute();
echo 'statement is executed. ';
$stmt->close();
}
if ($result) {
$response["success"] = 1;
$response["message"] = "account successfully created.";
echo json_encode($response);
} else {
$response["success"] = 0;
$response["message"] = "An error occurred during registration.";
echo json_encode($response);
}
?>
And the output is as follows:
prepared statement executed. values given. statement is executed. {"success":0,"message":"An error occurred during registration."}
Only the mistake must be $result = $stmt->execute();. Am I wrong here? or the error is something else? plz help.
UPDATE:
From fred's comment I added:
if(!$stmt->execute()){trigger_error("there was an error....".$con->error, E_USER_WARNING);}
Now he real error is showing:
Cannot add or update a child row: a foreign key constraint fails
looks like the error is in my database foreign keys...I'll solve it later..If you know about the error right now, then please say me...thanks fred.
As per OP's wish to close the question, seeing that the error has been found.
It seems like you're closing your DB connection too soon.
Place $stmt->close(); before your closing ?> tag.
Also, instead of the whole if/else block including $result = $stmt->execute(); replace it with if(!$stmt->execute()){trigger_error("There was an error....".$con->error, E_USER_WARNING);} in order to see the real error why it's failing.
You should also replace die("Database Connection Failed"); with
die('Connection failed [' . $con->connect_error . ']');
to get the real error in case your connection should ever fail.
Seeing that the problem is with your foreign keys.

PHP/MySQL: Check if username exists

I'm a beginner in php and I want to check if the username entered already exists.
Here is my code.
<?php
ini_set('display_errors',1);
error_reporting(E_ALL);
if (isset($_POST['submit'])) {
include "connect.php";
ValidateUser();
}
function ValidateUser()
{
if (!empty($_POST['username']) AND !empty($_POST['password'])) {
$queryrow=mysqli_query("SELECT * FROM websiteusers WHERE username = '$_POST['username']'");
if ($rows=mysqli_num_rows($queryrow)=0) {
RegisterUser();
}
}
function RegisterUser() {
echo "works up to here";
}
?>
It doesn't even give me an error despite turning error reporting on.
Have you even initialized a mysqli_connect?
$Connection = mysqli_connect("host","user","pass","database");
Then pass it to a function which uses mysqli_query() by:
function foo ($DB){
mysqli_query($DB,"QUERY HERE");
// Do other stuff
return /* Whatever you wish to return here*/
}
foo($Connection);
What you are trying to achieve can be done very easily with the following code. A bigger concern is security. It is good practice to both sanitize your input every time the user has a chance to input text.
Also, using prepared query's will put yet another layer of security.
Although this isn't using your provided code directly, I believe it is good to teach good habits.
If you have any questions feel free to ask.
$username = $_POST['username']; <-- sanitize this
$message = null;
$mysqli = new mysqli("localhost", "user", "password", "database");
$stmt = $mysqli->prepare("SELECT username FROM websiteusers WHERE username=?");
$stmt->bind_param('s', $username);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($usernamesql);
$stmt->fetch();
if ($stmt->num_rows() > 0) {
RegisterUser();
} else {
$message .= 'username already exists';
}
Later on when you require more items to be queried, or more results to be bound:
$stmt = $mysqli->prepare("SELECT username,password,other1,other2 FROM websiteusers WHERE username=?");
$stmt->bind_param('s', $username); <-- the "s" means the argument is a strings, if a argument is stored as an int use "i", but one character for each argument is required.
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($usernamesql);
$stmt->fetch();
Multiple Arguments:
$stmt = $mysqli->prepare("SELECT username,password,other1,other2 FROM websiteusers WHERE username=? AND authenticated=?");
$stmt->bind_param('si', $username,$isauthenticated); <-- second argument is a INT or BOOL
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($usernamesql,$passwordsql,$other1sql,$other2sql);
$stmt->fetch();
When your expecting multiple results, and lets say you want to dump them into arrays:
$userarray = array();
$stmt = $mysqli->prepare("SELECT username FROM websiteusers WHERE username=?");
$stmt->bind_param('s', $username);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($usernamesql);
while($stmt->fetch()){
array_push($userarray, $usernamesql);
}
$userarray is now an array of all the results fetched from the database.
Here is the right way to do this:
<?php
ini_set('display_errors',1);
error_reporting(E_ALL);
if (isset($_POST['submit'])) {
$mysqli = new mysqli("localhost", "user", "password", "database");
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if(check_user($mysqli, $_POST['username']){
registerUser();
}else{
echo 'user exist, cannot register';
}
}
function check_user($conn, $username){
$query = "SELECT * FROM websiteusers WHERE username = ?";
if ($stmt = $conn->prepare($query)) {
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->close();
}
return $stmt->num_rows === 0;
}
function registerUser() {
echo "registering user ...";
}
Read up on prepared statement

Categories