I'd like to use php uniqid() in a smarty template for my small reservation system (for a product) to generate an UNIQUE value that would make for a reservation number. Default uniqid() is a bit too long for my purpose, how can I make it like 5-6 characters?
Asked
Active
Viewed 1,603 times
0
-
How would you envision generating a unique id with only 6 characters? That is going to end bad soonish – PeeHaa Sep 25 '15 at 11:44
-
You could use a string generator similar to what's mentioned [here](http://stackoverflow.com/questions/4356289/php-random-string-generator) but would need to query it's uniqueness before being able to safely use it. Also, the shorter the string and smaller the character pool the sooner you will start running into problems. – user2959229 Sep 25 '15 at 11:50
-
@PeeHaa : Even if you only use digits and uppercase letters and remove all ambiguous characters - my prefered alphabet for such things - `3479ACEFHJKLMNPRTUVWXY` there are 22^10=26559922791424 different ids. Quite enough for a lot of things. – VolkerK Sep 25 '15 at 11:51
-
err, I meant 22^6. Ok now it's down to 113379904 - but it might be enough. Depends on what you want to achieve. – VolkerK Sep 25 '15 at 11:56
-
I think the number of reservations will top at around 100-200 – Caipiranha Sep 25 '15 at 11:59
-
And it wont be an end of the world if one number appears 2 times, but you know, 13 character long IDs look a bit weird. Maybe I should make a counter instead, but I don't know how, because the counter appears in the email that is processed in a smarty template, which is called by another smarty template processing a reservation form – Caipiranha Sep 25 '15 at 12:01
-
`And it wont be an end of the world if one number appears 2 times` - so, there is another bit of information (e.g. the username or email address) and "all" you need is: must match the record [uname,code] + hard enough to guess, i.e. there is no global uniqueness required at all? – VolkerK Sep 25 '15 at 12:04
-
"I think the number of reservations will top at around 100-200" well just use 00001 - 99999 in that case? I get the feeling you either didn't think this through or important information is lacking from this question. – PeeHaa Sep 25 '15 at 12:21
-
@PaulCrovella but I am guessing at some point a user also needs to handle the string. Just guessing here though – PeeHaa Sep 25 '15 at 12:23
-
Counter would be better, but Im not sure how to handle that between 2 smarty files. Somebody from smarty forum suggested using uniqid. – Caipiranha Sep 25 '15 at 13:06
-
And Im not sure how counter will handle multiple reservations per second – Caipiranha Sep 25 '15 at 13:06
1 Answers
1
tentative answer:
<?php
function toBase(/* positiv integer*/ $n, array $alphabet) {
$retval = '';
do {
$retval .= $alphabet[ $n%count($alphabet) ];
$n = intval( $n / count($alphabet) );
}
while( ($n=intval($n)) > 0);
return $retval;
}
function getCode() {
static $alphabet = null;
if( $alphabet==null) {
$alphabet = str_split('3479ACEFHJKLMNPRTUVWXY');
}
// get a random number
// and "encode" it using the alphabet
$code = toBase(mt_rand(), $alphabet);
// this might be both
// - too long
// - and too short*
// so first get the last 6 characters (if there are that much)
$code = substr($code, -6);
// and if there wasn't, pad them with 'zeros' (according to the alphabet that's a '3')
$code = str_pad($code, 6, $alphabet[0]);
return $code;
// *) the "too short" part could be avoided via mt_rand(22^6, ...)
// but I want to keep it in the range of a 32bit signed integer
}
getCode() gives you codes like
YFTRXA
MRMTMV
YC9HVN
VWCAUE
JEVXUF
WWMEYU
KLWAML
YCKE3V
37KJ3P
ME9EKU
I've tested getCode() (once) via
function testCodes() {
$codes = [];
for($i=0; $i<2000; $i++) {
$codes[] = getCode();
}
$withoutCollisions = array_unique($codes);
return count($codes)-count($withoutCollisions);
}
$collisions = [];
for($i=0; $i<5000; $i++) {
$c = testCodes();
if ( !isset($collisions[$c]) ) {
$collisions[$c] = 0;
}
$collisions[$c] += 1;
}
var_dump($collisions);
and the output was
array(3) {
[0]=>
int(4899)
[1]=>
int(100)
[2]=>
int(1)
}
So there are collisions (a set of 2000 codes having one or two doublets) but I'd say for what you're supposedly trying to achieve it's in the ball park. Collision rate is low enough so that you could even place a unique contraint in the database on that field and simply try again on a collison.
....BUT feel free to get over to https://security.stackexchange.com/ and have this algorithm shred to pieces ;-)
-
Thanks VolkerK. I will test it on Monday and let you know. Have a nice weekend! – Caipiranha Sep 25 '15 at 13:47
-
But don't play with the length and the alphabet ;-) Those are "hard-coded" into getCode() for a reason; this is _not_ a general purpose answer. There's not much wiggle room regarding the 32bit signed integer value range. And padding the `3`s if there are less 6 characters certainly creates a bias towards this digit. But anyway, I _believe_ this to be sufficient for your purpose and it's quite simple. – VolkerK Sep 25 '15 at 13:53
-
I haven't used your solution, because I dont exactly understand it, but what I've made is to use substr the uniqid to only 6 chars, but I still don't know how to pass the variable to which I've added this UNIQUE ID to smarty .tpl file :( I have to pass it because it is both used in email confirmation to US and to CUSTOMER, which are 2 different .tpl files – Caipiranha Oct 01 '15 at 09:47
-
I advise against using [uniqid](http://docs.php.net/manual/en/function.uniqid.php) for this. The manual states "This function does not create random nor unpredictable strings" - you want the string unpredictable ;-) For passing the value to smarty: Isn't that one of the most basic features of a templating system? I haven't used smarty but it looks like it's the first thing they show at (http://www.smarty.net/crash_course). `$smarty->assign('code', getCode()); $smarty->display('US.tpl'); $smarty->display('CUSTOMER.tpl');` – VolkerK Oct 01 '15 at 13:26
-
Nice solution. What I would add to this is to use a full alphabet and then shift the focus grabbing a random block of 6 chars instead of grabbing the last 6 chars. – TheLegendaryCopyCoder Nov 22 '18 at 10:49