Can someone tell me if there's a better way doing this?
I am trying to generate a Serial number (function Taken from a Post on
this site )
Check in my database if this Serial exist, if it does,regenerate another
Insert Unique serial into Db
function GenerateSerial() {
$chars = array(0,1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
$sn = '';
$max = count($chars)-1;
for($i=0;$i<20;$i++){
$sn .= (!($i % 5) && $i ? '-' : '').$chars[rand(0, $max)];
}
return $sn;
}
// Generate SN
$serial = GenerateSerial() ;
// Check if it exists, then re-generate
function checkifSerialexist () {
$statement = $dbh->prepare("SELECT `id` FROM `table` WHERE `SN` = :existSN");
$statement->bindParam(':existSN', $serial, PDO::PARAM_STR);
$statement->execute();
$statement->setFetchMode(PDO::FETCH_ASSOC);
$result = $statement->fetchAll();
$serial_count = $statement->rowCount();
if ($serial_count > 0) {
$serial = GenerateSerial() ;
}
return $serial ;
}
//Now insert unique SN
$stmt = $dbh->prepare("INSERT INTO `table` (SN) VALUES (:sn)");
$stmt->bindParam(':sn', $serial, PDO::PARAM_STR);
ect....
You are not calling your checkifSerialexist() function nor are you sending the serial (and the database connection...) as a parameter.
It should be something like:
$serial = GenerateSerial() ;
while (checkifSerialexist($dbh, $serial))
{
$serial = GenerateSerial() ;
}
function checkifSerialexist ($dbh, $serial)
{
$statement = $dbh->prepare("SELECT `id` FROM `table` WHERE `SN` = :existSN");
$statement->bindParam(':existSN', $serial, PDO::PARAM_STR);
$statement->execute();
$statement->setFetchMode(PDO::FETCH_ASSOC);
$result = $statement->fetchAll();
return (count($result) > 0);
}
it's hard to say with the provided code, but my usual logic for unique generation is as follows:
$serial = genSerial();
while(!serialUnique($serial)) {
$serial = genSerial();
}
save($serial);
i always make my check functions (e.g. serialUnique) return a boolean.
It seems like your function that checks if the serial exists does not have the serial you generated earlier.
Depending on where your function is, it might not have access to the variable. Pass it as a parameter and return true or false from the checkIfSerialExists function.
function checkifSerialexist ($serial) {
$statement = $dbh->prepare("SELECT `id` FROM `table` WHERE `SN` = :existSN");
$statement->bindParam(':existSN', $serial, PDO::PARAM_STR);
$statement->execute();
$statement->setFetchMode(PDO::FETCH_ASSOC);
$result = $statement->fetchAll();
$serial_count = $statement->rowCount();
if ($serial_count > 0) {
return true;
} else {
return false;
}
}
Then from where you want to add it to the database, do something like
$serial = GenerateSerial() ;
if(checkifSerialexist($serial)) {
//Now insert unique SN
$stmt = $dbh->prepare("INSERT INTO `table` (SN) VALUES (:sn)");
$stmt->bindParam(':sn', $serial, PDO::PARAM_STR);
//ect....
}
echo implode('-', str_split(substr(strtoupper(md5(microtime().rand(1000, 9999))), 0, 20), 5));
this is just the simply way, you can apply more secure if you need, you can use sha1(), microtime() combined with anothers functions and unique user data, and check if serial generated exsists in your data base... litle bit more secure, like:
$user_mail = 'customer#domain.com';
function salt($leng = 22) {
return substr(sha1(mt_rand()), 0, $leng);
}
function serial(){
$hash = md5($user_mail . salt());
for ($i = 0; $i < 1000; $i++) {
$hash = md5($hash);
}
return implode('-', str_split(substr(strtoupper($hash), 0, 20), 5))
}
This exit: XXXX-XXXX-XXXX-XXXX serial pattern
Related
I have this function which returns only one row, How can I modify the function so that it returns more than one row?
public function getVisitors($UserID)
{
$returnValue = array();
$sql = "select * from udtVisitors WHERE UserID = '".$UserID. "'";
$result = $this->conn->query($sql);
if ($result != null && (mysqli_num_rows($result) >= 1)) {
$row = $result->fetch_array(MYSQLI_ASSOC);
if (!empty($row)) {
$returnValue = $row;
}
}
return $returnValue;
}
There is a function in mysqli to do so, called fetch_all(), so, to answer your question literally, it would be
public function getVisitors($UserID)
{
$sql = "select * from udtVisitors WHERE UserID = ".intval($UserID);
return $this->conn->query($sql)->fetch_all();
}
However, this would not be right because you aren't using prepared statements. So the proper function would be like
public function getVisitors($UserID)
{
$sql = "select * from udtVisitors WHERE UserID = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("s", $UserID);
$stmt->execute();
$res = $stmt->get_result();
return $res->fetch_all();
}
I would suggest storing them in an associative array:
$returnValue = array();
while($row = mysqli_fetch_array($result)){
$returnValue[] = array('column1' => $row['column1'], 'column2' => $row['column2']); /* JUST REPLACE NECESSARY COLUMN NAME AND PREFERRED NAME FOR ITS ASSOCIATION WITH THE VALUE */
} /* END OF LOOP */
return $returnValue;
When you call the returned value, you can do something like:
echo $returnValue[0]['column1']; /* CALL THE column1 ON THE FIRST SET OF ARRAY */
echo $returnValue[3]['column2']; /* CALL THE column2 ON THE FOURTH SET OF ARRAY */
You can still call all the values using a loop.
$counter = count($returnValue);
for($x = 0; $x < $counter; $x++){
echo '<br>'.$rowy[$x]['column1'].' - '.$rowy[$x]['column2'];
}
i am using This code for showing user data record but this code is not work on my side
I want to echo out specific user data. I created a function where I insert multiple arguments (each argument represents a column in the database) and then echo whichever column I want with a simple line of code.
Index.php
include('function.php');
$conn = new MySQLi(localhost, root, password, database);
$user_id = $_SESSION['login_user']; // like 1
$user = user_data($conn, $user_id, 'login', 'pass', 'nikename', 'email');
if(empty($user)){
echo 'error'; // always showing this error
}else{
echo $user['nickename'];
}
Always Showing echo 'error';
function user_data($conn, $user_id){
$data = array();
$user_id = (int)$user_id;
$func_num_args = func_num_args();
$func_get_args = func_get_args();
if ($func_num_args > 1) {
unset($func_get_args[0]);
unset($func_get_args[1]);
$valid = array('login', 'pass', 'nikename', 'email');
$fields = array();
foreach($func_get_args as $arg) {
if(in_array($arg, $valid)) $fields[] = $arg;
}
$fields = '`' . implode ('`, `', $fields) . '`';
if($stmt = $conn->prepare("SELECT $fields FROM `users` WHERE `user_id` = ?")) {
$stmt->bind_param('si', $fields, $user_id);
$stmt->execute();
//here I am trying to convert the result into an array
$meta = $stmt->result_metadata();
while ($field = $meta->fetch_field()) {
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $parameters);
while ($stmt->fetch()) {
foreach($row as $key => $val) {
$x[$key] = $val;
}
$results[] = $x;
}
return $results;
$stmt->close();
}
}
}
Seeing and analyzing your code several times, I think the below will solve your issue.
Add this before your while/fetch loop
$row = array();
stmt_bind_assoc($stmt, $row);
so your code will look like this
$row = array();
stmt_bind_assoc($stmt, $row);
while ($stmt->fetch()) {
foreach($row as $key => $val) {
$x[$key] = $val;
}
$results[] = $x;
}
Also make sure you read the full documentation of bind_param on php.net here
Thanks and Best Regards
I guess, instead of
if($stmt = $conn->prepare("SELECT $fields FROM `users` WHERE `user_id` = ?")) {
$stmt->bind_param('si', $fields, $user_id);
you should go with
if($stmt = $conn->prepare("SELECT $fields FROM `users` WHERE `user_id` = ?")) {
$stmt->bind_param('i', $fields, $user_id);
Bind parameters. Types: s = string, i = integer, d = double, b = blob
As far as you have one argument with type INT you need to pass 'i' as a first parameters.
Try debugging over line by line in that function where you will get exact flaw by var_dump().
I have some user uploaded images that can be sorted and need to save the image position. Was thinking that I could do this easy enough by just using the loop index while iterating through them. However using my $i variable to bind the 3rd param is being passed as a reference and I need the value. How do I get around this?
Here's the code:
$postId = $args['postId'];
$images = explode(",", $args['images']);
$sql = 'INSERT INTO post_image (name,postId,ordinal) VALUES ';
$part = array_fill(0, count($images), "(?, ?, ?)");
$sql .= implode(",", $part);
logit($sql);
try{
$db = DB::getInstance();
$stmt = $db->dbh->prepare($sql);
$count = count($images);
$n = 1;
for($i = 0; $i < $count; $i++){
$stmt->bindParam($n++, $images[$i]);
$stmt->bindParam($n++, $postId);
$stmt->bindParam($n++, $i);
}
$result = $stmt->execute();
if($result !== false) {
return true;
}else {
logit('Query Failed');
return false;
}
}catch(PDOException $e) {
logit($e->getMessage());
return false;
}
I fixed it by using bindValue for the third param.
The following code needs to make use of 3 variables given by the user. By default all of these variables equal to 0.
time (textbox)
city (drop down list)
type (drop down list)
If for example time and city is given by the user, but lets the type zero, it will not return any results.
My question is what is an effective and efficient way to modify my existing code so that if the user chooses not to select time, city or type or any combination of these, there will be results returned?
For example if time 21:00 is added with city number 3, it will show all the types that meet the 2 criteria should be listed.
$question= 'SELECT * FROM events WHERE ABS(TIMESTAMPDIFF( HOUR , `time`, :time )) < 2 AND city=:city AND type=:type';
$query = $db->prepare($question);
$query->bindValue(":time", $time, PDO::PARAM_INT);
$query->bindValue(":city", $city, PDO::PARAM_INT);
$query->bindValue(":type", $type, PDO::PARAM_INT);
$query->execute();
<?php
$question= 'SELECT * FROM events WHERE ';
$hasTime = false;
if(!empty($time)) { // #note better validation here
$hasTime = true;
$question .= 'ABS(TIMESTAMPDIFF( HOUR , `time`, :time )) < 2 ';
}
$hasCity = false;
if(!empty($city)) { // #note better validation here
$hasCity = true;
$question .= 'AND city=:city ';
}
$hasType = false;
if(!empty($type)) { // #note better validation here
$hasType = true;
$question .= 'AND type=:type';
}
$query = $db->prepare($question);
if($hasTime)
$query->bindValue(":time", $time, PDO::PARAM_INT);
if($hasCity)
$query->bindValue(":city", $city, PDO::PARAM_INT);
if($hasType)
$query->bindValue(":type", $type, PDO::PARAM_INT);
$query->execute();
$results = $query->fetchAll();
if(empty($results))
echo 'no results';
else
// $results is an array of arrays
I prefer using an array of conditions, and checking through to see if the conditions exist, to built the individual parts of the SQL query:
$conditions = array(); // Creating an array of conditions.
if ($time) // Checks to see if value exists.
{
$timeCondition = "ABS(TIMESTAMPDIFF( HOUR , `time`, :time )) < 2";
$conditions[] = $timeCondition; // Adds this condition string to the array.
}
if ($city)
{
$cityCondition = "city=:city";
$conditions[] = $cityCondition;
}
if ($type)
{
$typeCondition = "type=:type";
$conditions[] = $typeCondition;
}
$conditionString = implode(" AND ", $conditions); // Gluing the values of the array with " AND " in between the string conditions.
if (count($conditions) > 0) // If conditions exist, add "WHERE " to the condition string.
{
$conditionString = "WHERE ".$conditionString;
}
else // Otherwise, the condition string is blank by default.
{
$conditionString = '';
}
$question= 'SELECT * FROM events '.$conditionString; // If no conditions, will return all from events. Otherwise, conditions will be slotted in through $conditionString.
$query = $db->prepare($question);
if($time)
$query->bindValue(":time", $time, PDO::PARAM_INT);
if($city)
$query->bindValue(":city", $city, PDO::PARAM_INT);
if($type)
$query->bindValue(":type", $type, PDO::PARAM_INT);
$query->execute();
You can use a series of IF() statements in your SQL statement, and return true if the value isn't set. So something like this:
...WHERE IF(:time, ABS(TIMESTAMPDIFF(HOUR, `time`, :time)) < 2, 1)
AND IF(:city, city=:city, 1) AND IF(:type, type=:type, 1)
Build the query dynamically so that if the field is at its default, do not include it in the where clause.
$conditions = array();
if ($_POST['time']) {
$conditions[] = "ABS(TIMESTAMPDIFF( HOUR , `time`, :time )) < 2";
}
if ($_POST['city']) {
$conditions[] = "city=:city";
}
if ($_POST['type']) {
$conditions[] = "type=:type";
}
$conditionString = implode(" AND ", $conditions);
if (count($conditions) > 0) {
$conditionString = "WHERE " . $conditionString;
}
else {
$conditionString = '';
}
$question = 'SELECT * FROM events ' . $conditionString;
This is the login function written using MySQL way
However, the problem exists when it convert into PDO way
MYSQL:
<?
function confirmUser($username, $password){
global $conn;
if(!get_magic_quotes_gpc()) {
$username = addslashes($username);
}
/* Verify that user is in database */
$q = "select UserID,UserPW from user where UserID = '$username'";
$result = mysql_query($q,$conn);
if(!$result || (mysql_numrows($result) < 1)){
return 1; //Indicates username failure
}
/* Retrieve password from result, strip slashes */
$dbarray = mysql_fetch_array($result);
$dbarray['UserPW'] = stripslashes($dbarray['UserPW']);
$password = stripslashes($password);
/* Validate that password is correct */
if($password == $dbarray['UserPW']){
return 0; //Success! Username and password confirmed
}
else{
return 2; //Indicates password failure
}
}
PDO:
<?
function confirmUser($username, $password){
global $conn;
include("connection/conn.php");
$sql = '
SELECT COALESCE(id,0) is_row
FROM user
WHERE UserID = ?
LIMIT 1
';
$stmt = $conn->prepare($sql);
$stmt->execute(array('09185346d'));
$row = $stmt->fetch();
if ($row[0] > 0) {
$sql = '
SELECT COALESCE(id,1) is_row
FROM user
WHERE UserPW = ?
LIMIT 1
';
$stmt = $conn->prepare($sql);
$stmt->execute(array('asdasdsa'));
$row = $stmt->fetch();
if ($row[0] > 0)
return 2;
else
return 0;
}
elseif ($row[0] = 0)
{return 1;}
}
What is the problem ?? And is it necessary to include bind parameter in PDO??? THANKS
Aside from your use of global and your include inside the function (you should investigate an alternative way of structuring your function not to do this), I would change the code as follows:
$sql =
'SELECT id
FROM user
WHERE UserID = ?
AND UserPW = ?
LIMIT 1';
$stmt = $conn->prepare($sql);
$stmt->execute(array(
'09185346d',
'asdasdsa'
));
if ($stmt->rowCount() == 1) {
return 0;
}
else {
return 1;
}
Combing the queries to give a general Authentication error, instead of allowing people to trial valid usernames, and then valid passwords, and then using PDOStatements rowCount method do see if your row was returned.
To answer your second part, it is not necessary to specifically use bindParam to prevent SQL injection.
Here's a quick example of the difference between bindParam and bindValue
$param = 1;
$sql = 'SELECT id FROM myTable WHERE myValue = :param';
$stmt = $conn->prepare($sql);
Using bindParam
$stmt->bindParam(':param', $param);
$param = 2;
$stmt->execute();
SELECT id FROM myTable WHERE myValue = '2'
Using bindValue
$stmt->bindValue(':param', $param);
$param = 2;
$stmt->execute();
SELECT id FROM myTable WHERE myValue = '1'