Generate unique key and save in the database not working - php

I'm trying to create a unique key to save information in the database, but for some reason the $randStr variable has nothing at the end. After submitting, I get only a new id and email, nothing appears in the keystring. What's wrong?
Here's my sql table:
create table users (
id int(11) not null PRIMARY KEY AUTO_INCREMENT,
keystring varchar(110) not null,
email varchar(220) not null
);
php code:
<?php
include_once 'includes/dbh.php';
function checkKeys($conn, $randStr) {
$sqlcheck = "SELECT keystring FROM users";
$result = mysqli_query($conn, $sqlcheck);
while ($row = mysqli_fetch_assoc($result)) {
if ($row['keystring'] == $randStr) {
$keyExists = true;
break;
} else {
$keyExists = false;
}
}
return $keyExists;
}
function generateKey($conn) {
$keyLength = 8;
$str = "0123456789abcdefghijklmnopqrstuvwxyz";
$randStr = substr(str_shuffle($str), 0, $keyLength);
$checkKey = checkKeys($conn, $randStr);
while($checkKey == true) {
$randStr = substr(str_shuffle($str), 0, $keyLength);
$checkKey = checkKeys($conn, $randStr);
}
return $randStr;
}
$recipient = $_POST['emailFor'];
$sender = $_POST['senderEmail'];
$message = $_POST['message'];
$sql = "INSERT INTO users (keystring, email) VALUES('$randStr', '$sender');";
mysqli_query($conn, $sql);

You are not calling generateKey function. Call this function and store return value to $randStr before inserting in database.
Note: please see SQL injection prevention for same.
$randStr = generateKey($conn);
// Your insert statement

While the accepted answer is correct as far as it goes, there is much here that can be improved. Reading all the existing keys and checking them one by one is intensive, and will get slower as the number of keys increases. It also opens a window for a race condition, where two requests are competing fro resources.
Applying a unique index to the keystring column allows us to perform an atomic 'test and set' with MySQL checking the key and inserting it in one operation.
We can also switch to prepared queries to eliminate the risk of SQL injection.
So, apply a UNIQUE index to the keystring column. This modification to the table only needs to be done once.
ALTER TABLE `users` ADD UNIQUE INDEX `keystring_UNIQUE` (`keystring` ASC) VISIBLE;
Then the new code looks like this:
function generateKey() {
$keyLength = 8;
$str = "0123456789abcdefghijklmnopqrstuvwxyz";
$randStr = substr(str_shuffle($str), 0, $keyLength);
return $randStr;
}
$recipient = $_POST['emailFor'];
$sender = $_POST['senderEmail'];
$message = $_POST['message'];
// This will be required somewhere
$conn = mysqli_connect("xxxx", "xxxx", "xxxx", "xxxx");
// Prepare the query
$stmt = mysqli_prepare($conn, "INSERT INTO users (keystring, email) VALUES(?, ?);");
// If the key generated already exists the loop will try again.
// Limit the collisions before we give up
$collisionLimit = 10;
do {
$randstr = generateKey();
mysqli_stmt_bind_param($stmt, 'ss', $randstr, $sender);
$result = mysqli_stmt_execute($stmt);
// check the result. If it's TRUE all's well and we continue.
// If we have an error throw an Exc eption if it's not a duplicate key error
if ($result === false) {
if (mysqli_stmt_errno($stmt) != 1062) {
throw new Exception(mysqli_stmt_error($stmt));
}
// If we have a collision, check the limit and throw an Exception if necessary
if (--$collisionLimit) {
throw new Exception("Unable to insert key - too many collisions");
}
}
} while ($result===false);

Related

How to validate and insert new random value if it is present in database

This is my code
$lclReferCode = rand(100000,999999);
function random(){
$lclReferCode1 = rand(100000,999999);
return $lclReferCode1;
}
$lclReferCode = random();
$lclQuery = "SELECT * FROM `sign_up` WHERE us_refer_code = '$lclReferCode'";
$qryResult = mysql_query($lclQuery);
if(mysql_num_rows($qryResult) == 0){
$lclQuery = "INSERT INTO sign_up(us_refer_code) values('" . $lclReferCode . "')";
$qryResult = mysql_query($lclQuery);
}else{
random();
}
Here my intention is to insert unique values not the duplicates. Before inserting I am checking the database if this random number is present already or not if it is present I have to generate new values again I have to generate values and match if not present then the insert query executes. can anyone help me.
Like this (untested):
function setRandom(\PDO $PDO){
$stmt = $PDO->prepare("SELECT * FROM `sign_up` WHERE us_refer_code = :us_refer_code");
try{
$stmt->execute([':us_refer_code' => rand(100000,999999)]);
}catch(\PDOException $e){
if($e->getCode() == 23000){ //if I recall right this is duplicate key
setRandom($PDO); //<-- recursive.
}else{
//if not duplicate key re-throw the error.
throw new \PDOException($e->getMessage(), $e->getCode(), $e);
}
}
}
But you have to use PDO, with the error mode set to exception, and with the database field defined as a unique index.
It might not be a bad idea to build in a recursion limit, as you could get in a situation where it never finds a value not entered in the DB. That would look like this.
function setRandom(\PDO $PDO, $limit=0){
if($limit > 1000) throw new \Exception(__FUNCTION__.' recursion limit exceeded');
$stmt = $PDO->prepare("SELECT * FROM `sign_up` WHERE us_refer_code = :us_refer_code");
try{
$stmt->execute([':us_refer_code' => rand(100000,999999)]);
}catch(\PDOException $e){
if($e->getCode() == 23000){ //if I recall right this is duplicate key
setRandom($PDO, ++$limit);//<-- recursive
}else{
//if not duplicate key re-throw the error.
throw new \PDOException($e->getMessage(), $e->getCode(), $e);
}
}
}
This would give it 1000 tries before throwing an exception. It's never good to have code that has a real possibility of creating infinite recursion.
On my sites I have a error handler that shoots me an email if I have any uncaught exceptions. So I would know right away. Of coarse this is optional and some of this is tightly coupled to your particular use case.
Otherwise you will have to use a select inside of a while loop. like this (Also untested).
/**
* return false or the random value to use.
* #return false|int
*/
function ckRandom($lclReferCode){
$lclReferCode = rand(100000,999999);
$lclQuery = "SELECT * FROM `sign_up` WHERE us_refer_code = '$lclReferCode'";
$qryResult = mysql_query($lclQuery);
if(mysql_num_rows($qryResult) == 0){
//no result means the number is not used yet.
return $lclReferCode;
}else{
return false;
}
}
//$limit = 0;
$lclReferCode = false;
while( false === $lclReferCode){
$lclReferCode = ckRandom();
//if($limt > 1000) throw new \Exception(' recursion limit exceeded');
//++$limit;
}
//if $lclReferCode IS NOT false then it is the last random number searched that had no results in the DB
$lclQuery = "INSERT INTO sign_up(us_refer_code) values('" . $lclReferCode . "')";
See the idea is the while loop will trap execution until the result returns something other then 0, if it's 0 then no row exists. (you have this backwards).
And again, this is prone to infinite recursion. I added the "limit" stuff in comments.
That said, I would strongly suggest doing away with any mysql_ functions as they wont work post PHP7.
you need to try like this
function random(){
$lclReferCode = rand(100000,999999);
$lclQuery = "SELECT * FROM `sign_up` WHERE us_refer_code = '$lclReferCode'";
$qryResult = mysql_query($lclQuery);
$checkRow=mysql_num_rows($qryResult);
if ($checkRow>0) {
return random();
}else{
$lclQuery = "INSERT INTO sign_up(us_refer_code) values('" . $lclReferCode . "')";
$qryResult = mysql_query($lclQuery);
return true;
}
}
if (random()===true) {
echo 'inserted succesfully';
}
remember this is demonstration.i recommend to you use mysqli or pdo because mysql is not valid in php7

Database table not updating properly from PHP website code

I am adding straw polls to a website using this tutorial: http://code.tutsplus.com/articles/creating-a-web-poll-with-php--net-14257
I have modified this code so I can create my own polls by entering the poll title and questions which gets stored in a database table. This table is then queried and the polls are loaded from the database. This is working fine, however, when I am trying to return the number of votes for each question the value is coming back 0 every time. The table 'tally' holds the question id, answer id and number of votes.
When I try to insert data into this tally table from the webPoll class, both the QID and AID rows are blank, only the votes value increments each time.
My database is MySQL, the tutorial I followed was to insert data into an SQLite DB, I think this might be the problem but I can't seem to find a solution as of yet.
In summary, I need to get the insert statements in the webPoll class inserting QID, AID & votes values as QID and AID are not inserting.
tally
CREATE TABLE tally (
QID varchar(32) NOT NULL,
AID integer NOT NULL,
votes integer NOT NULL,
PRIMARY KEY (QID,AID))
webPoll Class
$mysql_host = "localhost";
$mysql_database = "vote";
$mysql_user = "root";
$mysql_password = "";
class webPoll {
# makes some things more readable later
const POLL = true;
const VOTES = false;
# number of pixels for 1% on display bars
public $scale = 2;
public $question = '';
public $answers = array();
private $header = '<form class="webPoll" method="post" action="%src%">
<input type="hidden" name="QID" value="%qid%" />
<h4>%question%</h4>
<fieldset><ul>';
private $center = '';
private $footer = "\n</ul></fieldset>%button%\n</form>\n";
private $button = '<p class="buttons"><button type="submit" class="vote">Vote!</button></p>';
private $md5 = '';
/**
* ---
* Takes an array containing the question and list of answers as an
* argument. Creates the HTML for either the poll or the results depending
* on if the user has already voted
*/
public function __construct($params) {
$this->question = array_shift($params);
$this->answers = $params;
$this->md5 = md5($this->question);
$this->header = str_replace('%src%', $_SERVER['SCRIPT_NAME'], $this- >header);
$this->header = str_replace('%qid%', $this->md5, $this->header);
$this->header = str_replace('%question%', $this->question, $this->header);
# seperate cookie for each individual poll
isset($_COOKIE[$this->md5]) ? $this->poll(self::VOTES) : $this- >poll(self::POLL);
}
private function poll($show_poll) {
$replace = $show_poll ? $this->button : '';
$this->footer = str_replace('%button%', $replace, $this->footer);
# static function doesn't have access to instance variable
if(!$show_poll) {
$results = webPoll::getData($this->md5);
$votes = array_sum($results);
}
for( $x=0; $x<count($this->answers); $x++ ) {
$this->center .= $show_poll ? $this->pollLine($x) : $this->voteLine($this->answers[$x],$results[$x],$votes);
}
echo $this->header, $this->center, $this->footer;
}
private function pollLine($x) {
isset($this->answers[$x+1]) ? $class = 'bordered' : $class = '';
return "
<li class='$class'>
<label class='poll_active'>
<input type='radio' name='AID' value='$x' />
{$this->answers[$x]}
</label>
</li>
";
}
private function voteLine($answer,$result,$votes) {
$result = isset($result) ? $result : 0;
$percent = round(($result/$votes)*100);
$width = $percent * $this->scale;
return "
<li>
<div class='result' style='width:{$width}px;'> </div> {$percent}%
<label class='poll_results'>
$answer
</label>
</li>
";
}
/**
* processes incoming votes. votes are identified in the database by a combination
* of the question's MD5 hash, and the answer # ( an int 0 or greater ).
*/
static function vote() {
if(!isset($_POST['QID']) ||
!isset($_POST['AID']) ||
isset($_COOKIE[$_POST['QID']])) {
return;
}
try{
$dbh = new PDO('mysql:host=localhost;dbname=vote', 'root', '');
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
} catch(PDOException $e) {
echo "Error: " . $e->getMessage() . "<br/>";
}
try {
$sth = $dbh->prepare("INSERT INTO tally (QID,AID,votes) values ('QID', 'AID', '1')" );
$sth->execute(array($_POST['QID'],$_POST['AID']));
}
catch(PDOException $e) {
# 23000 error code means the key already exists, so UPDATE!
if($e->getCode() == 23000) {
try {
$sth = $dbh->prepare("UPDATE tally SET votes = votes + 1 WHERE QID='$QID' AND AID='$AID'");
$sth->execute(array($_POST['QID'],$_POST['AID']));
}
catch(PDOException $e) {
webPoll::db_error($e->getMessage());
}
}
else {
webPoll::db_error($e->getMessage());
}
}
# entry in $_COOKIE to signify the user has voted, if he has
if($sth->rowCount() == 1) {
setcookie($_POST['QID'], 1, time()+60*60*24*365);
$_COOKIE[$_POST['QID']] = 1;
}
}
static function getData($question_id) {
try {
$dbh = new PDO('mysql:host=localhost;dbname=vote', 'root', '');
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$STH = $dbh->prepare('SELECT AID, votes FROM tally WHERE QID = ?');
$STH->execute(array($question_id));
}
catch(PDOException $e) {
# Error getting data, just send empty data set
webPoll::db_error($e->getMessage());
return array(0);
}
while($row = $STH->fetch()) {
$results[$row['AID']] = $row['votes'];
}
return $results;
}
/*
* You can do something with the error message if you like. Email yourself
* so you know something happened, or make an entry in a log
*/
static function db_error($error) {
echo "A database error has occured. $error";
exit;
}
}
You are using the prepared statements incorrectly. Inside the values you should have placeholders. The values in the execute are bound to those placeholders.
So:
$sth = $dbh->prepare("INSERT INTO tally (QID,AID,votes) values ('QID', 'AID', '1')" );
Is sending QID, and AID to your DB. If those are integer columns I suspect you'll get 0s in their place.
You should change this to:
$sth = $dbh->prepare("INSERT INTO tally (QID,AID,votes) values (?, ?', '1')" );
Your execute:
$sth->execute(array($_POST['QID'],$_POST['AID']));
Is already set up correctly to pass the two values to the placeholders.
You also need to fix another update further down.
$sth = $dbh->prepare("UPDATE tally SET votes = votes + 1 WHERE QID=? AND AID= ?");
Prepared statements should rarely have variables in them and if they are in there they should have been checked against a whitelist of allowed terms.
You can read more about prepared statements:
http://php.net/manual/en/pdo.prepared-statements.php
https://en.wikipedia.org/wiki/Prepared_statement

send binary files from php to mysql using mysqli

i want to send data into two tables from a php script,the pic and idcard are two binary files that in mysql db and both of them have LONGBLOB type always the first insert query executed successfully(insert into user table) but the second query(insert into `applicant' table) have two situations:
1-if i make the pic and idcard field NULLABLE in db,other data except these two inserted successfully.
2-if i make the pic and idcard field NOT NULLABLE nothing insert into `applicant' table.
and both of two situation i cant save the binary data into my database,where is the problem?
and i also have no problem in $_FILES array and passing them,i can see the details of these fileds when i post it to the php script.
i think something is wrong with the send_long_data but i use it before exactly like now!
here is the script:
require_once('db_connection_config.php');
if (mysqli_connect_errno()) {
print_r("<div class='bs-example center-block'><div id='alertshow' class='bs-example center-block alert alert-danger'><a href='#' class='close' data-dismiss='alert'>×</a><strong>Connection Error:</strong>" . mysqli_connect_errno() . "</div></div>");
exit();
}
$today = date('Y:m:d');
$pass = randomPassword();
$stmt = $mysqli->prepare("INSERT INTO user (password,gender,firstname,lastname,email,phoneNum,mobileNum,Address,UserRoles_userRoleId) VALUES (?,?,?,?,?,?,?,?,3)");
$stmt->bind_param("sissssss", $pass, $_POST['gender'], $_POST['name'], $_POST['lastname'], $_POST['email'], $_POST['phonenum'], $_POST['mobilenum'], $_POST['address']);
$stmt->execute();
$mkey = mysqli_insert_id($mysqli);
$stmt->close();
if(isset($_FILES['pic']) && isset($_FILES['idcard'])){
$pic_file_path = $_FILES['pic']['tmp_name'];
if ( !file_exists($pic_file_path) ) {
throw new Exception('File not found.');
}
$pic_handle = fopen($pic_file_path, "rb");
if ( !$pic_handle ) {
throw new Exception('File open failed.');
}
$pic_content = null;
$idpic_file_path = $_FILES['idcard']['tmp_name'];
$idpic_handle = fopen($idpic_file_path, "rb");
$idpic_content = null;
$stmt = $mysqli->prepare("INSERT INTO applicant (userId,nationalCode,fatherName,birthPlace,nationality,religion,postalCode,picture,idpicture,registrationDate,militaryServiceStatus) VALUES (?,?,?,?,?,?,?,?,?,?,?)");
$stmt->bind_param("isssiisbbss", $mkey, $_POST['nationalcode'], $_POST['fathername'], $_POST['birthcity'], $_POST['nationality'], $_POST['religion'], $_POST['postalcode'], $pic_content, $idpic_content, $today, $_POST['mss']);
while (!feof($pic_handle)) {
$stmt->send_long_data(1, fread($pic_handle, 8192));
}
fclose($pic_handle);
while (!feof($idpic_handle)) {
$stmt->send_long_data(1, fread($idpic_handle, 8192));
}
fclose($idpic_handle);
$stmt->execute();
$stmt->close();
}
}
function randomPassword() {
$alphabet = "abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789";
$pass = array(); //remember to declare $pass as an array
$alphaLength = strlen($alphabet) - 1; //put the length -1 in cache
for ($i = 0; $i < 8; $i++) {
$n = rand(0, $alphaLength);
$pass[] = $alphabet[$n];
}
return implode($pass); //turn the array into a string
}

Get Return Value from SQL Stored Procedure using PHP

So I have a php script that uses a stored procedure to interact with my SQL database. The stored procedure works just fine, the problem is I don't know how to get my php to echo the Return Value from the stored procedure. The stored procedure is basically activating an account using an activation key and sets the username and password.
It basically says "if the activation key provided does not have a username yet, set it to the one provided and RETURN 1, if it already has a username RETURN 2, and if the activation key does not exist RETURN 3". It works perfectly in SQL and even give the correct Return Value. Now how can I get my php to echo that? I have tried the following:
$link = sqlsrv_connect($myServer, array('Database' => $myDB, 'UID' => $myUser, 'PWD' => $myPass));
if($link === FALSE) {
die('Could not connect: ' . sqlsrv_errors(SQLSRV_ERR_ALL));
}
$key = $_REQUEST['key'];
$username = $_REQUEST['username'];
$password = $_REQUEST['password'];
$query = "EXEC Activate_Account";
$query .=" #key = ";
$query .= $key;
$query .=", #user = ";
$query .= $username;
$query .=", #pass = ";
$query .= $password;
$result = sqlsrv_query($link, $query);
while($row = sqlsrv_fetch_array($result))
{
echo $row[0];
}
sqlsrv_close($link);
and
while($row = sqlsrv_fetch_array($result))
{
echo $row['Return Value'];
}
Neither of them echo anything.
To return a value with a stored procedure:
For example:
SQL :
CREATE DEFINER=`user`#`localhost` PROCEDURE `ProcedureName`(IN `Input_Value` INT, OUT `Out_val` INT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
// Your SQL Code
SET Out_val= Your Value;
SELECT Out_val;
END
PHP Code:
$insert = "CALL ProcedureName(:Input_Value,
#Out_val)";
$bdd = new PDO('mysql:host=localhost;dbname=db-name', 'user', 'password');
$stmt = $bdd->prepare($insert);
$stmt->bindParam(':Input_Value', $an_input_value, PDO::PARAM_STR);
$stmt->execute();
$tabResultat = $stmt->fetch();
$Out_val = $tabResultat['Out_val'];
var_dump($Out_val);
The following example enables you to retrieve the RETURN value from a stored procedure and allows you to retrieve the OUTPUT values.
SQL:
CREATE PROCEDURE [ProcedureName]
#input_parameter as int,
#output_parameter as int out
AS
BEGIN
-- Your SQL code
SELECT #output_parameter = #input_parameter;
RETURN 2
END
PHP:
// Connect.
$link = sqlsrv_connect($myServer, array('Database' => $myDB, 'UID' => $myUser, 'PWD' => $myPass));
$returnValue = 0;
$inputParameter = 10;
$outputParameter = 0;
$parameters = [
[&$returnValue, SQLSRV_PARAM_OUT],
[$inputParameter, SQLSRV_PARAM_IN],
[&$outputParameter, SQLSRV_PARAM_OUT],
];
$sql = "EXEC ? = ProcedureName ? ?";
$stmt = sqlsrv_query($link, $sql, $parameters);
if ($stmt === false)
{
// Throw/Handle Error.
}
// $returnValue and $outputParameter should be updated here because they were passed by Reference.
// Retrieve query rows if any.
$rows = [];
while (($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC) ?? false) !== false) {
$rows[] = $row;
}
Some more reading about this topic can be found on the following links: How to retrieve input and output parameters using the sqlsrv driver and Sqlsrv query

create new if already exists PHP

I am creating a random user ID, but I would like to check if the ID already has been used (very unlikely but the chances are there), but somehow this doesn't work. When I look in the database, there is no random character string in the account_id field. Do I call the functions in a wrong way?
function genRandomString() {
$length = 40;
$characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
for ($p = 0; $p < $length; $p++) {
$string .= $characters[mt_rand(0, strlen($characters))];
}
return $string;
}
function createID() {
$cl_id = 'h_u_'.genRandomString();
}
createID();
$sql_query="SELECT * FROM accounts WHERE account_id = :cl_id";
$statement = $conn->prepare($sql_query);
$statement->bindParam(':cl_id', $cl_id, PDO::PARAM_STR);
if ($statement->execute() && $row = $statement->fetch())
{
createID();
}
$conn->exec("INSERT INTO accounts SET
account_id='$cl_id' ,
name='$_POST[name]' ,
email='$_POST[email]' ");
$cl_id is a local variable in createID() function , you need to return your value to your global code ...
function createID() {
return $cl_id = 'h_u_'.genRandomString();
}
you need to check $id in the main code
$id = createID();
$sql_query="SELECT * FROM accounts WHERE account_id = '".$cl_id."'";
$statement = $conn->prepare($sql_query);
1 . You missed to return $c_id in createID(). Change it to:
function createID() {
return 'h_u_'.genRandomString();
}
$cl_id = createID();
2 . You could use good old uniqid() instead of your custom genRandomString().
This would lead to something simpler like:
function createID() {
return 'h_u_'.uniqid();
}
$cl_id = createID();
3 . You'll have to change the if in the database related code to a loop (have a look at my example below)
4 . Your insert query uses unverified $_POST vars. This is highly prone to SQL Injections. If your Database library supports server side prepared statements you should use them and you can feel secure because data is being kept separate from the query syntax. If you are using PHP with MySQL this is the case.
If you are not using server side prepared statements you should escape any $_POST data used in the query by using mysql_real_escape_string() or something like this. In the following example I'm assuming that you are using PHP with MySQL and thatswhy I use a prepared statement.
Taking all this in account may result in a finished script like this:
$sql_query="SELECT * FROM accounts WHERE account_id = :cl_id";
$statement = $conn->prepare($sql_query);
$maxtries = 3; // how many tries to generate a unique id?
for($i = 0; $i < $maxtries; $i++) {
$cl_id = uniqid(); // create a 'unique' id
$statement->bindParam(':cl_id', $cl_id, PDO::PARAM_STR);
if (!$statement->execute()) {
die('db error');
}
$row = $statement->fetch();
if($row) {
continue;
}
break;
}
// if a unique id couldn't get generated even
// after maxtries, then pigs can fly too :)
if($i === $maxtries) {
die('maximum number of tries reached. pigs can fly!');
}
// You should use a prepared statement for the insert to prevent from
// SQL injections as you pass $_POST vars to the query. You should further
// consider to validate email address and the name!
$name = $_POST['name'];
$email = $_POST['email'];
$insert_query = '
INSERT INTO accounts SET
account_id = :account_id,
name = :name,
email = :email';
$insert_statement = $conn->prepare($insert_query);
$insert_statement->bindParam(':account_id', $cl_id, PDO::PARAM_STR);
$insert_statement->bindParam(':name', $name, PDO::PARAM_STR);
$insert_statement->bindParam(':account_id', $email, PDO::PARAM_STR);
if (!$insert_statement->execute()) {
die('db error');
}

Categories