PHP: Best way to Generate an unique numeric ID
Asked Answered
E

8

5

I need to generate unique numeric id.

  • I could use uniqid, but it generates alphanumeric value.

  • And again I could use time, but there is no guarantee that it will be unique always.

  • And again I could use the auto increment property of a field in a database to get an unique id, but here we must need a database.

So what can be the best way to generate an unique numeric id?

Esoteric answered 18/11, 2014 at 13:29 Comment(9)
That said if you need to put inside a db... leave at the db the unique idComply
@MarcinOrlowski I need not random but I need unique.Esoteric
you can use microtime(true) and append the decimal part to integer part which will be giving you a unique most of the time, practicallyBroach
So why you do not just use just sequential numbers? Will be pretty much unique for your scope? even DB's auto_increment would doDrawknife
@MarcinOrlowski How will you manage it if you do not have a database?Esoteric
maybe you can use a hash. md5 and sha generates numeric values in hex. maybe you can convert it to decimal. but you have to save it as a string, since the numbers are too big.Forcemeat
@SazzadHossainKhan you can save last used ID to the file. If you do locking properly that shall be sufficientDrawknife
How about understanding what uniqid actually does? You can always convert the hexadecimal value to decimal.Piderit
I'm having this dilemma right now. I cannot use the auto increment ids since there are other records that should not be taken into account. That leaves me to a sort of secondaryId. What's the best approach for this problem?Tintinnabulum
K
4

Nothing can guarantee 100% uniqueness.

You need to know uniqueness comparing with what do you need.

And use any algorythm plus check each value in list of all used values.

In the world of programming what you need is called pseudo random number. So it's name actually explains what I mean.

Kristopherkristos answered 18/11, 2014 at 13:34 Comment(6)
I would just change in your sentence instead "recently used" to "all used", because recently used wont guarantee uniquenessBarbel
Actually, auto incrementing integer can guarante 100% uniqueness. You might want to highlight that.Calefactory
@Calefactory He's not planning to use DBKristopherkristos
@BogdanBurim - point being? I haven't said a word about databases.Calefactory
@Calefactory Oh you did not mean a DB.. Why not try to write your own answer then?Kristopherkristos
I have.. I'm just correcting you because you claim that uniqueness can't be achieved or guaranteed while it can.Calefactory
C
3

Database systems use exclusive locking when creating numbers such as MySQL's auto_increment which takes care of concurrency and many other intricate details.

You have to approach the problem you have the same way - acquire a lock from the PHP process that's serving the request, look up the current value within some sort of persistent storage, increment it by 1, return it and release the lock.

The easiest way to do this is to use a good old file and exclusive locking.

I'll illustrate with a class (which should be debugged since it's not complete):

class MyAutoIncrement
{
    protected $fh = null;
    protected $file_path = '';
    protected $auto_increment_offset = 1;

    public function __construct($file_path, $offset = 1)
    {
        $this->file_path = $file_path;
        $this->auto_increment_offset = $offset;
    }

    public function autoincrement()
    {
        if($this->acquire())
        {
            $current = (int)fread($this->fh);

            $next += $this->auto_increment_offset;

            fwrite($this->fh, $next);

            $this->release();

            return $next;
        }

        return null;
    }

    public function acquire()
    {       
        $handler = $this->getFileHandler();

        return flock($handler, LOCK_EX);
    }

    public function release($close = false)
    {
        $handler = $this->getFileHandler();

        return flock($handler, LOCK_UN);

        if($close) 
        {
            fclose($handler);
            $this->fh = null;
        }
    }   

    protected function acquireLock($handler)
    {
        return flock($handler, LOCK_EX);
    }

    protected function getFileHandler()
    {
        if(is_null($this->fh))
        {
            $this->fh = fopen($this->file_path, 'c+');

            if($this->fh === false)
            {
                throw new \Exception(sprintf("Unable to open the specified file: %s", $this->file_path));
            }
        }

        return $this->fh;
    }
}

Usage:

$ai = new MyAutoIncrement('/path/to/counter/file.txt');

try
{
    $id = $ai->autoincrement();

    if(!is_null($id))
    {
        // Voila, you got your number, do stuff
    }
    else
    {
        // We went wrong somewhere
    }
}
catch(\Exception $e)
{
// Something went wrong
}
Calefactory answered 18/11, 2014 at 14:5 Comment(0)
D
2

As mentioned before. Nothing can guarantee 100% uniqueness.

Although this will be fairly unique :)

$iUniqueNumber = crc32(uniqid());

See uniqid and crc32 polynomial of a string.

Dunham answered 18/11, 2014 at 13:47 Comment(0)
N
2

You can use a combination of time() and getmypid() to get what you need - a numeric unique ID. You can have multiple php processes launched at a given time, but they will never have the same process ID at that given time (unless the server process counter overlaps in less than a second, which is virtually impossible when kernel.pid_max is set correctly).

<?
function getUniqueID() {
  return time() . '.' . getmypid();
}
?>

That function will generate a unique ID per script execution per server. It will fail if you call it multiple times in the same script and expect it to return unique value every time. In that case you can define some static variable inside the function body to keep track of that.

Nerissa answered 18/11, 2014 at 14:26 Comment(0)
T
0

You talked about time, what about microtime?

Even if you create two numbers in a row you'll get a diferent value. You'll need of course to play a little around to make it an unique integer, but it should be able to help.

Thrush answered 18/11, 2014 at 14:29 Comment(0)
L
0

I suggest to concatenate PHP process ID with microtime(true) to increase possibility of having unique value.

Lavinialavinie answered 18/11, 2014 at 15:15 Comment(0)
A
0
function getserial()
{
 $fn='/where_you_want/serial.dat';
 $fp = fopen($fn, "r+"); 
 if (flock($fp, LOCK_EX)) { $serial=fgets($fp);$serial++; } 
 else 
 { print('lock error, ABORT'); exit; }
 $h=fopen($fn.'.tmp','w'); fwrite($h,$serial);fclose($h);
 if (filesize($fn.'.tmp')>0) 
 { 
  system('rm -f '.$fn.'.tmp'); 
  fseek ($fp, 0); 
  fwrite($fp,$serial); 
 }
 flock($fp, LOCK_UN); fclose($fp); @chmod($fn,0777);
 return $serial;
}

this example will get you a unique serial number, after this, you can be sure it's existing with only one instance. please note, to avoid data corruption, you must create first your file and put a number first. (for example, write 1 without enter or anything else)

this is a really simple function, but it's working for me over 10 years now ...

Alesandrini answered 18/11, 2014 at 15:24 Comment(1)
please note, you MUST lock the file exclusively, otherwise your parallel running scripts will mess up everything! Also i've put the .tmp write test for a really good reason, to avoid data loss in case of no disk space ...Alesandrini
G
-1

You can make your own increment value to guarantee 100% uniqueness without use heavy algo:

Session unique id :

session_start();

$_SESSION['increment'] = 5;//i fix 5 but you need to get it in bdd,xml,...

function get_new_id(){
    $_SESSION['increment']++;
    //store new value of increment
    return $_SESSION['increment'];
}

$my_unique_id = get_new_id();
echo $my_unique_id;

Global unique id (dont use this !):

function get_new_id(){
    $increment = file_get_contents('increment.txt');
    $increment++;
    file_put_contents('increment.txt', $increment);
    return $increment;
}

$my_unique_id = get_new_id();
echo $my_unique_id;
Guelders answered 18/11, 2014 at 13:42 Comment(3)
Its applicable only for a session.Esoteric
So you dont use database, where i can store this id ? in file ?Guelders
But it's not unique, if you want 100% guarantee unique you need to use increment value.Something like that (example without bdd, i dont advise to use this): function get_new_id(){ $increment = file_get_contents('increment.txt'); $increment++; file_put_contents('increment.txt', $increment); return $increment; } $my_unique_id = get_new_id(); echo $my_unique_id;Guelders

© 2022 - 2024 — McMap. All rights reserved.