Generate new username and check if already exist

Hi,

I need to generate unique username automatically, do you think these two functions can work?

function km_create_random_username($db_user_conn){

  //set the random id length 
  $km_random_username_length = 6; 
  //generate a random id encrypt it and store it in $rnd_id 
  $km_random_username = uniqid(rand(),1); 
  //to remove any slashes that might have come 
  $km_random_username = strip_tags(stripslashes($km_random_username)); 
  //Removing any . or / and reversing the string 
  $km_random_username = str_replace(".","",$km_random_username); 
  $km_random_username = strrev(str_replace("/","",$km_random_username)); 
  //finally I take the first 6 characters from the $km_rnd_id 
  $km_random_username = substr($km_random_username,0,$km_random_username_length); 

  if(!km_check_random_username($db_user_conn, $km_random_username)){
    return $km_random_username;
  }

}


function km_check_random_username($db_user_conn, $km_random_username) {

  $query = "SELECT km_user_username FROM km_users WHERE km_user_username=?";
  $stmt = mysqli_prepare($db_user_conn, $query);
  mysqli_stmt_bind_param($stmt, 's', $km_random_username);
  mysqli_stmt_execute($stmt);
  mysqli_stmt_store_result($stmt);
  $rows = mysqli_stmt_num_rows($stmt);
  mysqli_stmt_fetch($stmt);

  if($rows > 0) {

    km_create_random_username($db_user_conn);

  }else{

    return false;

  }

}

I’m trying to decide whether you’re just going to get into a never-ending spiral. Your create function calls your check function, which, if it finds the name already exists, calls the create function. I think I’d prefer to have the create function just loop around creating, then checking, and have the check function only check. The check function would return a Boolean, and the create function would keep running until it is sure the name is unique.

In your check routine, there’s no need to call fetch, is there? All you care about is whether you found zero or one entry for that username.

Do you even need a check?
Make the column unique, try an insert, if it works, no problem, if not, the name exists, so try again with another name.

3 Likes

Hi @SamA74 thanks for your answer, so if the table username is unique and I put the insert query into a try/catch block then when an exception is created I need to call the function km_create_random_username again in the catch block and try with another insert which I suppose I need to put again in the catch block is it correct?

repeat to create and insert a new user name until no exception is thrown. I would say the user name creation could be simplified, just have a look at this functions examples:

https://www.php.net/manual/en/function.random-bytes.php

Hi how do I repeat it? Thanks for your suggestion on create random string

A simple while loop. Set the condition to true, and set it to false when a valid username is created.

$doloop = true;
while ($doloop == true) {
  try {
     make_new_username();
     $doloop = false;
   } catch ex {
   // do nothing.
   }
}

(I haven’t used PHP exceptions, so that’s pseudo-code unless it happens to be the correct syntax.)

1 Like

do-while.

(A Do-While loop executes 1 time guaranteed, and then loops back if the condition is true at the end.)

Hi @droopsnoot thanks for your help. The function make_new_username() does generate the username and checks if it is already in the database then if it is in the database return true other wise false? Is it correct? Do I still need to use unique index for the table username? Thanks for your patience

A bit of column A, a bit of column B:

do {
  $name = km_create_random_username()
} while ( ! km_insert_username($name) )

If km_insert_username executes a database query “INSERT INTO table(‘name’) VALUES(?)” (and then feeds it the $name parameter), and the table has a UNIQUE condition on ‘name’, then the query should return either a success (an object of some form), or FALSE.

If it returns false, then ! FALSE = TRUE. And the loop will continue (because the while condition was true, so it starts over). If it returns something other than false (which is when it actually inserts a row), then ! (something) is FALSE, and the loop terminates.

This of course assumes you don’t set your database connection to throw an exception on error; otherwise you have to try-catch it:

do {
  try {
     $name = km_create_random_username()
     km_insert_username($name)
     break;
  } catch {
  }
} while ( true )

same theory, but this time instead of returning ‘false’, the query will throw an exception, which pushes code execution into the catch block. The catch block does nothing, you hit the while, and go back to the top because true is always true. Once you DON’T get an exception (because it succeeded in inserting a row), the code will continue to the ‘break’,which terminates the loop.

I think that’s probably a better approach than separately checking that the new username hasn’t already been taken. Let the database do the work.

To look at it from a completely different perspective:

What IS your database table layout? Why wouldn’t you make username a unique (or even Primary Key) of that table?

If the table layout suggests/requires you to put that unique condition in place, as droop and others have said, use what is available to you; the database can in one query do both the check and insert-if-allowed, rather than having to make two queries.

Hi @m_hutley yes I can make the username a unique index, how do I achieve this with a single query?

Read the first paragraph of post #10.

ALTER TABLE table_name ADD CONSTRAINT constraint_name UNIQUE KEY(column);

Well said @droopsnoot.

Hi @m_hutley sorry to bother you again, how can I test it?

It’s going to be difficult to test as a full function without subverting the code (thus defeating the purpose of the test) or filling your database with a lot of test data.

You can test individual parts of the code simply enough by mocking the results of the parts you arent testing at any given time. This is called Mocking.

You can test the whole function if you’re willing to flood your table with test data (obviously, don’t do this in production environment!). I would add a $tries counter to 0, and then inside your catch block, set to increment said counter; loop the function to try and create several thousand usernames, and see if any of the results have a counter value greater than 0…

Hi, after setting the username table as unique in the database I’ve created something like this:

function km_admin_new_username ($db_user_conn) {

    // Query database to insert a new user
    $query = "INSERT INTO km_users (km_user_first_name, km_user_last_name, km_user_role, km_user_username, km_user_email ) VALUES ('pippo', 'cipolla', '4', '446616', 'pippo@em.it')";
    $stmt = mysqli_prepare($db_user_conn, $query);
    mysqli_stmt_execute($stmt);

}

do {
  try {
     
     km_admin_new_username ($db_user_conn);
     break;
  } catch (Exception $e) {

  	echo "error";
  }
} while ( true )

I thought It should print error string is it correct? I don’t see anything printed :frowning:

What is the function returning?