Search code examples
phpincrementsequential

How to create URL-able unique id that is as short as possible?


For my domain, I'm trying to develop an ID generator that creates a unique ID, which can then be used for a shortlink (just as a URL shortener does).

I thought about using md5() or uniqueid() to create that ID. But to keep URLs as short as possible, this wouldn't be smart, as it passes out a huge amount of IDs, so the string/ID would get longer without any necessity.

Now PHP's string incrementation offers an interesting way. I could do:

$d = 'A08';
for ($n=0; $n<60; $n++) {
    echo ++$d . PHP_EOL;
}
// Outputs: A09 .. A68

But this only gives me all strings from A09 to A68. So I set $d = '000' in order to start from there. But now it only returns integers from 1 to60, instead of strings.

How can I force PHP to increment from 000 via AAA to ZZZ?

So to speak, I want an id than runs from 0 via 9 via A to Z and a to z.

0, 1, 2, .., 9, A, .., Z, a, .., z

Then the second character shall be added to the id:

00, 01, 02, .., 09, 0A, .., 0Z, 0a, .., 0z

Solution

  • I've done something similar to this on http://filepublicator.com. My solution was that each post gets its own unique incremental id (just like you normally would give them: 1, 2, 3, etc..), and also a copy of this id converted to base 62.

    Then I use this base 62 copy as the url. Heres another thread on SO about this: converting a number base 10 to base 62 (a-zA-Z0-9)

    I went with this method of first generating a real id and then converting it to base 62 because then I know I have a unique id without testing it against my already existing posts and it can easily be translated to my real id on the fly.

    Theoretically someone could easily figure out my method of creating urls and try to loop through all my pages, but if you are looking for making posts hidden and hard to find short urls without password protection isen't the way to go.

    This is the method written by Eineki in that thread:

    function toBase($num, $b=62) {
      $base='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
      $r = $num  % $b ;
      $res = $base[$r];
      $q = floor($num/$b);
      while ($q) {
        $r = $q % $b;
        $q =floor($q/$b);
        $res = $base[$r].$res;
      }
      return $res;
    }
    
    function to10( $num, $b=62) {
      $base='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
      $limit = strlen($num);
      $res=strpos($base,$num[0]);
      for($i=1;$i<$limit;$i++) {
        $res = $b * $res + strpos($base,$num[$i]);
      }
      return $res;
    }
    

    The test

    for ($i = 0; $i<1000000; $i++) {
      $x =  toBase($i);
      $y =  to10($x);
      if ($i-$y)
        echo "\n$i -> $x -> $y";
    }