It need not be meaningful words - more like random password generation, but the catch is - they should be unique. I will be using this for some kind of package / product cod
A random alphanumeric (base 36 = 0..9 + a..z) value that has 7 chars has to have a base 10 representation between 2176782336 and 78364164095, the following snippet proves it:
var_dump(base_convert('1000000', 36, 10)); // 2176782336
var_dump(base_convert('zzzzzzz', 36, 10)); // 78364164095
In order for it to be unique we have to rely on a non-repeating factor, the obvious choice is time():
var_dump(time()); // 1273508728
var_dump(microtime(true)); // 1273508728.2883
If we only wanted to ensure a minimum uniqueness factor of 1 unique code per second we could do:
var_dump(base_convert(time() * 2, 10, 36)); // 164ff8w
var_dump(base_convert(time() * 2 + 1, 10, 36)); // 164ff8x
var_dump(base_convert(time() * 2 + 2, 10, 36)); // 164ff8y
var_dump(base_convert(time() * 2 + 3, 10, 36)); // 164ff8z
You'll notice that these codes aren't random, you'll also notice that time() (1273508728) is less than 2176782336 (the minimum base 10 representation of a 7 char code), that's why I do time() * 2.
Now lets do some date math in order to add randomness and increase the uniqueness factor while complying with the integer limitations of older versions of PHP (< 5.0?):
var_dump(1 * 60 * 60); // 3600
var_dump(1 * 60 * 60 * 24); // 86400
var_dump(1 * 60 * 60 * 24 * 366); // 31622400
var_dump(1 * 60 * 60 * 24 * 366 * 10); // 316224000
var_dump(1 * 60 * 60 * 24 * 366 * 20); // 632448000
var_dump(1 * 60 * 60 * 24 * 366 * 30); // 948672000
var_dump(1 * 60 * 60 * 24 * 366 * 31); // 980294400
var_dump(PHP_INT_MAX); // 2147483647
Regarding PHP_INT_MAX I'm not sure what exactly changed in recent versions of PHP because the following clearly works in PHP 5.3.1, maybe someone could shed some light into this:
var_dump(base_convert(PHP_INT_MAX, 10, 36)); // zik0zj
var_dump(base_convert(PHP_INT_MAX + 1, 10, 36)); // zik0zk
var_dump(base_convert(PHP_INT_MAX + 2, 10, 36)); // zik0zl
var_dump(base_convert(PHP_INT_MAX * 2, 10, 36)); // 1z141z2
var_dump(base_convert(PHP_INT_MAX * 2 + 1, 10, 36)); // 1z141z3
var_dump(base_convert(PHP_INT_MAX * 2 + 2, 10, 36)); // 1z141z4
I got kinda lost with my rationalization here and I'm bored so I'll just finish really quick. We can use pretty much the whole base 36 charset and safely generate sequential codes with a minimum guaranteed uniqueness factor of 1 unique code per second for 3.16887646 years using this:
base_convert(mt_rand(22, 782) . substr(time(), 2), 10, 36);
I just realized that the above can sometimes return duplicated values due to the first argument of mt_rand(), in order to produce unique results we need to limit a our base 36 charset a little bit:
base_convert(mt_rand(122, 782) . substr(time(), 2), 10, 36);
Remember that the above values are still sequential, in order to make them look random we can use microtime() but we can only ensure a uniqueness factor of 10 codes per second for 3.8 months:
base_convert(mt_rand(122, 782) . substr(number_format(microtime(true), 1, '', ''), 3), 10, 36);
This proved to be more difficult than I originally antecipated since there are lot of constrains:
If we can ignore any of the above it would be a lot easier and I'm sure this can be further optimized but like I said: this is boring me. Maybe someone would like to pick this up where I left. =) I'm hungry! =S