5

I want to generate a unique 4-6 char long AlphaNumeric string to save in db with each record(user). The db field has a unique index, so trying to save a pre-existing string generates an error. Right now I am generating a random string and using try-catch, so when adding a new record if it throws an exception, I generate another random string and attempt to save again, and the code keep trying until it adds a record successfully. This whole solution not only looks heavy but also ugly, so I want to change it. I am interested in an elegant solution, so any help/guidance is welcome.

JohnD
  • 3,884
  • 1
  • 28
  • 40
Bryan
  • 645
  • 1
  • 6
  • 18
  • Possible dupe http://stackoverflow.com/questions/6826106/generate-random-string – JohnD Sep 17 '11 at 21:10
  • This may be related: http://stackoverflow.com/questions/7194506/unique-unpredictable-12-digit-integer-id/7194533#7194533 – Arnaud Le Blanc Sep 17 '11 at 21:11
  • @JohnD i know how to generate random strings, but the main issue is to add the approach to elegantly 1) generate the string in zend framework and 2) make sure the random string in unique in the database, and if it already exist, generate a new one until it found a unique string – Bryan Sep 17 '11 at 21:18
  • As it's already indexed, you could (for the moment) generate N keys and then run a select query prior inserting. You could then find out in one round-trip which keys have been already consumed and then go on with the unused ones try-and-error again. Not really a solution, but I don't find the related question I'm looking for. – hakre Sep 17 '11 at 21:19
  • what is this random string for? Is it a string that some user will have to enter manually? Does it *has* to be 4-6 characters long? What happens if you run out of bits (ie. when all possible combinations has been generated)? – Yanick Rochon Sep 17 '11 at 21:23
  • the key will be used to represent a user in the system as we have to hide user's identity from others within the system. we don't want to use the sequential numeric IDs(primary-key) as there are some areas where users can abuse the system if all the user-references are in sequence. Using a sequential series can also reveal which user joined earlier and which one joined later, and we don't want to reveal these details as well – Bryan Sep 17 '11 at 22:19

1 Answers1

6

With the given information :

  • id must be unique
  • id must not be numeric
  • id must not represent a sequential series
  • id will not be input by the user

The PHP function uniqid is exactly what you need. Though it returns a 13 character long hexadecimal value.


** Edit **

Yes, uniqid will return a seamingly sequential number, but we can get around this easily. Consider this code

class IDGenerator {
   //const BIT_MASK = '01110011';

   static public function generate() {

      $id = uniqid();

      $id = base_convert($id, 16, 2);
      $id = str_pad($id, strlen($id) + (8 - (strlen($id) % 8)), '0', STR_PAD_LEFT);

      $chunks = str_split($id, 8);
      //$mask = (int) base_convert(IDGenerator::BIT_MASK, 2, 10);

      $id = array();
      foreach ($chunks as $key => $chunk) {
         //$chunk = str_pad(base_convert(base_convert($chunk, 2, 10) ^ $mask, 10, 2), 8, '0', STR_PAD_LEFT);
         if ($key & 1) {  // odd
            array_unshift($id, $chunk);
         } else {         // even
            array_push($id, $chunk);
         }
      }

      return base_convert(implode($id), 2, 36);
   }
}

echo IDGenerator::generate();

Which will give results like

ivpa493xrx7
d173barerui
evpoiyjdryd
99ej19mnau2

Since there is nothing added or modified, except shuffling the bits around, there should not be any duplicated values and everything seems random. Voilà!

** Update (2014-02-24) **

I update this piece of code since the time it was originally posted. You may find the revised version here

Community
  • 1
  • 1
Yanick Rochon
  • 51,409
  • 25
  • 133
  • 214
  • but the value generated by uniqid are in sequence, though getting two adjacent values is very difficult, but values are in sequence. – Bryan Sep 17 '11 at 22:38